mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 16:23:22 +00:00
FIX - preserve empty homescreen slots
This commit is contained in:
@@ -1017,6 +1017,9 @@ button {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 28px 15px;
|
||||
}
|
||||
.app-grid-slot {
|
||||
min-height: 73px;
|
||||
}
|
||||
.app-icon-item {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
@@ -1172,6 +1175,10 @@ button {
|
||||
backdrop-filter: blur(24px) saturate(150%);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(150%);
|
||||
}
|
||||
.app-dock-slot {
|
||||
min-width: 0;
|
||||
height: 56px;
|
||||
}
|
||||
.springboard-edit-done {
|
||||
position: absolute;
|
||||
z-index: 12;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
createDefaultHomeLayout,
|
||||
HOME_GRID_PAGE_SIZE,
|
||||
moveHomeApp,
|
||||
parseHomeLayout,
|
||||
removeHomeApp,
|
||||
@@ -17,59 +18,105 @@ const defaults = createDefaultHomeLayout(
|
||||
)
|
||||
|
||||
describe('home layout', () => {
|
||||
it('uses the registry arrangement for devices without a saved layout', () => {
|
||||
expect(parseHomeLayout(undefined, defaults, [...installed])).toEqual(
|
||||
defaults,
|
||||
)
|
||||
})
|
||||
it('uses fixed grid and dock slots for the registry arrangement', () => {
|
||||
const layout = parseHomeLayout(undefined, defaults, [...installed])
|
||||
|
||||
it('validates persisted ids and appends newly installed apps', () => {
|
||||
expect(
|
||||
parseHomeLayout(
|
||||
{
|
||||
dock: ['messages', 'invalid', 'messages'],
|
||||
grid: ['mail'],
|
||||
hidden: ['phone'],
|
||||
},
|
||||
defaults,
|
||||
[...installed],
|
||||
),
|
||||
).toEqual({
|
||||
dock: ['messages'],
|
||||
grid: ['mail', 'clock', 'notes'],
|
||||
hidden: ['phone'],
|
||||
})
|
||||
})
|
||||
|
||||
it('reorders grid apps and swaps a full dock slot back into the grid', () => {
|
||||
const layout: HomeLayout = {
|
||||
dock: ['phone', 'messages', 'clock', 'notes'],
|
||||
grid: ['phone', 'messages', 'mail', 'clock', 'notes'],
|
||||
hidden: [],
|
||||
}
|
||||
|
||||
const reordered = moveHomeApp(layout, 'mail', 'grid', 'grid', 0)
|
||||
expect(reordered.grid).toEqual([
|
||||
'mail',
|
||||
expect(layout.grid).toHaveLength(HOME_GRID_PAGE_SIZE)
|
||||
expect(layout.grid.slice(0, 6)).toEqual([
|
||||
'phone',
|
||||
'messages',
|
||||
'mail',
|
||||
'clock',
|
||||
'notes',
|
||||
null,
|
||||
])
|
||||
expect(layout.dock).toEqual(['phone', 'messages', 'clock', null])
|
||||
expect(layout.version).toBe(2)
|
||||
})
|
||||
|
||||
it('migrates compact persisted arrays and appends newly installed apps', () => {
|
||||
const layout = parseHomeLayout(
|
||||
{
|
||||
dock: ['messages', 'invalid', 'messages'],
|
||||
grid: ['mail'],
|
||||
hidden: ['phone'],
|
||||
},
|
||||
defaults,
|
||||
[...installed],
|
||||
)
|
||||
|
||||
expect(layout.dock).toEqual(['messages', null, null, null])
|
||||
expect(layout.grid.slice(0, 4)).toEqual(['mail', 'clock', 'notes', null])
|
||||
expect(layout.hidden).toEqual(['phone'])
|
||||
expect(layout.version).toBe(2)
|
||||
})
|
||||
|
||||
it('preserves explicit gaps in versioned layouts', () => {
|
||||
const grid: HomeLayout['grid'] = Array.from(
|
||||
{ length: HOME_GRID_PAGE_SIZE },
|
||||
() => null,
|
||||
)
|
||||
grid[0] = 'phone'
|
||||
grid[7] = 'mail'
|
||||
|
||||
const layout = parseHomeLayout(
|
||||
{
|
||||
dock: ['messages', null, 'clock', null],
|
||||
grid,
|
||||
hidden: ['notes'],
|
||||
version: 2,
|
||||
},
|
||||
defaults,
|
||||
[...installed],
|
||||
)
|
||||
|
||||
expect(layout.grid[0]).toBe('phone')
|
||||
expect(layout.grid[1]).toBeNull()
|
||||
expect(layout.grid[7]).toBe('mail')
|
||||
expect(layout.dock).toEqual(['messages', null, 'clock', null])
|
||||
})
|
||||
|
||||
it('moves to an exact empty slot without compacting other apps', () => {
|
||||
const moved = moveHomeApp(defaults, 'mail', 'grid', 'grid', 12)
|
||||
|
||||
expect(moved.grid[2]).toBeNull()
|
||||
expect(moved.grid[12]).toBe('mail')
|
||||
expect(moved.grid[0]).toBe('phone')
|
||||
expect(moved.grid[4]).toBe('notes')
|
||||
})
|
||||
|
||||
it('swaps occupied slots and avoids duplicate displaced shortcuts', () => {
|
||||
const reordered = moveHomeApp(defaults, 'mail', 'grid', 'grid', 0)
|
||||
expect(reordered.grid.slice(0, 5)).toEqual([
|
||||
'mail',
|
||||
'messages',
|
||||
'phone',
|
||||
'clock',
|
||||
'notes',
|
||||
])
|
||||
|
||||
const docked = moveHomeApp(reordered, 'mail', 'grid', 'dock', 2)
|
||||
expect(docked.dock).toEqual(['phone', 'messages', 'mail', 'notes'])
|
||||
expect(docked.grid).toEqual(['phone', 'messages', 'clock', 'notes'])
|
||||
expect(docked.dock).toEqual(['phone', 'messages', 'mail', null])
|
||||
expect(docked.grid[0]).toBeNull()
|
||||
expect(docked.grid.filter((id) => id === 'clock')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('removes every home shortcut and can restore it from the library', () => {
|
||||
const removed = removeHomeApp(defaults, 'phone')
|
||||
expect(removed.grid).not.toContain('phone')
|
||||
expect(removed.dock).not.toContain('phone')
|
||||
it('removes shortcuts without closing gaps and restores the first gap', () => {
|
||||
const layout: HomeLayout = moveHomeApp(
|
||||
defaults,
|
||||
'phone',
|
||||
'grid',
|
||||
'grid',
|
||||
10,
|
||||
)
|
||||
const removed = removeHomeApp(layout, 'phone')
|
||||
expect(removed.grid[0]).toBeNull()
|
||||
expect(removed.grid[10]).toBeNull()
|
||||
expect(removed.dock[0]).toBeNull()
|
||||
expect(removed.hidden).toContain('phone')
|
||||
|
||||
const restored = restoreHomeApp(removed, 'phone')
|
||||
expect(restored.grid.at(-1)).toBe('phone')
|
||||
expect(restored.grid[0]).toBe('phone')
|
||||
expect(restored.hidden).not.toContain('phone')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import type { LaunchablePhoneAppId } from '@/types/apps'
|
||||
|
||||
export const HOME_DOCK_CAPACITY = 4
|
||||
export const HOME_GRID_PAGE_SIZE = 20
|
||||
const MAX_HOME_GRID_PAGES = 5
|
||||
|
||||
export type HomeArea = 'dock' | 'grid'
|
||||
export type HomeSlot = LaunchablePhoneAppId | null
|
||||
|
||||
export type HomeLayout = {
|
||||
dock: LaunchablePhoneAppId[]
|
||||
grid: LaunchablePhoneAppId[]
|
||||
dock: HomeSlot[]
|
||||
grid: HomeSlot[]
|
||||
hidden: LaunchablePhoneAppId[]
|
||||
version: 2
|
||||
}
|
||||
|
||||
function readAppIds(
|
||||
value: unknown,
|
||||
availableIds: Set<LaunchablePhoneAppId>,
|
||||
maximum = Number.POSITIVE_INFINITY,
|
||||
): LaunchablePhoneAppId[] {
|
||||
if (!Array.isArray(value)) return []
|
||||
|
||||
@@ -26,24 +29,79 @@ function readAppIds(
|
||||
) {
|
||||
ids.push(valueId as LaunchablePhoneAppId)
|
||||
}
|
||||
if (ids.length === maximum) break
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
function createSlots(length: number): HomeSlot[] {
|
||||
return Array.from({ length }, () => null)
|
||||
}
|
||||
|
||||
function getGridCapacity(itemCount: number): number {
|
||||
return Math.max(
|
||||
HOME_GRID_PAGE_SIZE,
|
||||
Math.ceil(itemCount / HOME_GRID_PAGE_SIZE) * HOME_GRID_PAGE_SIZE,
|
||||
)
|
||||
}
|
||||
|
||||
function readSlots(
|
||||
value: unknown,
|
||||
availableIds: Set<LaunchablePhoneAppId>,
|
||||
length: number,
|
||||
): HomeSlot[] {
|
||||
const slots = createSlots(length)
|
||||
if (!Array.isArray(value)) return slots
|
||||
|
||||
const usedIds = new Set<LaunchablePhoneAppId>()
|
||||
for (let index = 0; index < Math.min(value.length, length); index += 1) {
|
||||
const valueId = value[index]
|
||||
if (
|
||||
typeof valueId === 'string' &&
|
||||
availableIds.has(valueId as LaunchablePhoneAppId) &&
|
||||
!usedIds.has(valueId as LaunchablePhoneAppId)
|
||||
) {
|
||||
slots[index] = valueId as LaunchablePhoneAppId
|
||||
usedIds.add(valueId as LaunchablePhoneAppId)
|
||||
}
|
||||
}
|
||||
return slots
|
||||
}
|
||||
|
||||
function placeInFirstEmptySlot(
|
||||
slots: HomeSlot[],
|
||||
appId: LaunchablePhoneAppId,
|
||||
): void {
|
||||
const emptyIndex = slots.indexOf(null)
|
||||
if (emptyIndex !== -1) {
|
||||
slots[emptyIndex] = appId
|
||||
return
|
||||
}
|
||||
|
||||
slots.push(...createSlots(HOME_GRID_PAGE_SIZE))
|
||||
slots[slots.length - HOME_GRID_PAGE_SIZE] = appId
|
||||
}
|
||||
|
||||
export function createDefaultHomeLayout(
|
||||
installedIds: LaunchablePhoneAppId[],
|
||||
defaultGridIds: LaunchablePhoneAppId[],
|
||||
defaultDockIds: LaunchablePhoneAppId[],
|
||||
): HomeLayout {
|
||||
const installed = new Set(installedIds)
|
||||
return {
|
||||
dock: defaultDockIds
|
||||
.filter((id) => installed.has(id))
|
||||
.slice(0, HOME_DOCK_CAPACITY),
|
||||
grid: defaultGridIds.filter((id) => installed.has(id)),
|
||||
hidden: [],
|
||||
const gridIds = defaultGridIds.filter((id) => installed.has(id))
|
||||
const grid = createSlots(getGridCapacity(gridIds.length))
|
||||
for (let index = 0; index < gridIds.length; index += 1) {
|
||||
grid[index] = gridIds[index]
|
||||
}
|
||||
|
||||
const dock = createSlots(HOME_DOCK_CAPACITY)
|
||||
for (const [index, id] of defaultDockIds
|
||||
.filter((id) => installed.has(id))
|
||||
.slice(0, HOME_DOCK_CAPACITY)
|
||||
.entries()) {
|
||||
dock[index] = id
|
||||
}
|
||||
|
||||
return { dock, grid, hidden: [], version: 2 }
|
||||
}
|
||||
|
||||
export function parseHomeLayout(
|
||||
@@ -57,28 +115,48 @@ export function parseHomeLayout(
|
||||
const availableIds = new Set(installedIds)
|
||||
const hidden = readAppIds(source.hidden, availableIds)
|
||||
const hiddenIds = new Set(hidden)
|
||||
const dock = readAppIds(source.dock, availableIds, HOME_DOCK_CAPACITY).filter(
|
||||
(id) => !hiddenIds.has(id),
|
||||
const persistedGridLength = Array.isArray(source.grid)
|
||||
? Math.min(source.grid.length, HOME_GRID_PAGE_SIZE * MAX_HOME_GRID_PAGES)
|
||||
: 0
|
||||
const gridLength = Math.max(
|
||||
defaults.grid.length,
|
||||
getGridCapacity(persistedGridLength),
|
||||
)
|
||||
const grid = readAppIds(source.grid, availableIds).filter(
|
||||
(id) => !hiddenIds.has(id),
|
||||
let grid: HomeSlot[]
|
||||
let dock: HomeSlot[]
|
||||
|
||||
if (source.version === 2) {
|
||||
grid = readSlots(source.grid, availableIds, gridLength)
|
||||
dock = readSlots(source.dock, availableIds, HOME_DOCK_CAPACITY)
|
||||
} else {
|
||||
grid = createSlots(gridLength)
|
||||
for (const id of readAppIds(source.grid, availableIds)) {
|
||||
placeInFirstEmptySlot(grid, id)
|
||||
}
|
||||
dock = createSlots(HOME_DOCK_CAPACITY)
|
||||
for (const [index, id] of readAppIds(source.dock, availableIds)
|
||||
.slice(0, HOME_DOCK_CAPACITY)
|
||||
.entries()) {
|
||||
dock[index] = id
|
||||
}
|
||||
}
|
||||
|
||||
grid = grid.map((id) => (id && !hiddenIds.has(id) ? id : null))
|
||||
dock = dock.map((id) => (id && !hiddenIds.has(id) ? id : null))
|
||||
const placedIds = new Set(
|
||||
[...grid, ...dock, ...hidden].filter(
|
||||
(id): id is LaunchablePhoneAppId => id !== null,
|
||||
),
|
||||
)
|
||||
const placedIds = new Set([...dock, ...grid, ...hidden])
|
||||
|
||||
for (const id of defaults.grid) {
|
||||
if (!placedIds.has(id)) {
|
||||
grid.push(id)
|
||||
placedIds.add(id)
|
||||
}
|
||||
}
|
||||
for (const id of defaults.dock) {
|
||||
if (!placedIds.has(id) && dock.length < HOME_DOCK_CAPACITY) {
|
||||
dock.push(id)
|
||||
if (id && !placedIds.has(id)) {
|
||||
placeInFirstEmptySlot(grid, id)
|
||||
placedIds.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
return { dock, grid, hidden }
|
||||
return { dock, grid, hidden, version: 2 }
|
||||
}
|
||||
|
||||
export function removeHomeApp(
|
||||
@@ -86,11 +164,12 @@ export function removeHomeApp(
|
||||
appId: LaunchablePhoneAppId,
|
||||
): HomeLayout {
|
||||
return {
|
||||
dock: layout.dock.filter((id) => id !== appId),
|
||||
grid: layout.grid.filter((id) => id !== appId),
|
||||
dock: layout.dock.map((id) => (id === appId ? null : id)),
|
||||
grid: layout.grid.map((id) => (id === appId ? null : id)),
|
||||
hidden: layout.hidden.includes(appId)
|
||||
? [...layout.hidden]
|
||||
: [...layout.hidden, appId],
|
||||
version: 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,10 +178,13 @@ export function restoreHomeApp(
|
||||
appId: LaunchablePhoneAppId,
|
||||
): HomeLayout {
|
||||
if (layout.grid.includes(appId) || layout.dock.includes(appId)) return layout
|
||||
const grid = [...layout.grid]
|
||||
placeInFirstEmptySlot(grid, appId)
|
||||
return {
|
||||
dock: [...layout.dock],
|
||||
grid: [...layout.grid, appId],
|
||||
grid,
|
||||
hidden: layout.hidden.filter((id) => id !== appId),
|
||||
version: 2,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,30 +199,34 @@ export function moveHomeApp(
|
||||
dock: [...layout.dock],
|
||||
grid: [...layout.grid],
|
||||
hidden: layout.hidden.filter((id) => id !== appId),
|
||||
version: 2,
|
||||
}
|
||||
const source = next[from]
|
||||
const sourceIndex = source.indexOf(appId)
|
||||
if (sourceIndex === -1) return layout
|
||||
|
||||
source.splice(sourceIndex, 1)
|
||||
const target = next[to]
|
||||
const duplicateIndex = target.indexOf(appId)
|
||||
if (duplicateIndex !== -1) target.splice(duplicateIndex, 1)
|
||||
if (sourceIndex === -1 || targetIndex < 0 || targetIndex >= target.length) {
|
||||
return layout
|
||||
}
|
||||
|
||||
const insertionIndex = Math.max(0, Math.min(targetIndex, target.length))
|
||||
if (to === 'dock' && target.length >= HOME_DOCK_CAPACITY) {
|
||||
const displacedIndex = Math.min(insertionIndex, target.length - 1)
|
||||
const [displacedApp] = target.splice(displacedIndex, 1, appId)
|
||||
if (
|
||||
from === 'grid' &&
|
||||
displacedApp !== undefined &&
|
||||
!source.includes(displacedApp)
|
||||
) {
|
||||
source.splice(Math.min(sourceIndex, source.length), 0, displacedApp)
|
||||
}
|
||||
if (from === to) {
|
||||
const displacedApp = source[targetIndex]
|
||||
source[targetIndex] = appId
|
||||
source[sourceIndex] = displacedApp
|
||||
return next
|
||||
}
|
||||
|
||||
target.splice(insertionIndex, 0, appId)
|
||||
const duplicateIndex = target.indexOf(appId)
|
||||
if (duplicateIndex !== -1) {
|
||||
target[duplicateIndex] = null
|
||||
}
|
||||
|
||||
const displacedApp = target[targetIndex]
|
||||
source[sourceIndex] =
|
||||
displacedApp &&
|
||||
displacedApp !== appId &&
|
||||
!source.some((id, index) => id === displacedApp && index !== sourceIndex)
|
||||
? displacedApp
|
||||
: null
|
||||
target[targetIndex] = appId
|
||||
return next
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@ import { useAppStoreStore } from '@/stores/app-store'
|
||||
import { usePhoneStore } from '@/stores/phone'
|
||||
import type { PhoneAppCategory, PhoneAppDefinition } from '@/types/apps'
|
||||
import type { LaunchablePhoneAppId } from '@/types/apps'
|
||||
import type { HomeArea } from '@/utils/homeLayout'
|
||||
import { HOME_GRID_PAGE_SIZE, type HomeArea } from '@/utils/homeLayout'
|
||||
import { paginateItems } from '@/utils/pages'
|
||||
|
||||
const APPS_PER_HOME_PAGE = 20
|
||||
const APP_LIBRARY_CATEGORIES: PhoneAppCategory[] = [
|
||||
'games',
|
||||
'productivity',
|
||||
@@ -44,25 +43,23 @@ const installedApps = computed(() =>
|
||||
const installedAppsById = computed(
|
||||
() => new Map(installedApps.value.map((app) => [app.id, app])),
|
||||
)
|
||||
const gridApps = computed(() =>
|
||||
appStore.homeLayout.grid.flatMap((id) => {
|
||||
const app = installedAppsById.value.get(id)
|
||||
return app ? [app] : []
|
||||
}),
|
||||
const gridSlots = computed(() =>
|
||||
appStore.homeLayout.grid.map((id) =>
|
||||
id ? (installedAppsById.value.get(id) ?? null) : null,
|
||||
),
|
||||
)
|
||||
const appPages = computed(() =>
|
||||
paginateItems(gridApps.value, APPS_PER_HOME_PAGE),
|
||||
paginateItems(gridSlots.value, HOME_GRID_PAGE_SIZE),
|
||||
)
|
||||
const pageCount = computed(() => appPages.value.length + 2)
|
||||
const libraryPage = computed(() => pageCount.value - 1)
|
||||
const isAppPage = computed(
|
||||
() => phone.currentPage > 0 && phone.currentPage < libraryPage.value,
|
||||
)
|
||||
const dockApps = computed(() =>
|
||||
appStore.homeLayout.dock.flatMap((id) => {
|
||||
const app = installedAppsById.value.get(id)
|
||||
return app ? [app] : []
|
||||
}),
|
||||
const dockSlots = computed(() =>
|
||||
appStore.homeLayout.dock.map((id) =>
|
||||
id ? (installedAppsById.value.get(id) ?? null) : null,
|
||||
),
|
||||
)
|
||||
const filteredApps = computed(() => {
|
||||
const query = searchQuery.value.trim().toLocaleLowerCase(phone.lang)
|
||||
@@ -240,19 +237,33 @@ watch(isAppPage, (visible) => {
|
||||
:aria-label="phone.t('Home.apps')"
|
||||
>
|
||||
<div class="app-grid" data-home-area="grid">
|
||||
<AppIcon
|
||||
<template
|
||||
v-for="(app, appIndex) in apps"
|
||||
:key="app.id"
|
||||
:app="app"
|
||||
data-home-area="grid"
|
||||
:data-home-index="pageIndex * APPS_PER_HOME_PAGE + appIndex"
|
||||
:edit-mode="editMode"
|
||||
@dragcancel="stopHomeDrag"
|
||||
@dragend="finishHomeDrag"
|
||||
@dragstart="startHomeDrag(app.id, 'grid')"
|
||||
@edit="enterEditMode"
|
||||
@remove="removeHomeApp(app.id)"
|
||||
/>
|
||||
:key="
|
||||
app?.id ??
|
||||
`grid-empty-${pageIndex * HOME_GRID_PAGE_SIZE + appIndex}`
|
||||
"
|
||||
>
|
||||
<AppIcon
|
||||
v-if="app"
|
||||
:app="app"
|
||||
data-home-area="grid"
|
||||
:data-home-index="pageIndex * HOME_GRID_PAGE_SIZE + appIndex"
|
||||
:edit-mode="editMode"
|
||||
@dragcancel="stopHomeDrag"
|
||||
@dragend="finishHomeDrag"
|
||||
@dragstart="startHomeDrag(app.id, 'grid')"
|
||||
@edit="enterEditMode"
|
||||
@remove="removeHomeApp(app.id)"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="app-grid-slot"
|
||||
data-home-area="grid"
|
||||
:data-home-index="pageIndex * HOME_GRID_PAGE_SIZE + appIndex"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -368,20 +379,31 @@ watch(isAppPage, (visible) => {
|
||||
:aria-label="phone.t('Home.dock')"
|
||||
data-home-area="dock"
|
||||
>
|
||||
<AppIcon
|
||||
v-for="(app, appIndex) in dockApps"
|
||||
:key="app.id"
|
||||
:app="app"
|
||||
data-home-area="dock"
|
||||
:data-home-index="appIndex"
|
||||
:edit-mode="editMode"
|
||||
:show-label="false"
|
||||
@dragcancel="stopHomeDrag"
|
||||
@dragend="finishHomeDrag"
|
||||
@dragstart="startHomeDrag(app.id, 'dock')"
|
||||
@edit="enterEditMode"
|
||||
@remove="removeHomeApp(app.id)"
|
||||
/>
|
||||
<template
|
||||
v-for="(app, appIndex) in dockSlots"
|
||||
:key="app?.id ?? `dock-empty-${appIndex}`"
|
||||
>
|
||||
<AppIcon
|
||||
v-if="app"
|
||||
:app="app"
|
||||
data-home-area="dock"
|
||||
:data-home-index="appIndex"
|
||||
:edit-mode="editMode"
|
||||
:show-label="false"
|
||||
@dragcancel="stopHomeDrag"
|
||||
@dragend="finishHomeDrag"
|
||||
@dragstart="startHomeDrag(app.id, 'dock')"
|
||||
@edit="enterEditMode"
|
||||
@remove="removeHomeApp(app.id)"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="app-dock-slot"
|
||||
data-home-area="dock"
|
||||
:data-home-index="appIndex"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
</template>
|
||||
</nav>
|
||||
</Transition>
|
||||
|
||||
|
||||
@@ -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-CTY27YKv.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/sky-index-D9oyMrlO.css">
|
||||
<script type="module" crossorigin src="./assets/sky-index-Bh38VieM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/sky-index-DXf7PPOf.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Reference in New Issue
Block a user