ENH - Apps
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
@@ -66,6 +66,7 @@ button {
|
||||
}
|
||||
.phone-screen {
|
||||
position: relative;
|
||||
container-type: size;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
@@ -207,11 +208,19 @@ button {
|
||||
border-radius: 13px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
box-shadow:
|
||||
0 4px 13px #0004,
|
||||
inset 0 1px 1px #ffffff55;
|
||||
}
|
||||
.app-icon > img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
pointer-events: none;
|
||||
}
|
||||
.app-icon-label {
|
||||
width: 74px;
|
||||
overflow: hidden;
|
||||
@@ -246,6 +255,11 @@ button {
|
||||
.app-icon--settings {
|
||||
background: linear-gradient(145deg, #aeb3bd, #50535b);
|
||||
}
|
||||
.app-icon--image {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
.app-dock {
|
||||
position: absolute;
|
||||
z-index: 8;
|
||||
@@ -978,6 +992,704 @@ button {
|
||||
.wallpaper-picker button.active {
|
||||
border-color: #0a84ff;
|
||||
}
|
||||
|
||||
/* Reference-accurate app surfaces. These remain scoped to the phone resource. */
|
||||
.calculator-app {
|
||||
padding: 54px 16px 27px;
|
||||
}
|
||||
.calculator-display {
|
||||
height: 226px;
|
||||
padding: 0 5px 4px;
|
||||
font-size: 78px;
|
||||
letter-spacing: -5px;
|
||||
}
|
||||
.calculator-pad {
|
||||
gap: 11px 14px;
|
||||
}
|
||||
.calculator-pad button {
|
||||
font-size: 28px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.reference-camera {
|
||||
display: grid;
|
||||
grid-template-rows: 83px minmax(0, 1fr) 190px;
|
||||
padding: 0 0 24px;
|
||||
background: #020202;
|
||||
}
|
||||
.camera-topbar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: end;
|
||||
padding: 45px 15px 11px;
|
||||
}
|
||||
.camera-topbar > div {
|
||||
display: flex;
|
||||
gap: 17px;
|
||||
}
|
||||
.camera-topbar > svg {
|
||||
justify-self: end;
|
||||
}
|
||||
.camera-topbar button {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
.camera-topbar .camera-chevron {
|
||||
width: 34px;
|
||||
height: 26px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 20px;
|
||||
background: #262629;
|
||||
}
|
||||
.reference-viewfinder {
|
||||
position: relative;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at 66% 21%, #ffd88baa, transparent 15%),
|
||||
radial-gradient(circle at 45% 52%, #6d8ca5 0 6%, transparent 7%),
|
||||
linear-gradient(150deg, #173542, #5b3e5b 56%, #13191c);
|
||||
}
|
||||
.reference-viewfinder::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
115deg,
|
||||
transparent 45%,
|
||||
#ffffff0f 46%,
|
||||
transparent 48%
|
||||
);
|
||||
}
|
||||
.focus-corner {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-color: white;
|
||||
}
|
||||
.focus-corner--tl {
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
border-top: 1px solid;
|
||||
border-left: 1px solid;
|
||||
}
|
||||
.focus-corner--tr {
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
border-top: 1px solid;
|
||||
border-right: 1px solid;
|
||||
}
|
||||
.focus-corner--bl {
|
||||
bottom: 1px;
|
||||
left: 1px;
|
||||
border-bottom: 1px solid;
|
||||
border-left: 1px solid;
|
||||
}
|
||||
.focus-corner--br {
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
border-right: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.reference-viewfinder .camera-zoom {
|
||||
z-index: 3;
|
||||
bottom: 13px;
|
||||
}
|
||||
.reference-viewfinder .camera-zoom button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #0008;
|
||||
font-weight: 700;
|
||||
}
|
||||
.reference-viewfinder .camera-zoom small {
|
||||
margin-left: 1px;
|
||||
font-size: 8px;
|
||||
}
|
||||
.reference-camera-footer {
|
||||
display: grid;
|
||||
grid-template-rows: 43px 1fr;
|
||||
background: #020202;
|
||||
}
|
||||
.camera-mode-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 21px;
|
||||
padding: 0 148px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.camera-mode-strip button {
|
||||
flex: none;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
font-weight: 650;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.camera-mode-strip button.active {
|
||||
color: #ffd60a;
|
||||
}
|
||||
.camera-shutter-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
align-items: center;
|
||||
padding: 0 31px 12px;
|
||||
}
|
||||
.camera-thumbnail {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.reference-shutter {
|
||||
justify-self: center;
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
color: #111;
|
||||
box-shadow: inset 0 0 0 3px #020202;
|
||||
}
|
||||
.reference-flip {
|
||||
justify-self: end;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background: #1c1c1e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.reference-tabbar {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
right: 0;
|
||||
bottom: 21px;
|
||||
left: 0;
|
||||
height: 58px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-around;
|
||||
padding: 7px 5px 0;
|
||||
border-top: 1px solid #ffffff18;
|
||||
background: #101012dd;
|
||||
backdrop-filter: blur(22px);
|
||||
-webkit-backdrop-filter: blur(22px);
|
||||
}
|
||||
.reference-tabbar button {
|
||||
min-width: 49px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #77777c;
|
||||
font-size: 9px;
|
||||
}
|
||||
.reference-tabbar button.active {
|
||||
color: #0a84ff;
|
||||
}
|
||||
.clock-app .app-tabs button.active {
|
||||
color: #d99900;
|
||||
}
|
||||
.clock-app {
|
||||
padding-top: 0;
|
||||
background: #020202;
|
||||
}
|
||||
.reference-clock-header {
|
||||
padding: 47px 16px 0;
|
||||
}
|
||||
.reference-clock-header nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #d99900;
|
||||
}
|
||||
.reference-clock-header nav button {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font-size: 15px;
|
||||
}
|
||||
.reference-clock-header h1 {
|
||||
margin: 14px 0 0;
|
||||
padding-bottom: 11px;
|
||||
border-bottom: 1px solid #ffffff1c;
|
||||
font-size: 35px;
|
||||
line-height: 1;
|
||||
}
|
||||
.clock-app .clock-content {
|
||||
height: calc(100% - 155px);
|
||||
padding: 0 16px 72px;
|
||||
}
|
||||
.world-clock-list > div {
|
||||
min-height: 95px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #ffffff1c;
|
||||
}
|
||||
.world-clock-list > div > div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.world-clock-list small {
|
||||
color: #8e8e93;
|
||||
font-size: 11px;
|
||||
}
|
||||
.world-clock-list strong {
|
||||
font-size: min(25px, 7cqw);
|
||||
font-weight: 400;
|
||||
}
|
||||
.world-clock-list time {
|
||||
font-size: min(42px, 11cqw);
|
||||
font-weight: 200;
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
.reference-clock-tabs button.active {
|
||||
color: #d99900;
|
||||
}
|
||||
|
||||
.reference-photos {
|
||||
padding: 0 0 79px;
|
||||
background: #020202;
|
||||
}
|
||||
.photos-library,
|
||||
.photos-scroll {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.photos-floating-header {
|
||||
position: sticky;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
height: 95px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
padding: 47px 17px 14px;
|
||||
background: linear-gradient(#000 55%, transparent);
|
||||
}
|
||||
.photos-floating-header > div {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
.photos-floating-header strong {
|
||||
font-size: 17px;
|
||||
}
|
||||
.photos-floating-header span {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.photos-floating-header button {
|
||||
height: 27px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 18px;
|
||||
background: #ffffff1d;
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
font-weight: 650;
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
}
|
||||
.photos-floating-header button:last-child {
|
||||
width: 28px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.reference-photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 2px;
|
||||
}
|
||||
.reference-photo-grid article {
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
.photos-count {
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
}
|
||||
.photos-period {
|
||||
position: sticky;
|
||||
bottom: 5px;
|
||||
width: max-content;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
padding: 3px;
|
||||
border-radius: 22px;
|
||||
background: #303033aa;
|
||||
backdrop-filter: blur(18px);
|
||||
-webkit-backdrop-filter: blur(18px);
|
||||
}
|
||||
.photos-period button {
|
||||
padding: 5px 12px;
|
||||
border: 0;
|
||||
border-radius: 18px;
|
||||
background: transparent;
|
||||
color: #ddd;
|
||||
font-size: 10px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.photos-period button.active {
|
||||
background: #ffffff25;
|
||||
color: white;
|
||||
}
|
||||
.photos-scroll {
|
||||
padding: 82px 16px 20px;
|
||||
}
|
||||
.photos-scroll > h1 {
|
||||
margin: 0 0 28px;
|
||||
font-size: 34px;
|
||||
}
|
||||
.photos-section-title {
|
||||
margin-top: 22px;
|
||||
padding-top: 13px;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
border-top: 1px solid #ffffff1b;
|
||||
}
|
||||
.photos-section-title h2 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
}
|
||||
.photos-section-title button {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #0a84ff;
|
||||
}
|
||||
.memory-card {
|
||||
position: relative;
|
||||
height: 410px;
|
||||
margin-top: 12px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 17px;
|
||||
box-shadow: inset 0 -160px 100px #0008;
|
||||
}
|
||||
.memory-card > svg {
|
||||
margin-left: auto;
|
||||
}
|
||||
.memory-card > div {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.memory-card strong {
|
||||
font-size: 26px;
|
||||
}
|
||||
.memory-card span {
|
||||
font-size: 11px;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.featured-row {
|
||||
display: flex;
|
||||
gap: 9px;
|
||||
margin-top: 12px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.featured-row article {
|
||||
width: 88%;
|
||||
flex: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.featured-row article > div {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.featured-row article span {
|
||||
color: #777;
|
||||
font-size: 12px;
|
||||
}
|
||||
.reference-photos .photo-grid {
|
||||
height: auto;
|
||||
margin-top: 13px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.reference-store {
|
||||
padding: 0 0 79px;
|
||||
background: #0e0e0e;
|
||||
}
|
||||
.store-scroll {
|
||||
height: 100%;
|
||||
padding: 52px 19px 26px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.store-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.store-title > div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 11px;
|
||||
}
|
||||
.store-title h1 {
|
||||
margin: 0;
|
||||
font-size: 34px;
|
||||
}
|
||||
.store-title strong {
|
||||
color: #777;
|
||||
font-size: 18px;
|
||||
}
|
||||
.store-title > span {
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(145deg, #3a3c46, #1b1c22);
|
||||
}
|
||||
.editorial-card {
|
||||
position: relative;
|
||||
height: 390px;
|
||||
margin-bottom: 18px;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
border-radius: 22px;
|
||||
box-shadow: 0 10px 28px #0007;
|
||||
}
|
||||
.editorial-card small {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.editorial-card h2 {
|
||||
width: 80%;
|
||||
margin: 7px 0;
|
||||
font-size: 28px;
|
||||
line-height: 1.05;
|
||||
}
|
||||
.editorial-card p {
|
||||
width: 80%;
|
||||
margin: 0;
|
||||
color: #ffffffbf;
|
||||
font-size: 14px;
|
||||
}
|
||||
.editorial-card--sky {
|
||||
background:
|
||||
radial-gradient(
|
||||
circle at 68% 63%,
|
||||
#fff 0 4%,
|
||||
#4de1fb 5% 9%,
|
||||
transparent 10%
|
||||
),
|
||||
radial-gradient(circle at 65% 60%, #c653ff99, transparent 31%),
|
||||
linear-gradient(145deg, #10144d, #4630a6 58%, #1bc7e2);
|
||||
}
|
||||
.editorial-orbit {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
bottom: 30px;
|
||||
width: 230px;
|
||||
height: 230px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 2px solid #ffffff99;
|
||||
border-radius: 50%;
|
||||
font-size: 74px;
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
.editorial-card--music {
|
||||
background:
|
||||
radial-gradient(circle at 60% 70%, #ffdd45, transparent 22%),
|
||||
linear-gradient(155deg, #531849, #ca2c72 60%, #f48c46);
|
||||
}
|
||||
.editorial-wave {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 28px;
|
||||
left: 16px;
|
||||
height: 110px;
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
#ffffffaa 0 4px,
|
||||
transparent 4px 12px
|
||||
);
|
||||
clip-path: polygon(
|
||||
0 45%,
|
||||
10% 10%,
|
||||
20% 82%,
|
||||
30% 25%,
|
||||
40% 70%,
|
||||
50% 5%,
|
||||
60% 85%,
|
||||
70% 28%,
|
||||
80% 68%,
|
||||
90% 13%,
|
||||
100% 55%,
|
||||
100% 70%,
|
||||
0 70%
|
||||
);
|
||||
}
|
||||
.editorial-card--play {
|
||||
height: 330px;
|
||||
background:
|
||||
radial-gradient(circle at 80% 70%, #64ffbe88, transparent 30%),
|
||||
linear-gradient(145deg, #0d4137, #18236c);
|
||||
}
|
||||
.store-section-title {
|
||||
padding: 14px 0 12px;
|
||||
border-top: 1px solid #ffffff1c;
|
||||
}
|
||||
.store-section-title h2 {
|
||||
margin: 0;
|
||||
font-size: 19px;
|
||||
}
|
||||
.store-section-title p {
|
||||
margin: 3px 0 0;
|
||||
color: #777;
|
||||
font-size: 13px;
|
||||
}
|
||||
.reference-store .store-feature {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
.reference-store .store-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reference-settings {
|
||||
padding: 0;
|
||||
background: #0e0e0e;
|
||||
}
|
||||
.settings-scroll {
|
||||
height: 100%;
|
||||
padding: 69px 16px 38px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.settings-scroll > h1 {
|
||||
margin: 0 0 17px;
|
||||
font-size: 35px;
|
||||
line-height: 1;
|
||||
}
|
||||
.settings-search {
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 0 9px;
|
||||
border-radius: 9px;
|
||||
background: #1c1c1e;
|
||||
color: #777;
|
||||
}
|
||||
.settings-search input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
.settings-account {
|
||||
margin: 17px 0;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
border-radius: 12px;
|
||||
background: #1c1c1e;
|
||||
}
|
||||
.settings-account > span {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(145deg, #596172, #22252c);
|
||||
}
|
||||
.settings-account > div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.settings-account strong {
|
||||
font-size: 17px;
|
||||
}
|
||||
.settings-account small {
|
||||
color: #ccc;
|
||||
font-size: 11px;
|
||||
}
|
||||
.reference-settings-group {
|
||||
margin-bottom: 17px;
|
||||
padding: 0 11px;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
background: #1c1c1e;
|
||||
}
|
||||
.reference-settings-group article {
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.reference-settings-group article:not(:last-child) > div {
|
||||
border-bottom: 1px solid #ffffff18;
|
||||
}
|
||||
.reference-settings-group article > div {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.reference-settings-group article > div strong {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.reference-settings-group article > div small {
|
||||
max-width: 100px;
|
||||
overflow: hidden;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.reference-settings-group .settings-symbol {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
flex: none;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.settings-chevron {
|
||||
color: #555;
|
||||
}
|
||||
.reference-settings .wallpaper-picker button {
|
||||
height: 105px;
|
||||
}
|
||||
* {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.camera-mode-strip {
|
||||
padding: 0;
|
||||
}
|
||||
.camera-mode-strip button:first-child {
|
||||
margin-left: -205px;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
@@ -18,6 +19,7 @@ const props = withDefaults(
|
||||
|
||||
const phone = usePhoneStore()
|
||||
const router = useRouter()
|
||||
const iconFailed = ref(false)
|
||||
|
||||
function launch(event: MouseEvent): void {
|
||||
const button = event.currentTarget as HTMLElement
|
||||
@@ -49,8 +51,24 @@ function launch(event: MouseEvent): void {
|
||||
:aria-label="phone.t(app.labelKey)"
|
||||
@click="launch"
|
||||
>
|
||||
<span class="app-icon" :class="app.iconClass" aria-hidden="true">
|
||||
<component :is="app.icon" :size="compact ? 18 : 28" :stroke-width="2" />
|
||||
<span
|
||||
class="app-icon"
|
||||
:class="[app.iconClass, { 'app-icon--image': !iconFailed }]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<img
|
||||
v-if="!iconFailed"
|
||||
:src="app.iconImage"
|
||||
alt=""
|
||||
draggable="false"
|
||||
@error="iconFailed = true"
|
||||
/>
|
||||
<component
|
||||
:is="app.icon"
|
||||
v-else
|
||||
:size="compact ? 18 : 28"
|
||||
:stroke-width="2"
|
||||
/>
|
||||
</span>
|
||||
<span v-if="showLabel" class="app-icon-label">{{
|
||||
phone.t(app.labelKey)
|
||||
|
||||
@@ -8,6 +8,9 @@ describe('app registry', () => {
|
||||
expect(PHONE_APPS.every((app) => app.route === `/apps/${app.id}`)).toBe(
|
||||
true,
|
||||
)
|
||||
expect(PHONE_APPS.every((app) => app.iconImage.endsWith('.webp'))).toBe(
|
||||
true,
|
||||
)
|
||||
expect(
|
||||
PHONE_APPS.filter((app) => app.dockOrder !== null)
|
||||
.sort((a, b) => (a.dockOrder ?? 0) - (b.dockOrder ?? 0))
|
||||
|
||||
@@ -8,6 +8,12 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
import { defineAsyncComponent, markRaw } from 'vue'
|
||||
|
||||
import appStoreIcon from '@/assets/img/app-icons/apps.webp'
|
||||
import calculatorIcon from '@/assets/img/app-icons/calculator.webp'
|
||||
import cameraIcon from '@/assets/img/app-icons/camera.webp'
|
||||
import clockIcon from '@/assets/img/app-icons/clock.webp'
|
||||
import photosIcon from '@/assets/img/app-icons/gallery.webp'
|
||||
import settingsIcon from '@/assets/img/app-icons/settings.webp'
|
||||
import type { PhoneAppDefinition, PhoneAppId } from '@/types/apps'
|
||||
|
||||
export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
@@ -19,6 +25,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 0,
|
||||
icon: markRaw(Calculator),
|
||||
iconClass: 'app-icon--calculator',
|
||||
iconImage: calculatorIcon,
|
||||
id: 'calculator',
|
||||
labelKey: 'Apps.calculator.name',
|
||||
route: '/apps/calculator',
|
||||
@@ -31,6 +38,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 1,
|
||||
icon: markRaw(Camera),
|
||||
iconClass: 'app-icon--camera',
|
||||
iconImage: cameraIcon,
|
||||
id: 'camera',
|
||||
labelKey: 'Apps.camera.name',
|
||||
route: '/apps/camera',
|
||||
@@ -43,6 +51,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 2,
|
||||
icon: markRaw(Clock3),
|
||||
iconClass: 'app-icon--clock',
|
||||
iconImage: clockIcon,
|
||||
id: 'clock',
|
||||
labelKey: 'Apps.clock.name',
|
||||
route: '/apps/clock',
|
||||
@@ -55,6 +64,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 3,
|
||||
icon: markRaw(Images),
|
||||
iconClass: 'app-icon--photos',
|
||||
iconImage: photosIcon,
|
||||
id: 'photos',
|
||||
labelKey: 'Apps.photos.name',
|
||||
route: '/apps/photos',
|
||||
@@ -67,6 +77,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 4,
|
||||
icon: markRaw(ShoppingBag),
|
||||
iconClass: 'app-icon--store',
|
||||
iconImage: appStoreIcon,
|
||||
id: 'app-store',
|
||||
labelKey: 'Apps.appStore.name',
|
||||
route: '/apps/app-store',
|
||||
@@ -79,6 +90,7 @@ export const PHONE_APPS: PhoneAppDefinition[] = [
|
||||
gridOrder: 5,
|
||||
icon: markRaw(Settings),
|
||||
iconClass: 'app-icon--settings',
|
||||
iconImage: settingsIcon,
|
||||
id: 'settings',
|
||||
labelKey: 'Apps.settings.name',
|
||||
route: '/apps/settings',
|
||||
|
||||
@@ -26,6 +26,11 @@ const defaultLocales: LocaleTree = {
|
||||
get: 'GET',
|
||||
open: 'OPEN',
|
||||
searchPlaceholder: 'Games, Apps, Stories and More',
|
||||
communityTitle: 'Black Voices and Creators',
|
||||
communityBody: 'Apps and games from the community',
|
||||
playing: "What We're Playing",
|
||||
recommended: 'Recommended for You',
|
||||
selected: 'Great apps selected by our editors',
|
||||
tabs: {
|
||||
today: 'Today',
|
||||
apps: 'Apps',
|
||||
@@ -39,18 +44,42 @@ const defaultLocales: LocaleTree = {
|
||||
trail: 'Explore nearby',
|
||||
prism: 'A colorful puzzle',
|
||||
},
|
||||
card: {
|
||||
oneEyebrow: 'App of the Day',
|
||||
oneTitle: 'A universe in your pocket',
|
||||
oneBody: 'Explore something extraordinary today.',
|
||||
twoEyebrow: 'Now Trending',
|
||||
twoTitle: 'Turn up your afternoon',
|
||||
twoBody: 'Fresh sounds and stories picked for you.',
|
||||
threeEyebrow: "Editors' Choice",
|
||||
threeTitle: 'Play without limits',
|
||||
threeBody: 'A new world is waiting.',
|
||||
},
|
||||
},
|
||||
calculator: { name: 'Calculator' },
|
||||
camera: {
|
||||
name: 'Camera',
|
||||
shutter: 'Take photo',
|
||||
flip: 'Flip camera',
|
||||
modes: { video: 'Video', photo: 'Photo', portrait: 'Portrait' },
|
||||
flash: 'Toggle flash',
|
||||
controls: 'Camera controls',
|
||||
modes: {
|
||||
timelapse: 'Timelapse',
|
||||
slowMo: 'Slow-Mo',
|
||||
cinematic: 'Cinematic',
|
||||
video: 'Video',
|
||||
photo: 'Photo',
|
||||
portrait: 'Portrait',
|
||||
pano: 'Pano',
|
||||
},
|
||||
},
|
||||
clock: {
|
||||
name: 'Clock',
|
||||
lap: 'Lap',
|
||||
minutes: 'Minutes',
|
||||
add: 'Add clock',
|
||||
today: 'Today',
|
||||
offset: '+0HRS',
|
||||
tabs: {
|
||||
world: 'World Clock',
|
||||
alarm: 'Alarm',
|
||||
@@ -73,6 +102,19 @@ const defaultLocales: LocaleTree = {
|
||||
items: 'items',
|
||||
memories: 'Memories',
|
||||
featured: 'City colors',
|
||||
dateRange: '19 Apr–7 May 2024',
|
||||
place: 'Los Santos & more',
|
||||
select: 'Select',
|
||||
count: '3,042 Photos, 125 Videos',
|
||||
years: 'Years',
|
||||
months: 'Months',
|
||||
days: 'Days',
|
||||
allPhotos: 'All Photos',
|
||||
seeAll: 'See All',
|
||||
onThisDay: 'On This Day',
|
||||
trip: 'MAR 2024 TRIP',
|
||||
featuredPhotos: 'Featured Photos',
|
||||
featuredDate: '30 Mar 2024',
|
||||
tabs: {
|
||||
library: 'Library',
|
||||
forYou: 'For You',
|
||||
@@ -94,6 +136,24 @@ const defaultLocales: LocaleTree = {
|
||||
bluetooth: 'Bluetooth',
|
||||
airplaneMode: 'Airplane Mode',
|
||||
wallpaper: 'Wallpaper',
|
||||
on: 'On',
|
||||
accountName: 'Sky Citizen',
|
||||
accountDetail: 'Apple ID, iCloud, Media & Purchases',
|
||||
mobileServices: 'Mobile Services',
|
||||
personalHotspot: 'Personal Hotspot',
|
||||
vpn: 'VPN',
|
||||
notifications: 'Notifications',
|
||||
sounds: 'Sounds & Haptics',
|
||||
focus: 'Focus',
|
||||
screenTime: 'Screen Time',
|
||||
general: 'General',
|
||||
display: 'Display & Brightness',
|
||||
homeScreen: 'Home Screen & App Library',
|
||||
accessibility: 'Accessibility',
|
||||
battery: 'Battery',
|
||||
privacy: 'Privacy & Security',
|
||||
passwords: 'Passwords',
|
||||
wallpaperPicker: 'Built-in Wallpapers',
|
||||
toggle: {
|
||||
wifi: 'Toggle Wi-Fi',
|
||||
bluetooth: 'Toggle Bluetooth',
|
||||
@@ -109,6 +169,7 @@ const defaultLocales: LocaleTree = {
|
||||
Common: {
|
||||
cancel: 'Cancel',
|
||||
close: 'Close',
|
||||
edit: 'Edit',
|
||||
home: 'Home',
|
||||
pause: 'Pause',
|
||||
phone: 'Phone',
|
||||
|
||||
@@ -23,6 +23,7 @@ export type PhoneAppDefinition = {
|
||||
gridOrder: number
|
||||
icon: Component
|
||||
iconClass: string
|
||||
iconImage: string
|
||||
id: PhoneAppId
|
||||
labelKey: string
|
||||
route: `/apps/${PhoneAppId}`
|
||||
|
||||
@@ -1,37 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Gamepad2,
|
||||
Grid2X2,
|
||||
Rocket,
|
||||
Search,
|
||||
Sparkles,
|
||||
UserRound,
|
||||
} from 'lucide-vue-next'
|
||||
import { computed, ref } from 'vue'
|
||||
import { Search } from 'lucide-vue-next'
|
||||
|
||||
import { useMediaStore } from '@/stores/media'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
|
||||
const phone = usePhoneStore()
|
||||
const media = useMediaStore()
|
||||
const tab = ref('today')
|
||||
const tab = ref<'today' | 'apps' | 'games' | 'arcade' | 'search'>('today')
|
||||
const query = ref('')
|
||||
const tabs = ['today', 'apps', 'games', 'arcade', 'search']
|
||||
const tabs = [
|
||||
{ id: 'today', icon: Sparkles },
|
||||
{ id: 'apps', icon: Grid2X2 },
|
||||
{ id: 'games', icon: Gamepad2 },
|
||||
{ id: 'arcade', icon: Rocket },
|
||||
{ id: 'search', icon: Search },
|
||||
] as const
|
||||
const catalog = [
|
||||
{
|
||||
id: 'orbit',
|
||||
title: 'Orbit',
|
||||
subtitle: 'Apps.appStore.catalog.orbit',
|
||||
gradient: 'linear-gradient(135deg,#6f5cff,#29d3c2)',
|
||||
gradient: 'linear-gradient(145deg,#453bd1,#75e8ff)',
|
||||
},
|
||||
{
|
||||
id: 'studio',
|
||||
title: 'Studio',
|
||||
subtitle: 'Apps.appStore.catalog.studio',
|
||||
gradient: 'linear-gradient(135deg,#ff8b4a,#ff2d75)',
|
||||
gradient: 'linear-gradient(145deg,#ff8b4a,#ff2d75)',
|
||||
},
|
||||
{
|
||||
id: 'trail',
|
||||
title: 'Trail',
|
||||
subtitle: 'Apps.appStore.catalog.trail',
|
||||
gradient: 'linear-gradient(135deg,#4bd37b,#147ec1)',
|
||||
gradient: 'linear-gradient(145deg,#4bd37b,#147ec1)',
|
||||
},
|
||||
{
|
||||
id: 'puzzle',
|
||||
id: 'prism',
|
||||
title: 'Prism',
|
||||
subtitle: 'Apps.appStore.catalog.prism',
|
||||
gradient: 'linear-gradient(135deg,#ffd84d,#7b36dc)',
|
||||
gradient: 'linear-gradient(145deg,#ffd84d,#7b36dc)',
|
||||
},
|
||||
]
|
||||
const shown = computed(() =>
|
||||
@@ -39,53 +54,102 @@ const shown = computed(() =>
|
||||
item.title.toLowerCase().includes(query.value.toLowerCase()),
|
||||
),
|
||||
)
|
||||
const date = new Intl.DateTimeFormat(phone.lang, {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
}).format(new Date())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="native-app store-app">
|
||||
<header class="app-header">
|
||||
<small>{{ phone.t('Apps.appStore.eyebrow') }}</small>
|
||||
<h1>{{ phone.t(`Apps.appStore.tabs.${tab}`) }}</h1>
|
||||
</header>
|
||||
<div v-if="tab === 'search'" class="app-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.appStore.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<section class="store-feature">
|
||||
<p>{{ phone.t('Apps.appStore.featured') }}</p>
|
||||
<h2>{{ phone.t('Apps.appStore.heroTitle') }}</h2>
|
||||
<span>{{ phone.t('Apps.appStore.heroBody') }}</span>
|
||||
</section>
|
||||
<section class="store-list">
|
||||
<article v-for="item in shown" :key="item.id">
|
||||
<div class="store-icon" :style="{ background: item.gradient }">
|
||||
{{ item.title[0] }}
|
||||
</div>
|
||||
<main class="native-app reference-store">
|
||||
<section class="store-scroll">
|
||||
<header class="store-title">
|
||||
<div>
|
||||
<strong>{{ item.title }}</strong
|
||||
><small>{{ phone.t(item.subtitle) }}</small>
|
||||
<h1>{{ phone.t(`Apps.appStore.tabs.${tab}`) }}</h1>
|
||||
<strong v-if="tab === 'today'">{{ date }}</strong>
|
||||
</div>
|
||||
<button type="button" @click="media.claimApp(item.id)">
|
||||
{{
|
||||
phone.t(
|
||||
media.claimedApps.includes(item.id)
|
||||
? 'Apps.appStore.open'
|
||||
: 'Apps.appStore.get',
|
||||
)
|
||||
}}
|
||||
</button>
|
||||
</article>
|
||||
<span><UserRound :size="22" /></span>
|
||||
</header>
|
||||
<div v-if="tab === 'search'" class="app-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.appStore.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="tab === 'today'">
|
||||
<article class="editorial-card editorial-card--sky">
|
||||
<small>{{ phone.t('Apps.appStore.card.oneEyebrow') }}</small>
|
||||
<h2>{{ phone.t('Apps.appStore.card.oneTitle') }}</h2>
|
||||
<p>{{ phone.t('Apps.appStore.card.oneBody') }}</p>
|
||||
<div class="editorial-orbit">✦</div>
|
||||
</article>
|
||||
<article class="editorial-card editorial-card--music">
|
||||
<small>{{ phone.t('Apps.appStore.card.twoEyebrow') }}</small>
|
||||
<h2>{{ phone.t('Apps.appStore.card.twoTitle') }}</h2>
|
||||
<p>{{ phone.t('Apps.appStore.card.twoBody') }}</p>
|
||||
<div class="editorial-wave" />
|
||||
</article>
|
||||
<div class="store-section-title">
|
||||
<h2>{{ phone.t('Apps.appStore.communityTitle') }}</h2>
|
||||
<p>{{ phone.t('Apps.appStore.communityBody') }}</p>
|
||||
</div>
|
||||
<article class="editorial-card editorial-card--play">
|
||||
<small>{{ phone.t('Apps.appStore.card.threeEyebrow') }}</small>
|
||||
<h2>{{ phone.t('Apps.appStore.card.threeTitle') }}</h2>
|
||||
<p>{{ phone.t('Apps.appStore.card.threeBody') }}</p>
|
||||
</article>
|
||||
</template>
|
||||
<template v-else>
|
||||
<article v-if="tab !== 'search'" class="store-feature">
|
||||
<p>{{ phone.t('Apps.appStore.featured') }}</p>
|
||||
<h2>{{ phone.t('Apps.appStore.heroTitle') }}</h2>
|
||||
<span>{{ phone.t('Apps.appStore.heroBody') }}</span>
|
||||
</article>
|
||||
<div class="store-section-title">
|
||||
<h2>
|
||||
{{
|
||||
phone.t(
|
||||
tab === 'games'
|
||||
? 'Apps.appStore.playing'
|
||||
: 'Apps.appStore.recommended',
|
||||
)
|
||||
}}
|
||||
</h2>
|
||||
<p>{{ phone.t('Apps.appStore.selected') }}</p>
|
||||
</div>
|
||||
<section class="store-list">
|
||||
<article v-for="item in shown" :key="item.id">
|
||||
<div class="store-icon" :style="{ background: item.gradient }">
|
||||
{{ item.title[0] }}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ item.title }}</strong
|
||||
><small>{{ phone.t(item.subtitle) }}</small>
|
||||
</div>
|
||||
<button type="button" @click="media.claimApp(item.id)">
|
||||
{{
|
||||
phone.t(
|
||||
media.claimedApps.includes(item.id)
|
||||
? 'Apps.appStore.open'
|
||||
: 'Apps.appStore.get',
|
||||
)
|
||||
}}
|
||||
</button>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
</section>
|
||||
<nav class="app-tabs">
|
||||
<nav class="reference-tabbar">
|
||||
<button
|
||||
v-for="item in tabs"
|
||||
:key="item"
|
||||
:class="{ active: tab === item }"
|
||||
:key="item.id"
|
||||
:class="{ active: tab === item.id }"
|
||||
type="button"
|
||||
@click="tab = item"
|
||||
@click="tab = item.id"
|
||||
>
|
||||
{{ phone.t(`Apps.appStore.tabs.${item}`) }}
|
||||
<component :is="item.icon" :size="21" /><span>{{
|
||||
phone.t(`Apps.appStore.tabs.${item.id}`)
|
||||
}}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</main>
|
||||
|
||||
@@ -1,68 +1,126 @@
|
||||
<script setup lang="ts">
|
||||
import { Camera, RotateCcw } from 'lucide-vue-next'
|
||||
import {
|
||||
Aperture,
|
||||
Camera,
|
||||
ChevronUp,
|
||||
RotateCcw,
|
||||
Zap,
|
||||
ZapOff,
|
||||
} from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { useMediaStore } from '@/stores/media'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
|
||||
const media = useMediaStore()
|
||||
const phone = usePhoneStore()
|
||||
const flash = ref(false)
|
||||
const modes = ['video', 'photo', 'portrait'] as const
|
||||
const flashEnabled = ref(false)
|
||||
const modes = [
|
||||
'timelapse',
|
||||
'slowMo',
|
||||
'cinematic',
|
||||
'video',
|
||||
'photo',
|
||||
'portrait',
|
||||
'pano',
|
||||
] as const
|
||||
|
||||
function capture(): void {
|
||||
flash.value = true
|
||||
media.capture()
|
||||
setTimeout(() => {
|
||||
flash.value = false
|
||||
}, 120)
|
||||
window.setTimeout(() => (flash.value = false), 120)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="native-app camera-app">
|
||||
<div
|
||||
class="camera-viewfinder"
|
||||
<main class="native-app reference-camera">
|
||||
<header class="camera-topbar">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.flash')"
|
||||
@click="flashEnabled = !flashEnabled"
|
||||
>
|
||||
<Zap v-if="flashEnabled" :size="22" />
|
||||
<ZapOff v-else :size="22" />
|
||||
</button>
|
||||
<Aperture :size="22" />
|
||||
</div>
|
||||
<button
|
||||
class="camera-chevron"
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.controls')"
|
||||
>
|
||||
<ChevronUp :size="20" />
|
||||
</button>
|
||||
<Aperture :size="22" />
|
||||
</header>
|
||||
|
||||
<section
|
||||
class="reference-viewfinder"
|
||||
:class="`camera-facing--${media.cameraFacing}`"
|
||||
>
|
||||
<div class="camera-flash" :class="{ active: flash }" />
|
||||
<div class="camera-grid" />
|
||||
<i
|
||||
v-for="corner in ['tl', 'tr', 'bl', 'br']"
|
||||
:key="corner"
|
||||
:class="`focus-corner focus-corner--${corner}`"
|
||||
/>
|
||||
<div class="camera-zoom">
|
||||
<button
|
||||
v-for="zoom in [0.5, 1, 2]"
|
||||
v-for="zoom in [0.5, 1]"
|
||||
:key="zoom"
|
||||
:class="{ active: media.cameraZoom === zoom }"
|
||||
type="button"
|
||||
@click="media.cameraZoom = zoom"
|
||||
>
|
||||
{{ zoom }}×
|
||||
{{ zoom }}<small v-if="zoom === 1">x</small>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="camera-modes">
|
||||
<button
|
||||
v-for="mode in modes"
|
||||
:key="mode"
|
||||
:class="{ active: media.cameraMode === mode }"
|
||||
type="button"
|
||||
@click="media.cameraMode = mode"
|
||||
>
|
||||
{{ phone.t(`Apps.camera.modes.${mode}`) }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="camera-controls">
|
||||
<span /><button
|
||||
class="shutter"
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.shutter')"
|
||||
@click="capture"
|
||||
>
|
||||
<Camera :size="26" /></button
|
||||
><button
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.flip')"
|
||||
@click="
|
||||
media.cameraFacing = media.cameraFacing === 'rear' ? 'front' : 'rear'
|
||||
"
|
||||
>
|
||||
<RotateCcw :size="23" />
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="reference-camera-footer">
|
||||
<nav class="camera-mode-strip">
|
||||
<button
|
||||
v-for="mode in modes"
|
||||
:key="mode"
|
||||
:class="{ active: media.cameraMode === mode }"
|
||||
type="button"
|
||||
@click="
|
||||
mode === 'photo' || mode === 'portrait' || mode === 'video'
|
||||
? (media.cameraMode = mode)
|
||||
: undefined
|
||||
"
|
||||
>
|
||||
{{ phone.t(`Apps.camera.modes.${mode}`) }}
|
||||
</button>
|
||||
</nav>
|
||||
<div class="camera-shutter-row">
|
||||
<div
|
||||
class="camera-thumbnail"
|
||||
:style="{ background: media.photos[0]?.gradient }"
|
||||
/>
|
||||
<button
|
||||
class="reference-shutter"
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.shutter')"
|
||||
@click="capture"
|
||||
>
|
||||
<Camera :size="23" />
|
||||
</button>
|
||||
<button
|
||||
class="reference-flip"
|
||||
type="button"
|
||||
:aria-label="phone.t('Apps.camera.flip')"
|
||||
@click="
|
||||
media.cameraFacing =
|
||||
media.cameraFacing === 'rear' ? 'front' : 'rear'
|
||||
"
|
||||
>
|
||||
<RotateCcw :size="22" />
|
||||
</button>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { AlarmClock, Clock3, Plus, Timer, TimerReset } from 'lucide-vue-next'
|
||||
|
||||
import { useClockStore } from '@/stores/clock'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
@@ -35,7 +36,12 @@ const cities = [
|
||||
{ key: 'london', zone: 'Europe/London' },
|
||||
{ key: 'tokyo', zone: 'Asia/Tokyo' },
|
||||
]
|
||||
const tabs = ['world', 'alarm', 'stopwatch', 'timer'] as const
|
||||
const tabs = [
|
||||
{ id: 'world', icon: Clock3 },
|
||||
{ id: 'alarm', icon: AlarmClock },
|
||||
{ id: 'stopwatch', icon: Timer },
|
||||
{ id: 'timer', icon: TimerReset },
|
||||
] as const
|
||||
function cityTime(zone: string): string {
|
||||
return new Intl.DateTimeFormat(phone.lang, {
|
||||
hour: '2-digit',
|
||||
@@ -53,14 +59,25 @@ onBeforeUnmount(() => clearInterval(ticker))
|
||||
|
||||
<template>
|
||||
<main class="native-app clock-app">
|
||||
<header class="app-header">
|
||||
<header class="reference-clock-header">
|
||||
<nav>
|
||||
<button type="button">{{ phone.t('Common.edit') }}</button
|
||||
><button type="button" :aria-label="phone.t('Apps.clock.add')">
|
||||
<Plus :size="25" />
|
||||
</button>
|
||||
</nav>
|
||||
<h1>{{ phone.t(`Apps.clock.tabs.${tab}`) }}</h1>
|
||||
</header>
|
||||
<section class="clock-content">
|
||||
<div v-if="tab === 'world'" class="settings-list">
|
||||
<div v-for="city in cities" :key="city.key" class="settings-row">
|
||||
<span>{{ phone.t(`Apps.clock.cities.${city.key}`) }}</span
|
||||
><strong>{{ cityTime(city.zone) }}</strong>
|
||||
<div v-if="tab === 'world'" class="world-clock-list">
|
||||
<div v-for="city in cities" :key="city.key">
|
||||
<div>
|
||||
<small
|
||||
>{{ phone.t('Apps.clock.today') }},
|
||||
{{ phone.t('Apps.clock.offset') }}</small
|
||||
><strong>{{ phone.t(`Apps.clock.cities.${city.key}`) }}</strong>
|
||||
</div>
|
||||
<time>{{ cityTime(city.zone) }}</time>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="tab === 'alarm'" class="settings-list">
|
||||
@@ -162,15 +179,17 @@ onBeforeUnmount(() => clearInterval(ticker))
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="app-tabs">
|
||||
<nav class="reference-tabbar reference-clock-tabs">
|
||||
<button
|
||||
v-for="item in tabs"
|
||||
:key="item"
|
||||
:class="{ active: tab === item }"
|
||||
:key="item.id"
|
||||
:class="{ active: tab === item.id }"
|
||||
type="button"
|
||||
@click="tab = item"
|
||||
@click="tab = item.id"
|
||||
>
|
||||
{{ phone.t(`Apps.clock.tabs.${item}`) }}
|
||||
<component :is="item.icon" :size="22" /><span>{{
|
||||
phone.t(`Apps.clock.tabs.${item.id}`)
|
||||
}}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</main>
|
||||
|
||||
@@ -1,71 +1,134 @@
|
||||
<script setup lang="ts">
|
||||
import { Heart, Images, Library, Search } from 'lucide-vue-next'
|
||||
import { computed, ref } from 'vue'
|
||||
import { Search } from 'lucide-vue-next'
|
||||
|
||||
import { useMediaStore } from '@/stores/media'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
|
||||
const media = useMediaStore()
|
||||
const phone = usePhoneStore()
|
||||
const tab = ref('library')
|
||||
const tab = ref<'library' | 'forYou' | 'albums' | 'search'>('library')
|
||||
const query = ref('')
|
||||
const tabs = ['library', 'forYou', 'albums', 'search']
|
||||
const photos = computed(() =>
|
||||
const tabs = [
|
||||
{ id: 'library', icon: Library },
|
||||
{ id: 'forYou', icon: Heart },
|
||||
{ id: 'albums', icon: Images },
|
||||
{ id: 'search', icon: Search },
|
||||
] as const
|
||||
const gallery = computed(() =>
|
||||
Array.from(
|
||||
{ length: 54 },
|
||||
(_, index) => media.photos[index % media.photos.length],
|
||||
),
|
||||
)
|
||||
const filtered = computed(() =>
|
||||
media.photos.filter((photo) =>
|
||||
phone.t(photo.titleKey).toLowerCase().includes(query.value.toLowerCase()),
|
||||
),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="native-app photos-app">
|
||||
<header class="app-header">
|
||||
<h1>{{ phone.t(`Apps.photos.tabs.${tab}`) }}</h1>
|
||||
</header>
|
||||
<div v-if="tab === 'search'" class="app-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.photos.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<section v-if="tab === 'albums'" class="album-cards">
|
||||
<article>
|
||||
<div
|
||||
class="photo-tile"
|
||||
:style="{ background: media.photos[0]?.gradient }"
|
||||
<main class="native-app reference-photos">
|
||||
<section v-if="tab === 'library'" class="photos-library">
|
||||
<header class="photos-floating-header">
|
||||
<div>
|
||||
<strong>{{ phone.t('Apps.photos.dateRange') }}</strong
|
||||
><span>{{ phone.t('Apps.photos.place') }}</span>
|
||||
</div>
|
||||
<button type="button">{{ phone.t('Apps.photos.select') }}</button
|
||||
><button type="button">•••</button>
|
||||
</header>
|
||||
<div class="reference-photo-grid">
|
||||
<article
|
||||
v-for="(photo, index) in gallery"
|
||||
:key="`${photo.id}-${index}`"
|
||||
:style="{ background: photo.gradient }"
|
||||
/>
|
||||
<strong>{{ phone.t('Apps.photos.recents') }}</strong
|
||||
><small
|
||||
>{{ media.photos.length }} {{ phone.t('Apps.photos.items') }}</small
|
||||
>
|
||||
</article>
|
||||
<article>
|
||||
<div class="photo-tile favorites" />
|
||||
<strong>{{ phone.t('Apps.photos.favorites') }}</strong
|
||||
><small>2 {{ phone.t('Apps.photos.items') }}</small>
|
||||
</article>
|
||||
</section>
|
||||
<section v-else-if="tab === 'forYou'" class="featured-photo">
|
||||
<p>{{ phone.t('Apps.photos.memories') }}</p>
|
||||
<div :style="{ background: media.photos[1]?.gradient }">
|
||||
<span>{{ phone.t('Apps.photos.featured') }}</span>
|
||||
</div>
|
||||
<p class="photos-count">{{ phone.t('Apps.photos.count') }}</p>
|
||||
<div class="photos-period">
|
||||
<button>{{ phone.t('Apps.photos.years') }}</button
|
||||
><button>{{ phone.t('Apps.photos.months') }}</button
|
||||
><button>{{ phone.t('Apps.photos.days') }}</button
|
||||
><button class="active">{{ phone.t('Apps.photos.allPhotos') }}</button>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else class="photo-grid">
|
||||
<section v-else-if="tab === 'forYou'" class="photos-scroll">
|
||||
<h1>{{ phone.t('Apps.photos.tabs.forYou') }}</h1>
|
||||
<div class="photos-section-title">
|
||||
<h2>{{ phone.t('Apps.photos.memories') }}</h2>
|
||||
<button>{{ phone.t('Apps.photos.seeAll') }}</button>
|
||||
</div>
|
||||
<article
|
||||
v-for="photo in photos"
|
||||
:key="photo.id"
|
||||
class="photo-tile"
|
||||
:style="{ background: photo.gradient }"
|
||||
:aria-label="phone.t(photo.titleKey)"
|
||||
/>
|
||||
class="memory-card"
|
||||
:style="{ background: media.photos[1]?.gradient }"
|
||||
>
|
||||
<Heart :size="25" />
|
||||
<div>
|
||||
<strong>{{ phone.t('Apps.photos.onThisDay') }}</strong
|
||||
><span>{{ phone.t('Apps.photos.trip') }}</span>
|
||||
</div>
|
||||
</article>
|
||||
<div class="photos-section-title">
|
||||
<h2>{{ phone.t('Apps.photos.featuredPhotos') }}</h2>
|
||||
</div>
|
||||
<div class="featured-row">
|
||||
<article v-for="photo in media.photos.slice(0, 2)" :key="photo.id">
|
||||
<div :style="{ background: photo.gradient }" />
|
||||
<strong>{{ phone.t(photo.titleKey) }}</strong
|
||||
><span>{{ phone.t('Apps.photos.featuredDate') }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="app-tabs">
|
||||
<section v-else-if="tab === 'albums'" class="photos-scroll">
|
||||
<h1>{{ phone.t('Apps.photos.tabs.albums') }}</h1>
|
||||
<div class="album-cards">
|
||||
<article>
|
||||
<div
|
||||
class="photo-tile"
|
||||
:style="{ background: media.photos[0]?.gradient }"
|
||||
/>
|
||||
<strong>{{ phone.t('Apps.photos.recents') }}</strong
|
||||
><small
|
||||
>{{ media.photos.length }} {{ phone.t('Apps.photos.items') }}</small
|
||||
>
|
||||
</article>
|
||||
<article>
|
||||
<div class="photo-tile favorites" />
|
||||
<strong>{{ phone.t('Apps.photos.favorites') }}</strong
|
||||
><small>2 {{ phone.t('Apps.photos.items') }}</small>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<section v-else class="photos-scroll">
|
||||
<h1>{{ phone.t('Apps.photos.tabs.search') }}</h1>
|
||||
<div class="app-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.photos.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="photo-grid">
|
||||
<article
|
||||
v-for="photo in filtered"
|
||||
:key="photo.id"
|
||||
class="photo-tile"
|
||||
:style="{ background: photo.gradient }"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="reference-tabbar">
|
||||
<button
|
||||
v-for="item in tabs"
|
||||
:key="item"
|
||||
:class="{ active: tab === item }"
|
||||
:key="item.id"
|
||||
:class="{ active: tab === item.id }"
|
||||
type="button"
|
||||
@click="tab = item"
|
||||
@click="tab = item.id"
|
||||
>
|
||||
{{ phone.t(`Apps.photos.tabs.${item}`) }}
|
||||
<component :is="item.icon" :size="22" /><span>{{
|
||||
phone.t(`Apps.photos.tabs.${item.id}`)
|
||||
}}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</main>
|
||||
|
||||
@@ -1,68 +1,158 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Accessibility,
|
||||
BatteryMedium,
|
||||
BellRing,
|
||||
Bluetooth,
|
||||
ChevronRight,
|
||||
Clock3,
|
||||
Focus,
|
||||
Globe2,
|
||||
Grid2X2,
|
||||
KeyRound,
|
||||
LockKeyhole,
|
||||
Monitor,
|
||||
Plane,
|
||||
Radio,
|
||||
Search,
|
||||
Settings,
|
||||
Smartphone,
|
||||
Sun,
|
||||
UserRound,
|
||||
Volume2,
|
||||
Wifi,
|
||||
} from 'lucide-vue-next'
|
||||
import { computed, ref } from 'vue'
|
||||
import { Bluetooth, Search, Wifi, Radio } from 'lucide-vue-next'
|
||||
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
import { WALLPAPER_IDS } from '@/utils/preferences'
|
||||
|
||||
const phone = usePhoneStore()
|
||||
const query = ref('')
|
||||
const rows = [
|
||||
{ key: 'wifi', icon: Wifi, color: '#1677ff' },
|
||||
{ key: 'bluetooth', icon: Bluetooth, color: '#1677ff' },
|
||||
{ key: 'airplaneMode', icon: Radio, color: '#ff9500' },
|
||||
] as const
|
||||
const shown = computed(() =>
|
||||
rows.filter((row) =>
|
||||
phone
|
||||
.t(`Apps.settings.${row.key}`)
|
||||
.toLowerCase()
|
||||
.includes(query.value.toLowerCase()),
|
||||
),
|
||||
const groups = [
|
||||
[
|
||||
{ key: 'airplaneMode', icon: Plane, color: '#ff9500', toggle: true },
|
||||
{
|
||||
key: 'wifi',
|
||||
icon: Wifi,
|
||||
color: '#0a84ff',
|
||||
value: 'SkyNet 5G',
|
||||
toggle: true,
|
||||
},
|
||||
{
|
||||
key: 'bluetooth',
|
||||
icon: Bluetooth,
|
||||
color: '#0a84ff',
|
||||
valueKey: 'Apps.settings.on',
|
||||
toggle: true,
|
||||
},
|
||||
{ key: 'mobileServices', icon: Radio, color: '#30d158' },
|
||||
{ key: 'personalHotspot', icon: Smartphone, color: '#30d158' },
|
||||
{ key: 'vpn', icon: Globe2, color: '#0a84ff' },
|
||||
],
|
||||
[
|
||||
{ key: 'notifications', icon: BellRing, color: '#ff453a' },
|
||||
{ key: 'sounds', icon: Volume2, color: '#ff2d55' },
|
||||
{ key: 'focus', icon: Focus, color: '#5e5ce6' },
|
||||
{ key: 'screenTime', icon: Clock3, color: '#5e5ce6' },
|
||||
],
|
||||
[
|
||||
{ key: 'general', icon: Settings, color: '#8e8e93' },
|
||||
{ key: 'display', icon: Sun, color: '#0a84ff' },
|
||||
{ key: 'homeScreen', icon: Grid2X2, color: '#5e5ce6' },
|
||||
{ key: 'accessibility', icon: Accessibility, color: '#0a84ff' },
|
||||
{ key: 'wallpaper', icon: Monitor, color: '#64d2ff' },
|
||||
{ key: 'battery', icon: BatteryMedium, color: '#30d158' },
|
||||
{ key: 'privacy', icon: LockKeyhole, color: '#0a84ff' },
|
||||
],
|
||||
[{ key: 'passwords', icon: KeyRound, color: '#636366' }],
|
||||
]
|
||||
const filteredGroups = computed(() =>
|
||||
groups
|
||||
.map((group) =>
|
||||
group.filter((row) =>
|
||||
phone
|
||||
.t(`Apps.settings.${row.key}`)
|
||||
.toLowerCase()
|
||||
.includes(query.value.toLowerCase()),
|
||||
),
|
||||
)
|
||||
.filter((group) => group.length),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="native-app settings-app">
|
||||
<header class="app-header">
|
||||
<main class="native-app reference-settings">
|
||||
<section class="settings-scroll">
|
||||
<h1>{{ phone.t('Apps.settings.name') }}</h1>
|
||||
</header>
|
||||
<div class="app-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.settings.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<section class="settings-list">
|
||||
<div v-for="row in shown" :key="row.key" class="settings-row">
|
||||
<span class="settings-symbol" :style="{ background: row.color }"
|
||||
><component :is="row.icon" :size="16" /></span
|
||||
><span>{{ phone.t(`Apps.settings.${row.key}`) }}</span
|
||||
><button
|
||||
class="ios-switch"
|
||||
:class="{ active: phone.preferences.settings[row.key] }"
|
||||
type="button"
|
||||
:aria-label="phone.t(`Apps.settings.toggle.${row.key}`)"
|
||||
@click="
|
||||
phone.setSetting(row.key, !phone.preferences.settings[row.key])
|
||||
"
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
<div class="settings-search">
|
||||
<Search :size="17" /><input
|
||||
v-model="query"
|
||||
:placeholder="phone.t('Apps.settings.searchPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<h2 class="settings-section-title">
|
||||
{{ phone.t('Apps.settings.wallpaper') }}
|
||||
</h2>
|
||||
<section class="wallpaper-picker">
|
||||
<button
|
||||
v-for="wallpaper in WALLPAPER_IDS"
|
||||
:key="wallpaper"
|
||||
type="button"
|
||||
:class="[
|
||||
`wallpaper--${wallpaper}`,
|
||||
{ active: phone.preferences.settings.wallpaper === wallpaper },
|
||||
]"
|
||||
:aria-label="phone.t(`Apps.settings.wallpapers.${wallpaper}`)"
|
||||
@click="phone.setWallpaper(wallpaper)"
|
||||
/>
|
||||
<article class="settings-account">
|
||||
<span><UserRound :size="34" /></span>
|
||||
<div>
|
||||
<strong>{{ phone.t('Apps.settings.accountName') }}</strong
|
||||
><small>{{ phone.t('Apps.settings.accountDetail') }}</small>
|
||||
</div>
|
||||
<ChevronRight :size="19" />
|
||||
</article>
|
||||
<section
|
||||
v-for="(group, groupIndex) in filteredGroups"
|
||||
:key="groupIndex"
|
||||
class="reference-settings-group"
|
||||
>
|
||||
<article v-for="row in group" :key="row.key">
|
||||
<span class="settings-symbol" :style="{ background: row.color }"
|
||||
><component :is="row.icon" :size="16"
|
||||
/></span>
|
||||
<div>
|
||||
<strong>{{ phone.t(`Apps.settings.${row.key}`) }}</strong
|
||||
><small v-if="row.value">{{ row.value }}</small
|
||||
><small v-else-if="row.valueKey">{{ phone.t(row.valueKey) }}</small>
|
||||
</div>
|
||||
<button
|
||||
v-if="row.toggle"
|
||||
class="ios-switch"
|
||||
:class="{
|
||||
active:
|
||||
phone.preferences.settings[
|
||||
row.key as 'airplaneMode' | 'wifi' | 'bluetooth'
|
||||
],
|
||||
}"
|
||||
type="button"
|
||||
:aria-label="phone.t(`Apps.settings.toggle.${row.key}`)"
|
||||
@click="
|
||||
phone.setSetting(
|
||||
row.key as 'airplaneMode' | 'wifi' | 'bluetooth',
|
||||
!phone.preferences.settings[
|
||||
row.key as 'airplaneMode' | 'wifi' | 'bluetooth'
|
||||
],
|
||||
)
|
||||
"
|
||||
>
|
||||
<span /></button
|
||||
><ChevronRight v-else :size="18" class="settings-chevron" />
|
||||
</article>
|
||||
</section>
|
||||
<h2 class="settings-section-title">
|
||||
{{ phone.t('Apps.settings.wallpaperPicker') }}
|
||||
</h2>
|
||||
<section class="wallpaper-picker">
|
||||
<button
|
||||
v-for="wallpaper in WALLPAPER_IDS"
|
||||
:key="wallpaper"
|
||||
type="button"
|
||||
:class="[
|
||||
`wallpaper--${wallpaper}`,
|
||||
{ active: phone.preferences.settings.wallpaper === wallpaper },
|
||||
]"
|
||||
:aria-label="phone.t(`Apps.settings.wallpapers.${wallpaper}`)"
|
||||
@click="phone.setWallpaper(wallpaper)"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@ Locales["en"] = {
|
||||
CommandDescription = "Open your phone.",
|
||||
Nui = {
|
||||
Common = {
|
||||
cancel = "Cancel", close = "Close", home = "Home", pause = "Pause",
|
||||
cancel = "Cancel", close = "Close", edit = "Edit", home = "Home", pause = "Pause",
|
||||
phone = "Phone", phoneStatus = "Phone status", reset = "Reset",
|
||||
search = "Search", start = "Start", stop = "Stop",
|
||||
},
|
||||
@@ -21,11 +21,11 @@ Locales["en"] = {
|
||||
Apps = {
|
||||
calculator = { name = "Calculator" },
|
||||
camera = {
|
||||
name = "Camera", shutter = "Take photo", flip = "Flip camera",
|
||||
modes = { video = "Video", photo = "Photo", portrait = "Portrait" },
|
||||
name = "Camera", shutter = "Take photo", flip = "Flip camera", flash = "Toggle flash", controls = "Camera controls",
|
||||
modes = { timelapse = "Timelapse", slowMo = "Slow-Mo", cinematic = "Cinematic", video = "Video", photo = "Photo", portrait = "Portrait", pano = "Pano" },
|
||||
},
|
||||
clock = {
|
||||
name = "Clock", lap = "Lap", minutes = "Minutes",
|
||||
name = "Clock", lap = "Lap", minutes = "Minutes", add = "Add clock", today = "Today", offset = "+0HRS",
|
||||
tabs = { world = "World Clock", alarm = "Alarm", stopwatch = "Stopwatch", timer = "Timer" },
|
||||
cities = { cupertino = "Cupertino", newYork = "New York", london = "London", tokyo = "Tokyo" },
|
||||
alarm = { weekday = "Weekdays", weekend = "Weekend" },
|
||||
@@ -33,6 +33,9 @@ Locales["en"] = {
|
||||
photos = {
|
||||
name = "Photos", searchPlaceholder = "Photos, people, places...", recents = "Recents",
|
||||
favorites = "Favorites", items = "items", memories = "Memories", featured = "City colors",
|
||||
dateRange = "19 Apr–7 May 2024", place = "Los Santos & more", select = "Select", count = "3,042 Photos, 125 Videos",
|
||||
years = "Years", months = "Months", days = "Days", allPhotos = "All Photos", seeAll = "See All",
|
||||
onThisDay = "On This Day", trip = "MAR 2024 TRIP", featuredPhotos = "Featured Photos", featuredDate = "30 Mar 2024",
|
||||
tabs = { library = "Library", forYou = "For You", albums = "Albums", search = "Search" },
|
||||
samples = { sunset = "Sunset drive", ocean = "Ocean air", city = "City lights", desert = "Desert road", capture = "Camera capture" },
|
||||
},
|
||||
@@ -40,12 +43,23 @@ Locales["en"] = {
|
||||
name = "App Store", eyebrow = "Discover", featured = "Featured", heroTitle = "Apps for every day",
|
||||
heroBody = "Fresh ideas, built for your life in the city.", get = "GET", open = "OPEN",
|
||||
searchPlaceholder = "Games, Apps, Stories and More",
|
||||
communityTitle = "Black Voices and Creators", communityBody = "Apps and games from the community",
|
||||
playing = "What We're Playing", recommended = "Recommended for You", selected = "Great apps selected by our editors",
|
||||
tabs = { today = "Today", apps = "Apps", games = "Games", arcade = "Arcade", search = "Search" },
|
||||
catalog = { orbit = "Plan your day", studio = "Create something new", trail = "Explore nearby", prism = "A colorful puzzle" },
|
||||
card = {
|
||||
oneEyebrow = "App of the Day", oneTitle = "A universe in your pocket", oneBody = "Explore something extraordinary today.",
|
||||
twoEyebrow = "Now Trending", twoTitle = "Turn up your afternoon", twoBody = "Fresh sounds and stories picked for you.",
|
||||
threeEyebrow = "Editors' Choice", threeTitle = "Play without limits", threeBody = "A new world is waiting.",
|
||||
},
|
||||
},
|
||||
settings = {
|
||||
name = "Settings", searchPlaceholder = "Search", wifi = "Wi-Fi", bluetooth = "Bluetooth",
|
||||
airplaneMode = "Airplane Mode", wallpaper = "Wallpaper",
|
||||
airplaneMode = "Airplane Mode", wallpaper = "Wallpaper", on = "On", accountName = "Sky Citizen",
|
||||
accountDetail = "Apple ID, iCloud, Media & Purchases", mobileServices = "Mobile Services", personalHotspot = "Personal Hotspot",
|
||||
vpn = "VPN", notifications = "Notifications", sounds = "Sounds & Haptics", focus = "Focus", screenTime = "Screen Time",
|
||||
general = "General", display = "Display & Brightness", homeScreen = "Home Screen & App Library", accessibility = "Accessibility",
|
||||
battery = "Battery", privacy = "Privacy & Security", passwords = "Passwords", wallpaperPicker = "Built-in Wallpapers",
|
||||
toggle = { wifi = "Toggle Wi-Fi", bluetooth = "Toggle Bluetooth", airplaneMode = "Toggle Airplane Mode" },
|
||||
wallpapers = { midnight = "Midnight wallpaper", aurora = "Aurora wallpaper", ember = "Ember wallpaper" },
|
||||
},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import{a as v,u as A,o as l,c as p,b as e,t as s,e as a,g as m,S as y,w as k,v as C,m as w,F as u,r as h,p as z,n as B,h as g,l as V}from"./sky-index-DrcmepK6.js";import{u as $}from"./sky-media-DNqFydRb.js";const x={class:"native-app store-app"},L={class:"app-header"},N={key:0,class:"app-search"},P=["placeholder"],T={class:"store-feature"},D={class:"store-list"},F=["onClick"],M={class:"app-tabs"},q=["onClick"],j=v({__name:"AppStoreApp",setup(E){const o=A(),i=$(),r=g("today"),n=g(""),b=["today","apps","games","arcade","search"],_=[{id:"orbit",title:"Orbit",subtitle:"Apps.appStore.catalog.orbit",gradient:"linear-gradient(135deg,#6f5cff,#29d3c2)"},{id:"studio",title:"Studio",subtitle:"Apps.appStore.catalog.studio",gradient:"linear-gradient(135deg,#ff8b4a,#ff2d75)"},{id:"trail",title:"Trail",subtitle:"Apps.appStore.catalog.trail",gradient:"linear-gradient(135deg,#4bd37b,#147ec1)"},{id:"puzzle",title:"Prism",subtitle:"Apps.appStore.catalog.prism",gradient:"linear-gradient(135deg,#ffd84d,#7b36dc)"}],S=V(()=>_.filter(d=>d.title.toLowerCase().includes(n.value.toLowerCase())));return(d,c)=>(l(),p("main",x,[e("header",L,[e("small",null,s(a(o).t("Apps.appStore.eyebrow")),1),e("h1",null,s(a(o).t(`Apps.appStore.tabs.${r.value}`)),1)]),r.value==="search"?(l(),p("div",N,[m(a(y),{size:17}),k(e("input",{"onUpdate:modelValue":c[0]||(c[0]=t=>n.value=t),placeholder:a(o).t("Apps.appStore.searchPlaceholder")},null,8,P),[[C,n.value]])])):w("",!0),e("section",T,[e("p",null,s(a(o).t("Apps.appStore.featured")),1),e("h2",null,s(a(o).t("Apps.appStore.heroTitle")),1),e("span",null,s(a(o).t("Apps.appStore.heroBody")),1)]),e("section",D,[(l(!0),p(u,null,h(S.value,t=>(l(),p("article",{key:t.id},[e("div",{class:"store-icon",style:z({background:t.gradient})},s(t.title[0]),5),e("div",null,[e("strong",null,s(t.title),1),e("small",null,s(a(o).t(t.subtitle)),1)]),e("button",{type:"button",onClick:f=>a(i).claimApp(t.id)},s(a(o).t(a(i).claimedApps.includes(t.id)?"Apps.appStore.open":"Apps.appStore.get")),9,F)]))),128))]),e("nav",M,[(l(),p(u,null,h(b,t=>e("button",{key:t,class:B({active:r.value===t}),type:"button",onClick:f=>r.value=t},s(a(o).t(`Apps.appStore.tabs.${t}`)),11,q)),64))])]))}});export{j as default};
|
||||
@@ -1 +1 @@
|
||||
import{d as b,a as g,u as y,o as s,c as u,b as t,t as p,e as d,F as m,r as O,n as h}from"./sky-index-DrcmepK6.js";const _={accumulator:null,display:"0",error:!1,pendingOperator:null,waitingForOperand:!1};function c(r){if(!Number.isFinite(r))return"Error";const n=Number(r.toPrecision(12)),a=String(n);return a.length<=12?a:n.toExponential(6)}function f(r,n,a){return a==="add"?r+n:a==="subtract"?r-n:a==="multiply"?r*n:n===0?null:r/n}function l(){return{..._}}function k(r,n){return/^\d$/.test(n)?r.error||r.waitingForOperand?{...r,display:n,error:!1,waitingForOperand:!1}:r.display==="0"?{...r,display:n}:r.display.replace("-","").replace(".","").length>=10?r:{...r,display:`${r.display}${n}`}:r}function C(r){return r.error||r.waitingForOperand?{...r,display:"0.",error:!1,waitingForOperand:!1}:r.display.includes(".")?r:{...r,display:`${r.display}.`}}function F(r){return r.error||r.display==="0"?r:{...r,display:r.display.startsWith("-")?r.display.slice(1):`-${r.display}`}}function $(r){return r.error?r:{...r,display:c(Number(r.display)/100)}}function S(r,n){if(r.error)return l();const a=Number(r.display);let e=r.accumulator;if(e!==null&&r.pendingOperator&&!r.waitingForOperand){const i=f(e,a,r.pendingOperator);if(i===null)return{...l(),display:"Error",error:!0};e=i}else e===null&&(e=a);return{accumulator:e,display:c(e),error:!1,pendingOperator:n,waitingForOperand:!0}}function w(r){if(r.error||r.accumulator===null||!r.pendingOperator)return r;const n=f(r.accumulator,Number(r.display),r.pendingOperator);return n===null?{...l(),display:"Error",error:!0}:{accumulator:null,display:c(n),error:!1,pendingOperator:null,waitingForOperand:!0}}const N=b("calculator",{state:()=>l(),actions:{chooseOperator(r){Object.assign(this,S(this.$state,r))},clear(){Object.assign(this,l())},decimal(){Object.assign(this,C(this.$state))},digit(r){Object.assign(this,k(this.$state,r))},equals(){Object.assign(this,w(this.$state))},percent(){Object.assign(this,$(this.$state))},toggleSign(){Object.assign(this,F(this.$state))}}}),j=["aria-label"],A={class:"calculator-display","aria-live":"polite"},E={class:"calculator-pad"},L=["onClick"],B=g({__name:"CalculatorApp",setup(r){const n=N(),a=y(),e=[{label:"AC",action:n.clear,kind:"utility"},{label:"+/−",action:n.toggleSign,kind:"utility"},{label:"%",action:n.percent,kind:"utility"},{label:"÷",action:()=>n.chooseOperator("divide"),kind:"operator"},...["7","8","9"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"×",action:()=>n.chooseOperator("multiply"),kind:"operator"},...["4","5","6"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"−",action:()=>n.chooseOperator("subtract"),kind:"operator"},...["1","2","3"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"+",action:()=>n.chooseOperator("add"),kind:"operator"},{label:"0",action:()=>n.digit("0"),kind:"zero"},{label:".",action:n.decimal},{label:"=",action:n.equals,kind:"operator"}];return(i,T)=>(s(),u("main",{class:"native-app calculator-app","aria-label":d(a).t("Apps.calculator.name")},[t("div",A,p(d(n).display),1),t("div",E,[(s(),u(m,null,O(e,o=>t("button",{key:o.label,type:"button",class:h(o.kind),onClick:o.action},p(o.label),11,L)),64))])],8,j))}});export{B as default};
|
||||
import{d as b,a as g,u as y,o as s,c as u,b as t,t as p,e as d,F as m,r as O,n as h}from"./sky-index-CHPJPv4D.js";const _={accumulator:null,display:"0",error:!1,pendingOperator:null,waitingForOperand:!1};function c(r){if(!Number.isFinite(r))return"Error";const n=Number(r.toPrecision(12)),a=String(n);return a.length<=12?a:n.toExponential(6)}function f(r,n,a){return a==="add"?r+n:a==="subtract"?r-n:a==="multiply"?r*n:n===0?null:r/n}function l(){return{..._}}function k(r,n){return/^\d$/.test(n)?r.error||r.waitingForOperand?{...r,display:n,error:!1,waitingForOperand:!1}:r.display==="0"?{...r,display:n}:r.display.replace("-","").replace(".","").length>=10?r:{...r,display:`${r.display}${n}`}:r}function C(r){return r.error||r.waitingForOperand?{...r,display:"0.",error:!1,waitingForOperand:!1}:r.display.includes(".")?r:{...r,display:`${r.display}.`}}function F(r){return r.error||r.display==="0"?r:{...r,display:r.display.startsWith("-")?r.display.slice(1):`-${r.display}`}}function $(r){return r.error?r:{...r,display:c(Number(r.display)/100)}}function S(r,n){if(r.error)return l();const a=Number(r.display);let e=r.accumulator;if(e!==null&&r.pendingOperator&&!r.waitingForOperand){const i=f(e,a,r.pendingOperator);if(i===null)return{...l(),display:"Error",error:!0};e=i}else e===null&&(e=a);return{accumulator:e,display:c(e),error:!1,pendingOperator:n,waitingForOperand:!0}}function w(r){if(r.error||r.accumulator===null||!r.pendingOperator)return r;const n=f(r.accumulator,Number(r.display),r.pendingOperator);return n===null?{...l(),display:"Error",error:!0}:{accumulator:null,display:c(n),error:!1,pendingOperator:null,waitingForOperand:!0}}const N=b("calculator",{state:()=>l(),actions:{chooseOperator(r){Object.assign(this,S(this.$state,r))},clear(){Object.assign(this,l())},decimal(){Object.assign(this,C(this.$state))},digit(r){Object.assign(this,k(this.$state,r))},equals(){Object.assign(this,w(this.$state))},percent(){Object.assign(this,$(this.$state))},toggleSign(){Object.assign(this,F(this.$state))}}}),j=["aria-label"],A={class:"calculator-display","aria-live":"polite"},E={class:"calculator-pad"},L=["onClick"],B=g({__name:"CalculatorApp",setup(r){const n=N(),a=y(),e=[{label:"AC",action:n.clear,kind:"utility"},{label:"+/−",action:n.toggleSign,kind:"utility"},{label:"%",action:n.percent,kind:"utility"},{label:"÷",action:()=>n.chooseOperator("divide"),kind:"operator"},...["7","8","9"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"×",action:()=>n.chooseOperator("multiply"),kind:"operator"},...["4","5","6"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"−",action:()=>n.chooseOperator("subtract"),kind:"operator"},...["1","2","3"].map(i=>({label:i,action:()=>n.digit(i)})),{label:"+",action:()=>n.chooseOperator("add"),kind:"operator"},{label:"0",action:()=>n.digit("0"),kind:"zero"},{label:".",action:n.decimal},{label:"=",action:n.equals,kind:"operator"}];return(i,T)=>(s(),u("main",{class:"native-app calculator-app","aria-label":d(a).t("Apps.calculator.name")},[t("div",A,p(d(n).display),1),t("div",E,[(s(),u(m,null,O(e,o=>t("button",{key:o.label,type:"button",class:h(o.kind),onClick:o.action},p(o.label),11,L)),64))])],8,j))}});export{B as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{u as h}from"./sky-media-DNqFydRb.js";import{f as b,a as k,u as C,o as i,c as l,b as e,n,F as u,r as p,e as a,t as m,g as d,C as y,h as g}from"./sky-index-DrcmepK6.js";const F=b("rotate-ccw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]),M={class:"native-app camera-app"},$={class:"camera-zoom"},A=["onClick"],w={class:"camera-modes"},x=["onClick"],B={class:"camera-controls"},L=["aria-label"],S=["aria-label"],z=k({__name:"CameraApp",setup(N){const s=h(),r=C(),c=g(!1),v=["video","photo","portrait"];function _(){c.value=!0,s.capture(),setTimeout(()=>{c.value=!1},120)}return(V,o)=>(i(),l("main",M,[e("div",{class:n(["camera-viewfinder",`camera-facing--${a(s).cameraFacing}`])},[e("div",{class:n(["camera-flash",{active:c.value}])},null,2),o[1]||(o[1]=e("div",{class:"camera-grid"},null,-1)),e("div",$,[(i(),l(u,null,p([.5,1,2],t=>e("button",{key:t,class:n({active:a(s).cameraZoom===t}),type:"button",onClick:f=>a(s).cameraZoom=t},m(t)+"× ",11,A)),64))])],2),e("div",w,[(i(),l(u,null,p(v,t=>e("button",{key:t,class:n({active:a(s).cameraMode===t}),type:"button",onClick:f=>a(s).cameraMode=t},m(a(r).t(`Apps.camera.modes.${t}`)),11,x)),64))]),e("div",B,[o[2]||(o[2]=e("span",null,null,-1)),e("button",{class:"shutter",type:"button","aria-label":a(r).t("Apps.camera.shutter"),onClick:_},[d(a(y),{size:26})],8,L),e("button",{type:"button","aria-label":a(r).t("Apps.camera.flip"),onClick:o[0]||(o[0]=t=>a(s).cameraFacing=a(s).cameraFacing==="rear"?"front":"rear")},[d(a(F),{size:23})],8,S)])]))}});export{z as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{u as M}from"./sky-media-hPsqn053.js";import{f as i,a as g,u as w,o as r,c,b as t,e,g as f,h as n,n as l,F as h,r as m,i as x,t as y,j as z,k as A,C as $,l as k}from"./sky-index-CHPJPv4D.js";const b=i("aperture",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m14.31 8 5.74 9.94",key:"1y6ab4"}],["path",{d:"M9.69 8h11.48",key:"1wxppr"}],["path",{d:"m7.38 12 5.74-9.94",key:"1grp0k"}],["path",{d:"M9.69 16 3.95 6.06",key:"libnyf"}],["path",{d:"M14.31 16H2.83",key:"x5fava"}],["path",{d:"m16.62 12-5.74 9.94",key:"1vwawt"}]]);const F=i("chevron-up",[["path",{d:"m18 15-6-6-6 6",key:"153udz"}]]);const B=i("rotate-ccw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);const N=i("zap-off",[["path",{d:"M10.513 4.856 13.12 2.17a.5.5 0 0 1 .86.46l-1.377 4.317",key:"193nxd"}],["path",{d:"M15.656 10H20a1 1 0 0 1 .78 1.63l-1.72 1.773",key:"27a7lr"}],["path",{d:"M16.273 16.273 10.88 21.83a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14H4a1 1 0 0 1-.78-1.63l4.507-4.643",key:"1e0qe9"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]]);const S=i("zap",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]]),V={class:"native-app reference-camera"},Z={class:"camera-topbar"},q=["aria-label"],H=["aria-label"],L={class:"camera-zoom"},E=["onClick"],T={key:0},j={class:"reference-camera-footer"},D={class:"camera-mode-strip"},I=["onClick"],O={class:"camera-shutter-row"},P=["aria-label"],R=["aria-label"],Q=g({__name:"CameraApp",setup(U){const s=M(),o=w(),u=k(!1),d=k(!1),v=["timelapse","slowMo","cinematic","video","photo","portrait","pano"];function _(){u.value=!0,s.capture(),window.setTimeout(()=>u.value=!1,120)}return(G,p)=>(r(),c("main",V,[t("header",Z,[t("div",null,[t("button",{type:"button","aria-label":e(o).t("Apps.camera.flash"),onClick:p[0]||(p[0]=a=>d.value=!d.value)},[d.value?(r(),f(e(S),{key:0,size:22})):(r(),f(e(N),{key:1,size:22}))],8,q),n(e(b),{size:22})]),t("button",{class:"camera-chevron",type:"button","aria-label":e(o).t("Apps.camera.controls")},[n(e(F),{size:20})],8,H),n(e(b),{size:22})]),t("section",{class:l(["reference-viewfinder",`camera-facing--${e(s).cameraFacing}`])},[t("div",{class:l(["camera-flash",{active:u.value}])},null,2),(r(),c(h,null,m(["tl","tr","bl","br"],a=>t("i",{key:a,class:l(`focus-corner focus-corner--${a}`)},null,2)),64)),t("div",L,[(r(),c(h,null,m([.5,1],a=>t("button",{key:a,class:l({active:e(s).cameraZoom===a}),type:"button",onClick:C=>e(s).cameraZoom=a},[x(y(a),1),a===1?(r(),c("small",T,"x")):z("",!0)],10,E)),64))])],2),t("footer",j,[t("nav",D,[(r(),c(h,null,m(v,a=>t("button",{key:a,class:l({active:e(s).cameraMode===a}),type:"button",onClick:C=>a==="photo"||a==="portrait"||a==="video"?e(s).cameraMode=a:void 0},y(e(o).t(`Apps.camera.modes.${a}`)),11,I)),64))]),t("div",O,[t("div",{class:"camera-thumbnail",style:A({background:e(s).photos[0]?.gradient})},null,4),t("button",{class:"reference-shutter",type:"button","aria-label":e(o).t("Apps.camera.shutter"),onClick:_},[n(e($),{size:23})],8,P),t("button",{class:"reference-flip",type:"button","aria-label":e(o).t("Apps.camera.flip"),onClick:p[1]||(p[1]=a=>e(s).cameraFacing=e(s).cameraFacing==="rear"?"front":"rear")},[n(e(B),{size:22})],8,R)])])]))}});export{Q as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{f as k,a as S,u as w,o as a,c as n,b as t,t as e,e as s,F as u,r as h,k as c,h as v,S as b,w as z,x as D,n as L,g as P,q as x,I as B,l as g,v as m}from"./sky-index-CHPJPv4D.js";import{u as $}from"./sky-media-hPsqn053.js";const A=k("heart",[["path",{d:"M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z",key:"c3ymky"}]]);const q=k("library",[["path",{d:"m16 6 4 14",key:"ji33uf"}],["path",{d:"M12 6v14",key:"1n7gus"}],["path",{d:"M8 8v12",key:"1gg7y9"}],["path",{d:"M4 4v16",key:"6qkkli"}]]),I={class:"native-app reference-photos"},V={key:0,class:"photos-library"},Y={class:"photos-floating-header"},F={type:"button"},K={class:"reference-photo-grid"},N={class:"photos-count"},T={class:"photos-period"},j={class:"active"},E={key:1,class:"photos-scroll"},H={class:"photos-section-title"},R={class:"photos-section-title"},U={class:"featured-row"},Z={key:2,class:"photos-scroll"},G={class:"album-cards"},J={key:3,class:"photos-scroll"},O={class:"app-search"},Q=["placeholder"],W={class:"photo-grid"},X={class:"reference-tabbar"},tt=["onClick"],lt=S({__name:"PhotosApp",setup(st){const r=$(),o=w(),i=g("library"),d=g(""),f=[{id:"library",icon:q},{id:"forYou",icon:A},{id:"albums",icon:B},{id:"search",icon:b}],C=m(()=>Array.from({length:54},(_,p)=>r.photos[p%r.photos.length])),M=m(()=>r.photos.filter(_=>o.t(_.titleKey).toLowerCase().includes(d.value.toLowerCase())));return(_,p)=>(a(),n("main",I,[i.value==="library"?(a(),n("section",V,[t("header",Y,[t("div",null,[t("strong",null,e(s(o).t("Apps.photos.dateRange")),1),t("span",null,e(s(o).t("Apps.photos.place")),1)]),t("button",F,e(s(o).t("Apps.photos.select")),1),p[1]||(p[1]=t("button",{type:"button"},"•••",-1))]),t("div",K,[(a(!0),n(u,null,h(C.value,(l,y)=>(a(),n("article",{key:`${l.id}-${y}`,style:c({background:l.gradient})},null,4))),128))]),t("p",N,e(s(o).t("Apps.photos.count")),1),t("div",T,[t("button",null,e(s(o).t("Apps.photos.years")),1),t("button",null,e(s(o).t("Apps.photos.months")),1),t("button",null,e(s(o).t("Apps.photos.days")),1),t("button",j,e(s(o).t("Apps.photos.allPhotos")),1)])])):i.value==="forYou"?(a(),n("section",E,[t("h1",null,e(s(o).t("Apps.photos.tabs.forYou")),1),t("div",H,[t("h2",null,e(s(o).t("Apps.photos.memories")),1),t("button",null,e(s(o).t("Apps.photos.seeAll")),1)]),t("article",{class:"memory-card",style:c({background:s(r).photos[1]?.gradient})},[v(s(A),{size:25}),t("div",null,[t("strong",null,e(s(o).t("Apps.photos.onThisDay")),1),t("span",null,e(s(o).t("Apps.photos.trip")),1)])],4),t("div",R,[t("h2",null,e(s(o).t("Apps.photos.featuredPhotos")),1)]),t("div",U,[(a(!0),n(u,null,h(s(r).photos.slice(0,2),l=>(a(),n("article",{key:l.id},[t("div",{style:c({background:l.gradient})},null,4),t("strong",null,e(s(o).t(l.titleKey)),1),t("span",null,e(s(o).t("Apps.photos.featuredDate")),1)]))),128))])])):i.value==="albums"?(a(),n("section",Z,[t("h1",null,e(s(o).t("Apps.photos.tabs.albums")),1),t("div",G,[t("article",null,[t("div",{class:"photo-tile",style:c({background:s(r).photos[0]?.gradient})},null,4),t("strong",null,e(s(o).t("Apps.photos.recents")),1),t("small",null,e(s(r).photos.length)+" "+e(s(o).t("Apps.photos.items")),1)]),t("article",null,[p[2]||(p[2]=t("div",{class:"photo-tile favorites"},null,-1)),t("strong",null,e(s(o).t("Apps.photos.favorites")),1),t("small",null,"2 "+e(s(o).t("Apps.photos.items")),1)])])])):(a(),n("section",J,[t("h1",null,e(s(o).t("Apps.photos.tabs.search")),1),t("div",O,[v(s(b),{size:17}),z(t("input",{"onUpdate:modelValue":p[0]||(p[0]=l=>d.value=l),placeholder:s(o).t("Apps.photos.searchPlaceholder")},null,8,Q),[[D,d.value]])]),t("div",W,[(a(!0),n(u,null,h(M.value,l=>(a(),n("article",{key:l.id,class:"photo-tile",style:c({background:l.gradient})},null,4))),128))])])),t("nav",X,[(a(),n(u,null,h(f,l=>t("button",{key:l.id,class:L({active:i.value===l.id}),type:"button",onClick:y=>i.value=l.id},[(a(),P(x(l.icon),{size:22})),t("span",null,e(s(o).t(`Apps.photos.tabs.${l.id}`)),1)],10,tt)),64))])]))}});export{lt as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{a as y,u as f,o as l,c as n,b as s,t as a,e,g,S as k,w as A,v as C,m as S,p as u,F as h,r as m,n as w,h as _,l as V}from"./sky-index-DrcmepK6.js";import{u as x}from"./sky-media-DNqFydRb.js";const z={class:"native-app photos-app"},B={class:"app-header"},L={key:0,class:"app-search"},N=["placeholder"],P={key:1,class:"album-cards"},$={key:2,class:"featured-photo"},D={key:3,class:"photo-grid"},F=["aria-label"],K={class:"app-tabs"},M=["onClick"],U=y({__name:"PhotosApp",setup(Y){const p=x(),t=f(),r=_("library"),c=_(""),v=["library","forYou","albums","search"],b=V(()=>p.photos.filter(d=>t.t(d.titleKey).toLowerCase().includes(c.value.toLowerCase())));return(d,i)=>(l(),n("main",z,[s("header",B,[s("h1",null,a(e(t).t(`Apps.photos.tabs.${r.value}`)),1)]),r.value==="search"?(l(),n("div",L,[g(e(k),{size:17}),A(s("input",{"onUpdate:modelValue":i[0]||(i[0]=o=>c.value=o),placeholder:e(t).t("Apps.photos.searchPlaceholder")},null,8,N),[[C,c.value]])])):S("",!0),r.value==="albums"?(l(),n("section",P,[s("article",null,[s("div",{class:"photo-tile",style:u({background:e(p).photos[0]?.gradient})},null,4),s("strong",null,a(e(t).t("Apps.photos.recents")),1),s("small",null,a(e(p).photos.length)+" "+a(e(t).t("Apps.photos.items")),1)]),s("article",null,[i[1]||(i[1]=s("div",{class:"photo-tile favorites"},null,-1)),s("strong",null,a(e(t).t("Apps.photos.favorites")),1),s("small",null,"2 "+a(e(t).t("Apps.photos.items")),1)])])):r.value==="forYou"?(l(),n("section",$,[s("p",null,a(e(t).t("Apps.photos.memories")),1),s("div",{style:u({background:e(p).photos[1]?.gradient})},[s("span",null,a(e(t).t("Apps.photos.featured")),1)],4)])):(l(),n("section",D,[(l(!0),n(h,null,m(b.value,o=>(l(),n("article",{key:o.id,class:"photo-tile",style:u({background:o.gradient}),"aria-label":e(t).t(o.titleKey)},null,12,F))),128))])),s("nav",K,[(l(),n(h,null,m(v,o=>s("button",{key:o,class:w({active:r.value===o}),type:"button",onClick:q=>r.value=o},a(e(t).t(`Apps.photos.tabs.${o}`)),11,M)),64))])]))}});export{U as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{f as h,a as _,u as f,o as n,c as o,b as a,t as c,e as s,g as m,S as v,w as b,v as A,F as p,r as d,p as C,q as S,s as L,n as u,W as $,h as q,l as M,x}from"./sky-index-DrcmepK6.js";const B=h("bluetooth",[["path",{d:"m7 7 10 10-5 5V2l5 5L7 17",key:"1q5490"}]]);const w=h("radio",[["path",{d:"M16.247 7.761a6 6 0 0 1 0 8.478",key:"1fwjs5"}],["path",{d:"M19.075 4.933a10 10 0 0 1 0 14.134",key:"ehdyv1"}],["path",{d:"M4.925 19.067a10 10 0 0 1 0-14.134",key:"1q22gi"}],["path",{d:"M7.753 16.239a6 6 0 0 1 0-8.478",key:"r2q7qm"}],["circle",{cx:"12",cy:"12",r:"2",key:"1c9p78"}]]),z={class:"native-app settings-app"},D={class:"app-header"},P={class:"app-search"},V=["placeholder"],W={class:"settings-list"},E=["aria-label","onClick"],F={class:"settings-section-title"},I={class:"wallpaper-picker"},N=["aria-label","onClick"],T=_({__name:"SettingsApp",setup(R){const t=f(),i=q(""),y=[{key:"wifi",icon:x,color:"#1677ff"},{key:"bluetooth",icon:B,color:"#1677ff"},{key:"airplaneMode",icon:w,color:"#ff9500"}],k=M(()=>y.filter(r=>t.t(`Apps.settings.${r.key}`).toLowerCase().includes(i.value.toLowerCase())));return(r,l)=>(n(),o("main",z,[a("header",D,[a("h1",null,c(s(t).t("Apps.settings.name")),1)]),a("div",P,[m(s(v),{size:17}),b(a("input",{"onUpdate:modelValue":l[0]||(l[0]=e=>i.value=e),placeholder:s(t).t("Apps.settings.searchPlaceholder")},null,8,V),[[A,i.value]])]),a("section",W,[(n(!0),o(p,null,d(k.value,e=>(n(),o("div",{key:e.key,class:"settings-row"},[a("span",{class:"settings-symbol",style:C({background:e.color})},[(n(),S(L(e.icon),{size:16}))],4),a("span",null,c(s(t).t(`Apps.settings.${e.key}`)),1),a("button",{class:u(["ios-switch",{active:s(t).preferences.settings[e.key]}]),type:"button","aria-label":s(t).t(`Apps.settings.toggle.${e.key}`),onClick:g=>s(t).setSetting(e.key,!s(t).preferences.settings[e.key])},[...l[1]||(l[1]=[a("span",null,null,-1)])],10,E)]))),128))]),a("h2",F,c(s(t).t("Apps.settings.wallpaper")),1),a("section",I,[(n(!0),o(p,null,d(s($),e=>(n(),o("button",{key:e,type:"button",class:u([`wallpaper--${e}`,{active:s(t).preferences.settings.wallpaper===e}]),"aria-label":s(t).t(`Apps.settings.wallpapers.${e}`),onClick:g=>s(t).setWallpaper(e)},null,10,N))),128))])]))}});export{T as default};
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -1 +1 @@
|
||||
import{d as a}from"./sky-index-DrcmepK6.js";const s=[{capturedAt:Date.now()-864e5,gradient:"linear-gradient(145deg, #ff9a62, #5f2c82 58%, #141e30)",id:"sunset-drive",titleKey:"Apps.photos.samples.sunset"},{capturedAt:Date.now()-1728e5,gradient:"linear-gradient(160deg, #67d5b5, #26648e 55%, #0b132b)",id:"ocean-air",titleKey:"Apps.photos.samples.ocean"},{capturedAt:Date.now()-2592e5,gradient:"linear-gradient(135deg, #fbc2eb, #a6c1ee 48%, #302b63)",id:"city-lights",titleKey:"Apps.photos.samples.city"},{capturedAt:Date.now()-3456e5,gradient:"linear-gradient(150deg, #f6d365, #fda085 45%, #512b58)",id:"desert-road",titleKey:"Apps.photos.samples.desert"}],r=a("media",{state:()=>({cameraFacing:"rear",cameraMode:"photo",cameraZoom:1,captures:[],claimedApps:[]}),getters:{photos:e=>[...e.captures,...s]},actions:{capture(){const e=["linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)","linear-gradient(150deg, #00c9a7, #4d8076 46%, #1f3a5f)","linear-gradient(135deg, #ffc75f, #f96d80 48%, #4b4453)"],t=this.captures.length;this.captures.unshift({capturedAt:Date.now(),gradient:e[t%e.length],id:`capture-${Date.now()}`,titleKey:"Apps.photos.samples.capture"})},claimApp(e){this.claimedApps.includes(e)||this.claimedApps.push(e)}}});export{r as u};
|
||||
import{d as a}from"./sky-index-CHPJPv4D.js";const s=[{capturedAt:Date.now()-864e5,gradient:"linear-gradient(145deg, #ff9a62, #5f2c82 58%, #141e30)",id:"sunset-drive",titleKey:"Apps.photos.samples.sunset"},{capturedAt:Date.now()-1728e5,gradient:"linear-gradient(160deg, #67d5b5, #26648e 55%, #0b132b)",id:"ocean-air",titleKey:"Apps.photos.samples.ocean"},{capturedAt:Date.now()-2592e5,gradient:"linear-gradient(135deg, #fbc2eb, #a6c1ee 48%, #302b63)",id:"city-lights",titleKey:"Apps.photos.samples.city"},{capturedAt:Date.now()-3456e5,gradient:"linear-gradient(150deg, #f6d365, #fda085 45%, #512b58)",id:"desert-road",titleKey:"Apps.photos.samples.desert"}],r=a("media",{state:()=>({cameraFacing:"rear",cameraMode:"photo",cameraZoom:1,captures:[],claimedApps:[]}),getters:{photos:e=>[...e.captures,...s]},actions:{capture(){const e=["linear-gradient(145deg, #ff6b6b, #845ec2 52%, #0f2027)","linear-gradient(150deg, #00c9a7, #4d8076 46%, #1f3a5f)","linear-gradient(135deg, #ffc75f, #f96d80 48%, #4b4453)"],t=this.captures.length;this.captures.unshift({capturedAt:Date.now(),gradient:e[t%e.length],id:`capture-${Date.now()}`,titleKey:"Apps.photos.samples.capture"})},claimApp(e){this.claimedApps.includes(e)||this.claimedApps.push(e)}}});export{r as u};
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1 @@
|
||||
import{f as e}from"./sky-index-CHPJPv4D.js";const c=e("grid-2x2",[["path",{d:"M12 3v18",key:"108xh3"}],["path",{d:"M3 12h18",key:"1i2n21"}],["rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",key:"h1oib"}]]);const h=e("user-round",[["circle",{cx:"12",cy:"8",r:"5",key:"1hypcn"}],["path",{d:"M20 21a8 8 0 0 0-16 0",key:"rfgkzh"}]]);export{c as G,h as U};
|
||||
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sky Phone</title>
|
||||
<script type="module" crossorigin src="./assets/sky-index-DrcmepK6.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/sky-index-BQ2KvS-_.css">
|
||||
<script type="module" crossorigin src="./assets/sky-index-CHPJPv4D.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/sky-index-DOLQ1Bue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||