ENH - merge dev into weather app

Integrates the current dev branch while preserving the weather-app Dynamic Island, Notes deletion flow, and simplified Phone contact header. Resolves the overlapping Sky UI migration conflicts in Notes and Phone.
This commit is contained in:
Type
2026-08-16 23:37:42 +02:00
138 changed files with 16211 additions and 6526 deletions
+12 -25
View File
@@ -1,25 +1,21 @@
<script setup lang="ts">
import {
kBlock,
kBlockTitle,
kButton,
kLink,
kList,
kListInput,
kListItem,
kNavbar,
} from 'konsta/vue'
SkyBlock as kBlock,
SkyBlockTitle as kBlockTitle,
SkyButton as kButton,
SkyField as kListInput,
SkyLink as kLink,
SkyList as kList,
SkyListItem as kListItem,
SkyNavbar as kNavbar,
} from '@/ui'
import { ChevronRight, Check, X } from 'lucide-vue-next'
import { reactive, ref } from 'vue'
import AlarmSoundMenu from '@/components/AlarmSoundMenu.vue'
import { usePhoneStore } from '@/stores/phone'
import TimeWheelPicker from '@/components/TimeWheelPicker.vue'
import {
type Alarm,
type AlarmDraft,
WEEKDAY_IDS,
} from '@/utils/alarms'
import { type Alarm, type AlarmDraft, WEEKDAY_IDS } from '@/utils/alarms'
const props = defineProps<{ alarm?: Alarm }>()
const emit = defineEmits<{
@@ -44,14 +40,6 @@ const draft = reactive<AlarmDraft>({
time: props.alarm?.time ?? '07:00',
weekdays: [...(props.alarm?.weekdays ?? [])],
})
const dangerColors = {
tonalBgIos: 'bg-red-500/15 active:bg-red-500/25',
tonalTextIos: 'text-red-500',
}
const saveLinkColors = {
navbarTextIos: 'text-[#ff9f0a]',
}
function setNote(event: Event): void {
draft.note = (event.target as HTMLInputElement).value.slice(0, 80)
}
@@ -97,7 +85,7 @@ function toggleWeekday(weekday: number): void {
<k-link
component="button"
icon-only
:colors="saveLinkColors"
class="clock-alarm-save"
:link-props="{ type: 'button' }"
:aria-label="phone.t('Common.save')"
@click="save"
@@ -168,8 +156,7 @@ function toggleWeekday(weekday: number): void {
v-if="alarm"
large
rounded
tonal
:colors="dangerColors"
variant="danger"
class="clock-alarm-delete"
@click="emit('delete')"
>
+6 -1
View File
@@ -1,5 +1,10 @@
<script setup lang="ts">
import { kList, kListItem, kNavbar, kNavbarBackLink } from 'konsta/vue'
import {
SkyList as kList,
SkyListItem as kListItem,
SkyNavbar as kNavbar,
SkyNavbarBackLink as kNavbarBackLink,
} from '@/ui'
import { Check } from 'lucide-vue-next'
import { onBeforeUnmount } from 'vue'
+3 -12
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { kBadge } from 'konsta/vue'
import { SkyBadge as kBadge } from '@/ui'
import { Minus } from 'lucide-vue-next'
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
@@ -100,10 +100,6 @@ const unreadCount = computed(() => {
if (props.app.id === 'companies') return companies.unreadCount
return 0
})
const notificationBadgeColors = {
bg: 'bg-[#ff3b30]',
text: 'text-white',
}
const calendarWeekday = computed(() =>
new Intl.DateTimeFormat(phone.lang, { weekday: 'short' })
.format(calendarToday.value)
@@ -142,11 +138,6 @@ function launch(event: MouseEvent): void {
void router.push(props.app.route)
}
const removeBadgeColors = {
bg: 'bg-[#8e8e93]',
text: 'text-black',
}
function clearHold(): void {
if (holdTimer !== undefined) window.clearTimeout(holdTimer)
holdTimer = undefined
@@ -321,7 +312,7 @@ onBeforeUnmount(() => {
v-if="unreadCount"
class="app-icon-badge"
:small="compact"
:colors="notificationBadgeColors"
tone="danger"
>
{{ unreadCount > 99 ? '99+' : unreadCount }}
</k-badge>
@@ -343,7 +334,7 @@ onBeforeUnmount(() => {
@click.stop="emit('remove')"
@pointerdown.stop
>
<k-badge class="app-icon-remove__badge" :colors="removeBadgeColors">
<k-badge class="app-icon-remove__badge" tone="neutral">
<Minus :size="15" :stroke-width="3" aria-hidden="true" />
</k-badge>
</button>
+7 -2
View File
@@ -1,5 +1,10 @@
<script setup lang="ts">
import { kBlock, kButton, kPage, kPreloader } from 'konsta/vue'
import {
SkyAppPage as kPage,
SkyBlock as kBlock,
SkyButton as kButton,
SkySpinner as kPreloader,
} from '@/ui'
import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
@@ -478,7 +483,7 @@ watch(() => catalog.openRequests[props.app.id], flushOpenRequest, {
text-align: center;
}
.custom-app-state :deep(.k-block) {
.custom-app-state :deep(.sky-block) {
margin: 0;
}
+153 -113
View File
@@ -15,6 +15,8 @@ import {
} from 'lucide-vue-next'
import { onBeforeUnmount, watch } from 'vue'
import { SkyButton, SkyIcon, SkyToolbar, SkyToolbarPane } from '@/ui'
export type MailEditorLabels = {
bold: string
bulletList: string
@@ -22,6 +24,7 @@ export type MailEditorLabels = {
numberedList: string
quote: string
redo: string
toolbar: string
undo: string
}
@@ -41,6 +44,7 @@ const props = withDefaults(
numberedList: 'Numbered list',
quote: 'Quote',
redo: 'Redo',
toolbar: 'Formatting tools',
undo: 'Undo',
}),
placeholder: '',
@@ -111,126 +115,159 @@ onBeforeUnmount(() => editor.value?.destroy())
<template>
<div class="mail-editor" :class="{ 'mail-editor--readonly': !editable }">
<div v-if="editable && editor" class="mail-editor__toolbar">
<button
type="button"
:class="{ 'is-active': editor.isActive('bold') }"
:aria-label="labels.bold"
:title="labels.bold"
@click="editor.chain().focus().toggleBold().run()"
>
<Bold :size="17" />
</button>
<button
type="button"
:class="{ 'is-active': editor.isActive('italic') }"
:aria-label="labels.italic"
:title="labels.italic"
@click="editor.chain().focus().toggleItalic().run()"
>
<Italic :size="17" />
</button>
<button
type="button"
:class="{ 'is-active': editor.isActive('bulletList') }"
:aria-label="labels.bulletList"
:title="labels.bulletList"
@click="editor.chain().focus().toggleBulletList().run()"
>
<List :size="17" />
</button>
<button
type="button"
:class="{ 'is-active': editor.isActive('orderedList') }"
:aria-label="labels.numberedList"
:title="labels.numberedList"
@click="editor.chain().focus().toggleOrderedList().run()"
>
<ListOrdered :size="17" />
</button>
<button
type="button"
:class="{ 'is-active': editor.isActive('blockquote') }"
:aria-label="labels.quote"
:title="labels.quote"
@click="editor.chain().focus().toggleBlockquote().run()"
>
<Quote :size="17" />
</button>
<span class="mail-editor__toolbar-spacer" />
<button
type="button"
:disabled="!editor.can().chain().focus().undo().run()"
:aria-label="labels.undo"
:title="labels.undo"
@click="editor.chain().focus().undo().run()"
>
<Undo2 :size="17" />
</button>
<button
type="button"
:disabled="!editor.can().chain().focus().redo().run()"
:aria-label="labels.redo"
:title="labels.redo"
@click="editor.chain().focus().redo().run()"
>
<Redo2 :size="17" />
</button>
</div>
<EditorContent v-if="editor" :editor="editor" />
<EditorContent
v-if="editor"
class="mail-editor__content"
:editor="editor"
/>
<SkyToolbar
v-if="editable && editor"
class="mail-editor__tools"
component="footer"
:aria-label="labels.toolbar"
>
<SkyToolbarPane class="mail-editor__tool-pane">
<SkyButton
class="mail-editor__tool"
:class="{
'mail-editor__tool--active': editor.isActive('bold'),
}"
variant="plain"
icon-only
:aria-label="labels.bold"
:aria-pressed="editor.isActive('bold')"
@click="editor.chain().focus().toggleBold().run()"
>
<SkyIcon :size="20"><Bold /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
:class="{
'mail-editor__tool--active': editor.isActive('italic'),
}"
variant="plain"
icon-only
:aria-label="labels.italic"
:aria-pressed="editor.isActive('italic')"
@click="editor.chain().focus().toggleItalic().run()"
>
<SkyIcon :size="20"><Italic /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
:class="{
'mail-editor__tool--active': editor.isActive('bulletList'),
}"
variant="plain"
icon-only
:aria-label="labels.bulletList"
:aria-pressed="editor.isActive('bulletList')"
@click="editor.chain().focus().toggleBulletList().run()"
>
<SkyIcon :size="20"><List /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
:class="{
'mail-editor__tool--active': editor.isActive('orderedList'),
}"
variant="plain"
icon-only
:aria-label="labels.numberedList"
:aria-pressed="editor.isActive('orderedList')"
@click="editor.chain().focus().toggleOrderedList().run()"
>
<SkyIcon :size="20"><ListOrdered /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
:class="{
'mail-editor__tool--active': editor.isActive('blockquote'),
}"
variant="plain"
icon-only
:aria-label="labels.quote"
:aria-pressed="editor.isActive('blockquote')"
@click="editor.chain().focus().toggleBlockquote().run()"
>
<SkyIcon :size="20"><Quote /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
variant="plain"
icon-only
:disabled="!editor.can().chain().focus().undo().run()"
:aria-label="labels.undo"
@click="editor.chain().focus().undo().run()"
>
<SkyIcon :size="20"><Undo2 /></SkyIcon>
</SkyButton>
<SkyButton
class="mail-editor__tool"
variant="plain"
icon-only
:disabled="!editor.can().chain().focus().redo().run()"
:aria-label="labels.redo"
@click="editor.chain().focus().redo().run()"
>
<SkyIcon :size="20"><Redo2 /></SkyIcon>
</SkyButton>
</SkyToolbarPane>
</SkyToolbar>
</div>
</template>
<style scoped>
.mail-editor {
min-height: 210px;
color: #f5f5f7;
}
.mail-editor__toolbar {
position: sticky;
z-index: 2;
top: 0;
width: 100%;
min-height: 0;
height: 100%;
display: flex;
align-items: center;
gap: 3px;
min-height: 43px;
padding: 5px 7px;
border-bottom: 1px solid #ffffff14;
background: #1c1c1ee8;
backdrop-filter: blur(18px) saturate(160%);
-webkit-backdrop-filter: blur(18px) saturate(160%);
flex-direction: column;
overflow: hidden;
background: var(--sky-bg);
color: var(--sky-text);
}
.mail-editor__toolbar button {
display: grid;
width: 32px;
height: 32px;
place-items: center;
border: 0;
border-radius: 9px;
background: transparent;
color: #f5f5f7;
cursor: pointer;
}
.mail-editor__toolbar button.is-active {
background: #0a84ff;
color: #fff;
}
.mail-editor__toolbar button:disabled {
opacity: 0.28;
cursor: default;
}
.mail-editor__toolbar-spacer {
.mail-editor__content {
min-height: 0;
flex: 1;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: none;
}
.mail-editor__content::-webkit-scrollbar {
display: none;
}
.mail-editor__tools {
padding-right: var(--sky-space-3);
padding-left: var(--sky-space-3);
}
.mail-editor__tools :deep(.sky-toolbar__inner),
.mail-editor__tool-pane {
width: 100%;
}
.mail-editor__tool {
min-width: var(--sky-touch-target);
min-height: var(--sky-touch-target);
flex: 1 1 0;
border-radius: 0;
background: transparent;
color: var(--sky-text);
}
.mail-editor__tool--active {
background: transparent;
color: var(--sky-app-accent);
}
:deep(.tiptap) {
min-height: 210px;
padding: 15px 16px 92px;
min-height: 100%;
padding: 15px 16px 124px;
outline: none;
font-size: 15px;
line-height: 1.5;
@@ -238,7 +275,14 @@ onBeforeUnmount(() => editor.value?.destroy())
}
.mail-editor--readonly {
height: auto;
display: block;
min-height: 0;
overflow: visible;
}
.mail-editor--readonly .mail-editor__content {
overflow: visible;
}
.mail-editor--readonly :deep(.tiptap) {
@@ -248,11 +292,7 @@ onBeforeUnmount(() => editor.value?.destroy())
}
:deep(.tiptap p) {
margin: 0 0 0.8em;
}
:deep(.tiptap p:last-child) {
margin-bottom: 0;
margin: 0;
}
:deep(.tiptap h2),
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { kDialog, kDialogButton } from 'konsta/vue'
import { SkyDialog as kDialog, SkyDialogButton as kDialogButton } from '@/ui'
import { useAppAuthStore, type AppAuthId } from '@/stores/app-auth'
import { useCrewLinkStore } from '@/stores/crewlink'
@@ -39,7 +39,9 @@ function confirmLogout(): void {
<template>
<k-dialog :opened="opened" @backdropclick="close">
<template #title>{{ phone.t('Common.signOutTitle', { app: appName }) }}</template>
<template #title>{{
phone.t('Common.signOutTitle', { app: appName })
}}</template>
<p>{{ phone.t('Common.signOutBody', { app: appName }) }}</p>
<template #buttons>
<k-dialog-button @click="close">
@@ -8,14 +8,14 @@ import {
UserRound,
} from 'lucide-vue-next'
import {
kButton,
kGlass,
kList,
kListInput,
kPreloader,
kSegmented,
kSegmentedButton,
} from 'konsta/vue'
SkyButton as kButton,
SkyField as kListInput,
SkyGlass as kGlass,
SkyList as kList,
SkySegmented as kSegmented,
SkySegmentedButton as kSegmentedButton,
SkySpinner as kPreloader,
} from '@/ui'
import { computed } from 'vue'
const props = withDefaults(
@@ -328,7 +328,7 @@ const canSubmit = computed(() => {
flex: 1;
gap: 6px;
}
.app-profile-auth__photo :deep(.k-button) {
.app-profile-auth__photo :deep(.sky-button) {
min-height: 32px;
justify-content: flex-start;
gap: 6px;
@@ -410,8 +410,8 @@ const canSubmit = computed(() => {
font-size: 11px;
}
.app-profile-auth__submit {
--k-button-bg-color: var(--auth-accent, var(--yellow, #ffd63e));
--k-button-text-color: #fff;
--sky-app-accent: var(--auth-accent, var(--yellow, #ffd63e));
--sky-button-text: #fff;
width: 100%;
min-height: 44px;
display: flex;
@@ -1,12 +1,12 @@
<script setup lang="ts">
import {
kButton,
kList,
kListInput,
kPreloader,
kSegmented,
kSegmentedButton,
} from 'konsta/vue'
SkyButton as kButton,
SkyField as kListInput,
SkyList as kList,
SkySegmented as kSegmented,
SkySegmentedButton as kSegmentedButton,
SkySpinner as kPreloader,
} from '@/ui'
import { computed, ref } from 'vue'
import { useAccountStore } from '@/stores/account'
@@ -36,11 +36,13 @@ const error = ref('')
const canSubmit = computed(() => {
const normalized = normalizeMailAddress(email.value)
const passwordValid = password.value.length >= 6 && password.value.length <= 64
const passwordValid =
password.value.length >= 6 && password.value.length <= 64
return Boolean(
normalized &&
passwordValid &&
(mode.value === 'login' || (confirm.value && confirm.value === password.value)),
(mode.value === 'login' ||
(confirm.value && confirm.value === password.value)),
)
})
@@ -101,7 +103,10 @@ async function submit(): Promise<void> {
<k-segmented-button :active="mode === 'login'" @click="setMode('login')">
{{ phone.t('Common.appAuth.login') }}
</k-segmented-button>
<k-segmented-button :active="mode === 'register'" @click="setMode('register')">
<k-segmented-button
:active="mode === 'register'"
@click="setMode('register')"
>
{{ phone.t('Common.appAuth.register') }}
</k-segmented-button>
</k-segmented>
@@ -144,7 +149,13 @@ async function submit(): Promise<void> {
<k-button large rounded :disabled="!canSubmit || pending" @click="submit">
<k-preloader v-if="pending" />
<template v-else>
{{ phone.t(mode === 'login' ? 'Common.appAuth.loginAction' : 'Common.appAuth.registerAction') }}
{{
phone.t(
mode === 'login'
? 'Common.appAuth.loginAction'
: 'Common.appAuth.registerAction',
)
}}
</template>
</k-button>
</section>
@@ -159,7 +170,7 @@ async function submit(): Promise<void> {
text-align: center;
}
.ifruit-app-auth > small {
color: var(--k-color-primary);
color: var(--sky-app-accent);
font-size: 10px;
font-weight: 800;
letter-spacing: 0.08em;
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { Check, ChevronDown } from 'lucide-vue-next'
import { kGlass } from 'konsta/vue'
import { SkyGlass as kGlass } from '@/ui'
import { computed, onMounted, onUnmounted, ref } from 'vue'
type SelectOption = {
@@ -8,7 +8,11 @@ import {
Share2,
UserRound,
} from 'lucide-vue-next'
import { kButton, kGlass, kIcon } from 'konsta/vue'
import {
SkyButton as kButton,
SkyGlass as kGlass,
SkyIcon as kIcon,
} from '@/ui'
import { computed, ref } from 'vue'
import { usePhoneStore } from '@/stores/phone'
@@ -263,8 +267,8 @@ function relativeTime(timestamp: number): string {
color: #438cf5;
}
.feather-follow {
--k-button-bg-color: transparent;
--k-button-text-color: var(--feather-blue, #1d9bf0);
--sky-app-accent: transparent;
--sky-button-text: var(--feather-blue, #1d9bf0);
width: auto;
min-width: 58px;
max-width: 68px;