From 9384f5399da39e53855beb8e7f8ded1aa56bf72e Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 27 Dec 2022 14:36:33 +0900 Subject: rename: client -> frontend --- packages/frontend/src/ui/_common_/common.vue | 139 ++++++ .../frontend/src/ui/_common_/navbar-for-mobile.vue | 314 +++++++++++++ packages/frontend/src/ui/_common_/navbar.vue | 521 +++++++++++++++++++++ .../src/ui/_common_/statusbar-federation.vue | 108 +++++ .../frontend/src/ui/_common_/statusbar-rss.vue | 93 ++++ .../src/ui/_common_/statusbar-user-list.vue | 113 +++++ packages/frontend/src/ui/_common_/statusbars.vue | 92 ++++ .../frontend/src/ui/_common_/stream-indicator.vue | 61 +++ packages/frontend/src/ui/_common_/sw-inject.ts | 35 ++ packages/frontend/src/ui/_common_/upload.vue | 129 +++++ packages/frontend/src/ui/classic.header.vue | 217 +++++++++ packages/frontend/src/ui/classic.sidebar.vue | 268 +++++++++++ packages/frontend/src/ui/classic.vue | 320 +++++++++++++ packages/frontend/src/ui/classic.widgets.vue | 84 ++++ packages/frontend/src/ui/deck.vue | 435 +++++++++++++++++ packages/frontend/src/ui/deck/antenna-column.vue | 70 +++ packages/frontend/src/ui/deck/column-core.vue | 34 ++ packages/frontend/src/ui/deck/column.vue | 398 ++++++++++++++++ packages/frontend/src/ui/deck/deck-store.ts | 296 ++++++++++++ packages/frontend/src/ui/deck/direct-column.vue | 31 ++ packages/frontend/src/ui/deck/list-column.vue | 58 +++ packages/frontend/src/ui/deck/main-column.vue | 68 +++ packages/frontend/src/ui/deck/mentions-column.vue | 28 ++ .../frontend/src/ui/deck/notifications-column.vue | 44 ++ packages/frontend/src/ui/deck/tl-column.vue | 119 +++++ packages/frontend/src/ui/deck/widgets-column.vue | 69 +++ packages/frontend/src/ui/universal.vue | 390 +++++++++++++++ packages/frontend/src/ui/universal.widgets.vue | 71 +++ packages/frontend/src/ui/visitor.vue | 19 + packages/frontend/src/ui/visitor/a.vue | 259 ++++++++++ packages/frontend/src/ui/visitor/b.vue | 248 ++++++++++ packages/frontend/src/ui/visitor/header.vue | 228 +++++++++ packages/frontend/src/ui/visitor/kanban.vue | 257 ++++++++++ packages/frontend/src/ui/zen.vue | 34 ++ 34 files changed, 5650 insertions(+) create mode 100644 packages/frontend/src/ui/_common_/common.vue create mode 100644 packages/frontend/src/ui/_common_/navbar-for-mobile.vue create mode 100644 packages/frontend/src/ui/_common_/navbar.vue create mode 100644 packages/frontend/src/ui/_common_/statusbar-federation.vue create mode 100644 packages/frontend/src/ui/_common_/statusbar-rss.vue create mode 100644 packages/frontend/src/ui/_common_/statusbar-user-list.vue create mode 100644 packages/frontend/src/ui/_common_/statusbars.vue create mode 100644 packages/frontend/src/ui/_common_/stream-indicator.vue create mode 100644 packages/frontend/src/ui/_common_/sw-inject.ts create mode 100644 packages/frontend/src/ui/_common_/upload.vue create mode 100644 packages/frontend/src/ui/classic.header.vue create mode 100644 packages/frontend/src/ui/classic.sidebar.vue create mode 100644 packages/frontend/src/ui/classic.vue create mode 100644 packages/frontend/src/ui/classic.widgets.vue create mode 100644 packages/frontend/src/ui/deck.vue create mode 100644 packages/frontend/src/ui/deck/antenna-column.vue create mode 100644 packages/frontend/src/ui/deck/column-core.vue create mode 100644 packages/frontend/src/ui/deck/column.vue create mode 100644 packages/frontend/src/ui/deck/deck-store.ts create mode 100644 packages/frontend/src/ui/deck/direct-column.vue create mode 100644 packages/frontend/src/ui/deck/list-column.vue create mode 100644 packages/frontend/src/ui/deck/main-column.vue create mode 100644 packages/frontend/src/ui/deck/mentions-column.vue create mode 100644 packages/frontend/src/ui/deck/notifications-column.vue create mode 100644 packages/frontend/src/ui/deck/tl-column.vue create mode 100644 packages/frontend/src/ui/deck/widgets-column.vue create mode 100644 packages/frontend/src/ui/universal.vue create mode 100644 packages/frontend/src/ui/universal.widgets.vue create mode 100644 packages/frontend/src/ui/visitor.vue create mode 100644 packages/frontend/src/ui/visitor/a.vue create mode 100644 packages/frontend/src/ui/visitor/b.vue create mode 100644 packages/frontend/src/ui/visitor/header.vue create mode 100644 packages/frontend/src/ui/visitor/kanban.vue create mode 100644 packages/frontend/src/ui/zen.vue (limited to 'packages/frontend/src/ui') diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue new file mode 100644 index 0000000000..7f3fc0e4af --- /dev/null +++ b/packages/frontend/src/ui/_common_/common.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue new file mode 100644 index 0000000000..50b28de063 --- /dev/null +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue new file mode 100644 index 0000000000..b82da15f13 --- /dev/null +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -0,0 +1,521 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue new file mode 100644 index 0000000000..24fc4f6f6d --- /dev/null +++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue new file mode 100644 index 0000000000..e7f88e4984 --- /dev/null +++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue new file mode 100644 index 0000000000..f4d989c387 --- /dev/null +++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue new file mode 100644 index 0000000000..114ca5be8c --- /dev/null +++ b/packages/frontend/src/ui/_common_/statusbars.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue new file mode 100644 index 0000000000..a855de8ab9 --- /dev/null +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts new file mode 100644 index 0000000000..8676d2d48d --- /dev/null +++ b/packages/frontend/src/ui/_common_/sw-inject.ts @@ -0,0 +1,35 @@ +import { inject } from 'vue'; +import { post } from '@/os'; +import { $i, login } from '@/account'; +import { defaultStore } from '@/store'; +import { getAccountFromId } from '@/scripts/get-account-from-id'; +import { mainRouter } from '@/router'; + +export function swInject() { + navigator.serviceWorker.addEventListener('message', ev => { + if (_DEV_) { + console.log('sw msg', ev.data); + } + + if (ev.data.type !== 'order') return; + + if (ev.data.loginId !== $i?.id) { + return getAccountFromId(ev.data.loginId).then(account => { + if (!account) return; + return login(account.token, ev.data.url); + }); + } + + switch (ev.data.order) { + case 'post': + return post(ev.data.options); + case 'push': + if (mainRouter.currentRoute.value.path === ev.data.url) { + return window.scroll({ top: 0, behavior: 'smooth' }); + } + return mainRouter.push(ev.data.url); + default: + return; + } + }); +} diff --git a/packages/frontend/src/ui/_common_/upload.vue b/packages/frontend/src/ui/_common_/upload.vue new file mode 100644 index 0000000000..70882bd251 --- /dev/null +++ b/packages/frontend/src/ui/_common_/upload.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue new file mode 100644 index 0000000000..46d79e6355 --- /dev/null +++ b/packages/frontend/src/ui/classic.header.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue new file mode 100644 index 0000000000..dac09ea703 --- /dev/null +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue new file mode 100644 index 0000000000..0e726c11ed --- /dev/null +++ b/packages/frontend/src/ui/classic.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/packages/frontend/src/ui/classic.widgets.vue b/packages/frontend/src/ui/classic.widgets.vue new file mode 100644 index 0000000000..163ec982ce --- /dev/null +++ b/packages/frontend/src/ui/classic.widgets.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue new file mode 100644 index 0000000000..f3415cfd09 --- /dev/null +++ b/packages/frontend/src/ui/deck.vue @@ -0,0 +1,435 @@ + + + + + diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue new file mode 100644 index 0000000000..ba14530662 --- /dev/null +++ b/packages/frontend/src/ui/deck/antenna-column.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/packages/frontend/src/ui/deck/column-core.vue b/packages/frontend/src/ui/deck/column-core.vue new file mode 100644 index 0000000000..30c0dc5e1c --- /dev/null +++ b/packages/frontend/src/ui/deck/column-core.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue new file mode 100644 index 0000000000..2a99b621e6 --- /dev/null +++ b/packages/frontend/src/ui/deck/column.vue @@ -0,0 +1,398 @@ + + + + + diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts new file mode 100644 index 0000000000..56db7398e5 --- /dev/null +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -0,0 +1,296 @@ +import { throttle } from 'throttle-debounce'; +import { markRaw } from 'vue'; +import { notificationTypes } from 'misskey-js'; +import { Storage } from '../../pizzax'; +import { i18n } from '@/i18n'; +import { api } from '@/os'; +import { deepClone } from '@/scripts/clone'; + +type ColumnWidget = { + name: string; + id: string; + data: Record; +}; + +export type Column = { + id: string; + type: 'main' | 'widgets' | 'notifications' | 'tl' | 'antenna' | 'list' | 'mentions' | 'direct'; + name: string | null; + width: number; + widgets?: ColumnWidget[]; + active?: boolean; + flexible?: boolean; + antennaId?: string; + listId?: string; + includingTypes?: typeof notificationTypes[number][]; + tl?: 'home' | 'local' | 'social' | 'global'; +}; + +export const deckStore = markRaw(new Storage('deck', { + profile: { + where: 'deviceAccount', + default: 'default', + }, + columns: { + where: 'deviceAccount', + default: [] as Column[], + }, + layout: { + where: 'deviceAccount', + default: [] as Column['id'][][], + }, + columnAlign: { + where: 'deviceAccount', + default: 'left' as 'left' | 'right' | 'center', + }, + alwaysShowMainColumn: { + where: 'deviceAccount', + default: true, + }, + navWindow: { + where: 'deviceAccount', + default: true, + }, +})); + +export const loadDeck = async () => { + let deck; + + try { + deck = await api('i/registry/get', { + scope: ['client', 'deck', 'profiles'], + key: deckStore.state.profile, + }); + } catch (err) { + if (err.code === 'NO_SUCH_KEY') { + // 後方互換性のため + if (deckStore.state.profile === 'default') { + saveDeck(); + return; + } + + deckStore.set('columns', []); + deckStore.set('layout', []); + return; + } + throw err; + } + + deckStore.set('columns', deck.columns); + deckStore.set('layout', deck.layout); +}; + +// TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する +export const saveDeck = throttle(1000, () => { + api('i/registry/set', { + scope: ['client', 'deck', 'profiles'], + key: deckStore.state.profile, + value: { + columns: deckStore.reactiveState.columns.value, + layout: deckStore.reactiveState.layout.value, + }, + }); +}); + +export async function getProfiles(): Promise { + return await api('i/registry/keys', { + scope: ['client', 'deck', 'profiles'], + }); +} + +export async function deleteProfile(key: string): Promise { + return await api('i/registry/remove', { + scope: ['client', 'deck', 'profiles'], + key: key, + }); +} + +export function addColumn(column: Column) { + if (column.name === undefined) column.name = null; + deckStore.push('columns', column); + deckStore.push('layout', [column.id]); + saveDeck(); +} + +export function removeColumn(id: Column['id']) { + deckStore.set('columns', deckStore.state.columns.filter(c => c.id !== id)); + deckStore.set('layout', deckStore.state.layout + .map(ids => ids.filter(_id => _id !== id)) + .filter(ids => ids.length > 0)); + saveDeck(); +} + +export function swapColumn(a: Column['id'], b: Column['id']) { + const aX = deckStore.state.layout.findIndex(ids => ids.indexOf(a) !== -1); + const aY = deckStore.state.layout[aX].findIndex(id => id === a); + const bX = deckStore.state.layout.findIndex(ids => ids.indexOf(b) !== -1); + const bY = deckStore.state.layout[bX].findIndex(id => id === b); + const layout = deepClone(deckStore.state.layout); + layout[aX][aY] = b; + layout[bX][bY] = a; + deckStore.set('layout', layout); + saveDeck(); +} + +export function swapLeftColumn(id: Column['id']) { + const layout = deepClone(deckStore.state.layout); + deckStore.state.layout.some((ids, i) => { + if (ids.includes(id)) { + const left = deckStore.state.layout[i - 1]; + if (left) { + layout[i - 1] = deckStore.state.layout[i]; + layout[i] = left; + deckStore.set('layout', layout); + } + return true; + } + }); + saveDeck(); +} + +export function swapRightColumn(id: Column['id']) { + const layout = deepClone(deckStore.state.layout); + deckStore.state.layout.some((ids, i) => { + if (ids.includes(id)) { + const right = deckStore.state.layout[i + 1]; + if (right) { + layout[i + 1] = deckStore.state.layout[i]; + layout[i] = right; + deckStore.set('layout', layout); + } + return true; + } + }); + saveDeck(); +} + +export function swapUpColumn(id: Column['id']) { + const layout = deepClone(deckStore.state.layout); + const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); + const ids = deepClone(deckStore.state.layout[idsIndex]); + ids.some((x, i) => { + if (x === id) { + const up = ids[i - 1]; + if (up) { + ids[i - 1] = id; + ids[i] = up; + + layout[idsIndex] = ids; + deckStore.set('layout', layout); + } + return true; + } + }); + saveDeck(); +} + +export function swapDownColumn(id: Column['id']) { + const layout = deepClone(deckStore.state.layout); + const idsIndex = deckStore.state.layout.findIndex(ids => ids.includes(id)); + const ids = deepClone(deckStore.state.layout[idsIndex]); + ids.some((x, i) => { + if (x === id) { + const down = ids[i + 1]; + if (down) { + ids[i + 1] = id; + ids[i] = down; + + layout[idsIndex] = ids; + deckStore.set('layout', layout); + } + return true; + } + }); + saveDeck(); +} + +export function stackLeftColumn(id: Column['id']) { + let layout = deepClone(deckStore.state.layout); + const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); + layout = layout.map(ids => ids.filter(_id => _id !== id)); + layout[i - 1].push(id); + layout = layout.filter(ids => ids.length > 0); + deckStore.set('layout', layout); + saveDeck(); +} + +export function popRightColumn(id: Column['id']) { + let layout = deepClone(deckStore.state.layout); + const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); + const affected = layout[i]; + layout = layout.map(ids => ids.filter(_id => _id !== id)); + layout.splice(i + 1, 0, [id]); + layout = layout.filter(ids => ids.length > 0); + deckStore.set('layout', layout); + + const columns = deepClone(deckStore.state.columns); + for (const column of columns) { + if (affected.includes(column.id)) { + column.active = true; + } + } + deckStore.set('columns', columns); + + saveDeck(); +} + +export function addColumnWidget(id: Column['id'], widget: ColumnWidget) { + const columns = deepClone(deckStore.state.columns); + const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); + const column = deepClone(deckStore.state.columns[columnIndex]); + if (column == null) return; + if (column.widgets == null) column.widgets = []; + column.widgets.unshift(widget); + columns[columnIndex] = column; + deckStore.set('columns', columns); + saveDeck(); +} + +export function removeColumnWidget(id: Column['id'], widget: ColumnWidget) { + const columns = deepClone(deckStore.state.columns); + const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); + const column = deepClone(deckStore.state.columns[columnIndex]); + if (column == null) return; + column.widgets = column.widgets.filter(w => w.id !== widget.id); + columns[columnIndex] = column; + deckStore.set('columns', columns); + saveDeck(); +} + +export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { + const columns = deepClone(deckStore.state.columns); + const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); + const column = deepClone(deckStore.state.columns[columnIndex]); + if (column == null) return; + column.widgets = widgets; + columns[columnIndex] = column; + deckStore.set('columns', columns); + saveDeck(); +} + +export function updateColumnWidget(id: Column['id'], widgetId: string, widgetData: any) { + const columns = deepClone(deckStore.state.columns); + const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); + const column = deepClone(deckStore.state.columns[columnIndex]); + if (column == null) return; + column.widgets = column.widgets.map(w => w.id === widgetId ? { + ...w, + data: widgetData, + } : w); + columns[columnIndex] = column; + deckStore.set('columns', columns); + saveDeck(); +} + +export function updateColumn(id: Column['id'], column: Partial) { + const columns = deepClone(deckStore.state.columns); + const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); + const currentColumn = deepClone(deckStore.state.columns[columnIndex]); + if (currentColumn == null) return; + for (const [k, v] of Object.entries(column)) { + currentColumn[k] = v; + } + columns[columnIndex] = currentColumn; + deckStore.set('columns', columns); + saveDeck(); +} diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue new file mode 100644 index 0000000000..75b018cacd --- /dev/null +++ b/packages/frontend/src/ui/deck/direct-column.vue @@ -0,0 +1,31 @@ + + + diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue new file mode 100644 index 0000000000..d9f3f7b4e7 --- /dev/null +++ b/packages/frontend/src/ui/deck/list-column.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue new file mode 100644 index 0000000000..0c66172397 --- /dev/null +++ b/packages/frontend/src/ui/deck/main-column.vue @@ -0,0 +1,68 @@ + + + diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue new file mode 100644 index 0000000000..16962956a0 --- /dev/null +++ b/packages/frontend/src/ui/deck/mentions-column.vue @@ -0,0 +1,28 @@ + + + diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue new file mode 100644 index 0000000000..9d133035fe --- /dev/null +++ b/packages/frontend/src/ui/deck/notifications-column.vue @@ -0,0 +1,44 @@ + + + diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue new file mode 100644 index 0000000000..49b29145ff --- /dev/null +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue new file mode 100644 index 0000000000..fc61d18ff6 --- /dev/null +++ b/packages/frontend/src/ui/deck/widgets-column.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue new file mode 100644 index 0000000000..b91bf476e8 --- /dev/null +++ b/packages/frontend/src/ui/universal.vue @@ -0,0 +1,390 @@ + + + + + + + diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue new file mode 100644 index 0000000000..33fb492836 --- /dev/null +++ b/packages/frontend/src/ui/universal.widgets.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue new file mode 100644 index 0000000000..ec9150d346 --- /dev/null +++ b/packages/frontend/src/ui/visitor.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/frontend/src/ui/visitor/a.vue b/packages/frontend/src/ui/visitor/a.vue new file mode 100644 index 0000000000..f8db7a9d09 --- /dev/null +++ b/packages/frontend/src/ui/visitor/a.vue @@ -0,0 +1,259 @@ + + + + + + + diff --git a/packages/frontend/src/ui/visitor/b.vue b/packages/frontend/src/ui/visitor/b.vue new file mode 100644 index 0000000000..275008a8f8 --- /dev/null +++ b/packages/frontend/src/ui/visitor/b.vue @@ -0,0 +1,248 @@ + + + + + + + diff --git a/packages/frontend/src/ui/visitor/header.vue b/packages/frontend/src/ui/visitor/header.vue new file mode 100644 index 0000000000..7300b12a75 --- /dev/null +++ b/packages/frontend/src/ui/visitor/header.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/packages/frontend/src/ui/visitor/kanban.vue b/packages/frontend/src/ui/visitor/kanban.vue new file mode 100644 index 0000000000..51e47f277d --- /dev/null +++ b/packages/frontend/src/ui/visitor/kanban.vue @@ -0,0 +1,257 @@ + + + + + + diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue new file mode 100644 index 0000000000..84c96a1dae --- /dev/null +++ b/packages/frontend/src/ui/zen.vue @@ -0,0 +1,34 @@ + + + + + -- cgit v1.2.3-freya