@lincyaw/dsh 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +22 -2
- package/build/client.mjs +66 -0
- package/build/dist/client.js +90 -0
- package/build/dist/client.json +70 -0
- package/build/ensure.mjs +30 -0
- package/cordis.patch.yml +17 -0
- package/package.json +95 -3
- package/profile.patch.yml +25 -0
- package/rows/context.mjs +7 -0
- package/rows/load.mjs +8 -0
- package/rows/policy.mjs +7 -0
- package/rows/routes.mjs +7 -0
- package/rows/service.mjs +7 -0
- package/rows/skills.mjs +7 -0
- package/rows/tools.mjs +7 -0
- package/rows/turns.mjs +7 -0
- package/rows/workbench.mjs +9 -0
- package/rows/workspaces.mjs +7 -0
- package/skills/presmium-author/SKILL.md +95 -0
- package/skills/presmium-author/references/assistance.md +27 -0
- package/skills/presmium-author/references/code.md +52 -0
- package/skills/presmium-author/references/directives.md +592 -0
- package/skills/presmium-author/references/publishing.md +60 -0
- package/skills/presmium-author/references/writing.md +72 -0
- package/skills/presmium-present/SKILL.md +69 -0
- package/skills/presmium-present/references/classroom.md +46 -0
- package/skills/presmium-present/references/delivery.md +63 -0
- package/skills/presmium-present/references/lecture.md +58 -0
- package/src/client/Frame.tsx +314 -0
- package/src/client/brand.tsx +90 -0
- package/src/client/chrome.tsx +79 -0
- package/src/client/contract.ts +35 -0
- package/src/client/dsh.d.ts +129 -0
- package/src/client/fallback.tsx +49 -0
- package/src/client/frame.css +345 -0
- package/src/client/index.tsx +154 -0
- package/src/client/layout.ts +217 -0
- package/src/client/messages.ts +78 -0
- package/src/client/source.tsx +81 -0
- package/src/client/styles.css +3 -0
- package/src/client/theme.ts +149 -0
- package/src/node/context.ts +55 -0
- package/src/node/dsh.ts +93 -0
- package/src/node/launch.ts +59 -0
- package/src/node/policy.ts +59 -0
- package/src/node/routes.ts +46 -0
- package/src/node/service.ts +280 -0
- package/src/node/skills.ts +46 -0
- package/src/node/tools.ts +43 -0
- package/src/node/turns/index.ts +165 -0
- package/src/node/watchdog.mjs +36 -0
- package/src/node/workbench.ts +43 -0
- package/src/node/workspaces.ts +10 -0
- package/web.patch.yml +190 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { PmButton, WorkbenchProvider } from '@lincyaw/workbench'
|
|
2
|
+
import type { Workbench } from '@lincyaw/workbench'
|
|
3
|
+
import { useEffect, useSyncExternalStore } from 'react'
|
|
4
|
+
import { Modal } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
5
|
+
import { BRAND_LANGUAGES, BRAND_SLOTS, ONBOARDING } from './contract.ts'
|
|
6
|
+
import { BRAND_COPY, FRAME_MESSAGES } from './messages.ts'
|
|
7
|
+
import type { FrameMessages } from './messages.ts'
|
|
8
|
+
|
|
9
|
+
export function Logo({ size = 24, className }: { size?: number, className?: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<svg className={className ? `pmw-logo ${className}` : 'pmw-logo'} width={size} height={size} viewBox="0 0 32 32" role="img" aria-label="Presmium">
|
|
12
|
+
<rect width="32" height="32" rx="7" fill="#0F5F5C" />
|
|
13
|
+
<path d="M11 8h7.5a6.5 6.5 0 0 1 0 13H15v4h-4zM15 12v5h3.5a2.5 2.5 0 0 0 0-5z" fill="#F7F5F0" fillRule="evenodd" />
|
|
14
|
+
</svg>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function useFrameMessages(workbench: Workbench): FrameMessages {
|
|
19
|
+
const locale = useSyncExternalStore(workbench.subscribe, () => workbench.getState().locale)
|
|
20
|
+
return FRAME_MESSAGES[locale]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const WELCOMED = 'presmium.workbench.welcomed'
|
|
24
|
+
|
|
25
|
+
function welcomed(): boolean {
|
|
26
|
+
try {
|
|
27
|
+
return localStorage.getItem(WELCOMED) === '1'
|
|
28
|
+
} catch {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Welcome({ workbench, complete, openSection }: { workbench: Workbench, complete?: () => void, openSection?: (id: string) => void }) {
|
|
34
|
+
const t = useFrameMessages(workbench)
|
|
35
|
+
const read = welcomed()
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (read) complete?.()
|
|
38
|
+
}, [read, complete])
|
|
39
|
+
if (read) return null
|
|
40
|
+
const done = () => {
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(WELCOMED, '1')
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
complete?.()
|
|
46
|
+
}
|
|
47
|
+
return (
|
|
48
|
+
<WorkbenchProvider workbench={workbench} protectChanges={false}>
|
|
49
|
+
<Modal open title={t.welcomeTitle} onClose={done} headless>
|
|
50
|
+
<div className="pmw-welcome__card pm-ui" data-pmw-welcome="">
|
|
51
|
+
<Logo size={40} />
|
|
52
|
+
<h2 id="pmw-welcome-title" className="pmw-welcome__title">{t.welcomeTitle}</h2>
|
|
53
|
+
<p>{t.welcome}</p>
|
|
54
|
+
<div className="pmw-welcome__actions">
|
|
55
|
+
<PmButton
|
|
56
|
+
onClick={() => {
|
|
57
|
+
done()
|
|
58
|
+
openSection?.('models')
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{t.models}
|
|
62
|
+
</PmButton>
|
|
63
|
+
<PmButton variant="primary" onClick={done}>{t.start}</PmButton>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</Modal>
|
|
67
|
+
</WorkbenchProvider>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function PassOver({ complete }: { complete?: () => void }) {
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
complete?.()
|
|
74
|
+
}, [complete])
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function brand(ctx: DshClientContext, workbench: Workbench): void {
|
|
79
|
+
const [mark, name, hero] = BRAND_SLOTS
|
|
80
|
+
ctx.effect(() => ctx.slots.inject(mark, () => ctx.slots.register({ name: mark }, ({ size }: { size?: number }) => <Logo size={size ?? 24} />)), 'presmium: sidebar brand mark')
|
|
81
|
+
ctx.effect(() => ctx.slots.inject(name, () => ctx.slots.register({ name }, () => <span className="pmw-brand-name">Presmium</span>)), 'presmium: sidebar brand name')
|
|
82
|
+
ctx.effect(() => ctx.slots.inject(hero, () => ctx.slots.register({ name: hero }, ({ size, className }: { size?: number, className?: string }) => <Logo size={size ?? 34} className={className} />)), 'presmium: conversation brand mark')
|
|
83
|
+
const [welcome, signIn] = ONBOARDING.steps
|
|
84
|
+
ctx.effect(() => ctx.slots.inject(ONBOARDING.slot, () => ctx.slots.register({ name: ONBOARDING.slot, id: welcome, priority: -1, order: -100 }, props => <Welcome workbench={workbench} {...props} />)), 'presmium: first-use notice')
|
|
85
|
+
ctx.effect(() => ctx.slots.inject(ONBOARDING.slot, () => ctx.slots.register({ name: ONBOARDING.slot, id: signIn, priority: -1, order: 0 }, PassOver)), 'presmium: no DeepSeek sign-in step')
|
|
86
|
+
for (const language of BRAND_LANGUAGES) {
|
|
87
|
+
ctx.effect(() => ctx.locale.addLanguage({ id: language.id, label: language.label, fallback: language.fallback }), `presmium: language ${language.id}`)
|
|
88
|
+
for (const [ns, dict] of Object.entries(BRAND_COPY[language.id])) ctx.effect(() => ctx.locale.register(ns, language.id, dict), `presmium: ${ns} copy ${language.id}`)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { PmIconButton } from '@lincyaw/workbench'
|
|
2
|
+
import type { Workbench } from '@lincyaw/workbench'
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
4
|
+
import { useFrameMessages } from './brand.tsx'
|
|
5
|
+
import type { LayoutActions, LayoutState } from './layout.ts'
|
|
6
|
+
import { NARROW, SIDE_COLLAPSE } from './layout.ts'
|
|
7
|
+
|
|
8
|
+
interface SidebarProps {
|
|
9
|
+
renderSlot(name: string, props?: Record<string, unknown>, options?: Record<string, unknown>): ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Replace navigation chrome through public slots; Harness still owns the session browser and dialogs. */
|
|
13
|
+
export function chrome(ctx: DshClientContext, workbench: Workbench, actions: LayoutActions, currentLayout: () => LayoutState, startSession: () => Promise<void>): DshObservable<readonly DshSlotEntry[]> {
|
|
14
|
+
let panels: DshSlotEntry[] = []
|
|
15
|
+
const listeners = new Set<() => void>()
|
|
16
|
+
const panelStore = {
|
|
17
|
+
getSnapshot: () => panels,
|
|
18
|
+
subscribe(listener: () => void) { listeners.add(listener); return () => { listeners.delete(listener) } },
|
|
19
|
+
}
|
|
20
|
+
// An already running host retains its old plugin roster until it is restarted.
|
|
21
|
+
if (!window.__DSH_BOOT__?.entries.some(entry => entry.id === '@deepseek-ai/dsh-client-ui-sidebar')) ctx.effect(() => {
|
|
22
|
+
const offPanels = ctx.slots.subscribe('sidebar.panellist', () => {
|
|
23
|
+
panels = ctx.slots.entries('sidebar.panellist')
|
|
24
|
+
for (const listener of listeners) listener()
|
|
25
|
+
})
|
|
26
|
+
ctx.slots.inject('sidebar.panellist', () => {
|
|
27
|
+
panels = ctx.slots.entries('sidebar.panellist')
|
|
28
|
+
for (const listener of listeners) listener()
|
|
29
|
+
})
|
|
30
|
+
const closeHistory = () => {
|
|
31
|
+
if (currentLayout().layoutInfo.viewportWidth < SIDE_COLLAPSE) actions.revealConversation()
|
|
32
|
+
else actions.toggleSidebar()
|
|
33
|
+
}
|
|
34
|
+
function Sidebar({ renderSlot }: SidebarProps) {
|
|
35
|
+
const t = useFrameMessages(workbench)
|
|
36
|
+
return <section className="pmw-history" aria-label={t.conversationHistory}>
|
|
37
|
+
<header className="pmw-history__header"><h2>{t.conversationHistory}</h2><PmIconButton icon="close" small label={t.closeHistory} onClick={closeHistory} /></header>
|
|
38
|
+
<div className="pmw-history__browser">{renderSlot('sidebar.workspaces', { wide: true, expandSidebar: () => actions.choose('sessions') })}</div>
|
|
39
|
+
<div hidden>{renderSlot('sidebar.settings', { wide: false })}</div>
|
|
40
|
+
</section>
|
|
41
|
+
}
|
|
42
|
+
let offSidebar: (() => void) | undefined
|
|
43
|
+
ctx.slots.inject('sidebar', () => {
|
|
44
|
+
offSidebar = ctx.slots.register({ name: 'sidebar', priority: -1, children: {
|
|
45
|
+
'sidebar.brand.mark': { kind: 'single', scope: 'root' },
|
|
46
|
+
'sidebar.brand.name': { kind: 'single', scope: 'root' },
|
|
47
|
+
'sidebar.toggle.badge': { kind: 'single', scope: 'root' },
|
|
48
|
+
'sidebar.workspaces': { kind: 'single', scope: 'root' },
|
|
49
|
+
'sidebar.settings': { kind: 'single', scope: 'root' },
|
|
50
|
+
'sidebar.footer.action': { kind: 'list', scope: 'root' },
|
|
51
|
+
} }, Sidebar)
|
|
52
|
+
})
|
|
53
|
+
return () => { offSidebar?.(); offPanels() }
|
|
54
|
+
}, 'presmium: one navigation shell')
|
|
55
|
+
ctx.effect(() => {
|
|
56
|
+
function ConversationActions() {
|
|
57
|
+
const t = useFrameMessages(workbench)
|
|
58
|
+
return <div className="pmw-chat-actions pm-ui" role="group" aria-label={t.conversation}>
|
|
59
|
+
<PmIconButton icon="plus" label={t.newConversation} small onClick={() => void startSession()} />
|
|
60
|
+
<PmIconButton icon="history" label={t.conversationHistory} small onClick={() => actions.showHistory()} />
|
|
61
|
+
<PmIconButton icon="close" label={t.hideConversation} small onClick={() => {
|
|
62
|
+
if (currentLayout().layoutInfo.viewportWidth < NARROW) actions.show(workbench.state.document === undefined ? 'documents' : 'preview')
|
|
63
|
+
else actions.setDockOpen(false)
|
|
64
|
+
}} />
|
|
65
|
+
</div>
|
|
66
|
+
}
|
|
67
|
+
let off: (() => void) | undefined
|
|
68
|
+
ctx.slots.inject('conversation.header.leading', () => { off = ctx.slots.register({ name: 'conversation.header.leading', priority: -1 }, ConversationActions) })
|
|
69
|
+
return () => off?.()
|
|
70
|
+
}, 'presmium: conversation actions in its resident header')
|
|
71
|
+
ctx.effect(() => {
|
|
72
|
+
let off: (() => void) | undefined
|
|
73
|
+
ctx.slots.inject('conversation.session.header.corner', () => {
|
|
74
|
+
off = ctx.slots.register({ name: 'conversation.session.header.corner', priority: -1 }, () => null)
|
|
75
|
+
})
|
|
76
|
+
return () => off?.()
|
|
77
|
+
}, 'presmium: remove the duplicate sidebar opener')
|
|
78
|
+
return panelStore
|
|
79
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const CLIENT_ID = '@lincyaw/dsh'
|
|
2
|
+
|
|
3
|
+
export const ROOT_CHILDREN = {
|
|
4
|
+
'sidebar': { kind: 'single', scope: 'root' },
|
|
5
|
+
'main': { kind: 'keyed', scope: 'root' },
|
|
6
|
+
'rightbar': { kind: 'single', scope: 'root' },
|
|
7
|
+
'shell.bottom': { kind: 'single', scope: 'root' },
|
|
8
|
+
'shell.overlay': { kind: 'list', scope: 'root' },
|
|
9
|
+
'shell.leading': { kind: 'single', scope: 'root' },
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
export const LAYOUT_MEMBERS = ['panelInfo', 'selectPanel', 'beginNavigation', 'toggleSidebar', 'openRightbar', 'closeRightbar'] as const
|
|
13
|
+
|
|
14
|
+
export const BRAND_SLOTS = ['sidebar.brand.mark', 'sidebar.brand.name', 'conversation.hero.brand.mark'] as const
|
|
15
|
+
export const ONBOARDING = { slot: 'settings.onboarding', steps: ['welcome-notice', 'deepseek-official'] } as const
|
|
16
|
+
|
|
17
|
+
export const SHELL_MODULES = [
|
|
18
|
+
'react',
|
|
19
|
+
'react/jsx-runtime',
|
|
20
|
+
'react-dom',
|
|
21
|
+
'react-dom/client',
|
|
22
|
+
'@deepseek-ai/cordis',
|
|
23
|
+
'zod',
|
|
24
|
+
'@tanstack/react-virtual',
|
|
25
|
+
'@deepseek-ai/dsh-client-store',
|
|
26
|
+
'@deepseek-ai/dsh-client-ui-slots',
|
|
27
|
+
'@deepseek-ai/dsh-client-ui-primitives',
|
|
28
|
+
'@deepseek-ai/dsh-client-ui-dockkit',
|
|
29
|
+
] as const
|
|
30
|
+
|
|
31
|
+
export const EXPERIMENTAL_GROUP = '[data-plugin-group="extensions"]'
|
|
32
|
+
|
|
33
|
+
export const UI_LAYOUT = '@deepseek-ai/dsh-client-ui-layout'
|
|
34
|
+
|
|
35
|
+
export const BRAND_LANGUAGES = [{ id: 'zh-CN', label: '简体中文(Presmium)', fallback: 'zh' }, { id: 'en-US', label: 'English (Presmium)', fallback: 'en' }] as const
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
declare module '@deepseek-ai/dsh-client-ui-primitives' {
|
|
2
|
+
export const Modal: import('react').ComponentType<{
|
|
3
|
+
open: boolean
|
|
4
|
+
title: string
|
|
5
|
+
onClose(): void
|
|
6
|
+
headless: true
|
|
7
|
+
children?: import('react').ReactNode
|
|
8
|
+
}>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module '@deepseek-ai/dsh-client-store' {
|
|
12
|
+
export interface StoreInstance<S, A> {
|
|
13
|
+
actions: A
|
|
14
|
+
getSnapshot(): S
|
|
15
|
+
subscribe(listener: () => void): () => void
|
|
16
|
+
}
|
|
17
|
+
export interface StoreHandle<S, A> {
|
|
18
|
+
create(): StoreInstance<S, A>
|
|
19
|
+
}
|
|
20
|
+
type Bound<A> = { [K in keyof A]: A[K] extends (draft: any, ...args: infer P) => void ? (...args: P) => void : never }
|
|
21
|
+
export function defineStore<S, A extends Record<string, (draft: S, ...args: any[]) => void>>(spec: { init: () => S, actions: A }): StoreHandle<S, Bound<A>>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface DshObservable<T> {
|
|
25
|
+
getSnapshot(): T
|
|
26
|
+
subscribe(listener: () => void): () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type DshSelectorHook<T> = <U>(select: (value: T) => U) => U
|
|
30
|
+
|
|
31
|
+
interface DshSlotOptions {
|
|
32
|
+
name: string
|
|
33
|
+
key?: string
|
|
34
|
+
id?: string
|
|
35
|
+
priority?: number
|
|
36
|
+
order?: number
|
|
37
|
+
locale?: string
|
|
38
|
+
label?: string | (() => string)
|
|
39
|
+
children?: Record<string, { kind: 'single' | 'keyed' | 'list', scope: 'root' }>
|
|
40
|
+
store?: unknown
|
|
41
|
+
inject?: unknown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface DshSlotEntry {
|
|
45
|
+
options: { key?: string, id?: string, label?: string | (() => string) }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface DshSlots {
|
|
49
|
+
register(options: DshSlotOptions, component: (props: any) => unknown): () => void
|
|
50
|
+
inject(name: string, effect: () => unknown): void
|
|
51
|
+
entries(name: string): DshSlotEntry[]
|
|
52
|
+
subscribe(name: string, listener: () => void): () => void
|
|
53
|
+
provideRoot(options: { hooks: Record<string, DshObservable<unknown>> }): () => void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface DshThemeSnapshot {
|
|
57
|
+
preference: string
|
|
58
|
+
fontSizes: { text: number, code: number, terminal: number }
|
|
59
|
+
fontFamilies: { text: string, code: string, terminal: string }
|
|
60
|
+
active: { id: string, colorScheme: 'light' | 'dark', tokens: Record<string, string> }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface DshTheme {
|
|
64
|
+
getTheme(): DshThemeSnapshot
|
|
65
|
+
overrideTokens(source: string, tokens: Record<string, { light: string, dark: string }>): () => void
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface DshLocale {
|
|
69
|
+
getLocale(): { active: string, locales: readonly { id: string }[] }
|
|
70
|
+
subscribe(listener: () => void): () => void
|
|
71
|
+
addLanguage(language: { id: string, label: string, fallback?: string }): () => void
|
|
72
|
+
register(ns: string, locale: string | Record<string, Record<string, string>>, dict?: Record<string, string>): () => void
|
|
73
|
+
bind(ns: string): (key: string, values?: Record<string, string>) => string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DshShortcuts {
|
|
77
|
+
register(command: {
|
|
78
|
+
id: string
|
|
79
|
+
label: () => string
|
|
80
|
+
aliases?: string[]
|
|
81
|
+
defaults: Record<string, { code: string, modifiers: string[] }>
|
|
82
|
+
regions?: string[]
|
|
83
|
+
modals?: string[]
|
|
84
|
+
resolve: () => { status: 'handled', run: () => void }
|
|
85
|
+
}): () => void
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface DshSidebarRightTabs {
|
|
89
|
+
register(definition: {
|
|
90
|
+
id: string
|
|
91
|
+
kind: string
|
|
92
|
+
priority?: 'extension' | 'builtin' | 'fallback'
|
|
93
|
+
keepMounted?: boolean
|
|
94
|
+
patterns?: readonly string[]
|
|
95
|
+
canOpen?(address: string): boolean
|
|
96
|
+
title: (address?: string) => string
|
|
97
|
+
guide?: { id: string, order?: number, title: () => string, description?: () => string }[]
|
|
98
|
+
}): () => void
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface DshSourceTabProps {
|
|
102
|
+
useTabInfo(): { tab: {
|
|
103
|
+
contentId: string
|
|
104
|
+
visible: boolean
|
|
105
|
+
navigation: { revision: number, params?: { line?: unknown } }
|
|
106
|
+
actions: { close(): void }
|
|
107
|
+
} }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface DshClientContext {
|
|
111
|
+
effect(effect: () => unknown, label?: string): void
|
|
112
|
+
on(event: string, listener: (...args: any[]) => void): () => void
|
|
113
|
+
inject(names: string[], apply: (ctx: DshClientContext) => void): void
|
|
114
|
+
reflect: { provide(name: string, value: unknown): () => void }
|
|
115
|
+
slots: DshSlots
|
|
116
|
+
theme: DshTheme
|
|
117
|
+
locale: DshLocale
|
|
118
|
+
shortcuts: DshShortcuts
|
|
119
|
+
sidebarRightTabs: DshSidebarRightTabs
|
|
120
|
+
sidebarRight: { active(): { id: string, kind: string, contentId: string } | undefined, close(id: string): void, isExpanded(): boolean, toggleExpanded(): void }
|
|
121
|
+
layout: { selectPanel(id: string | null): void }
|
|
122
|
+
sessions: { list: DshObservable<{ byId: Record<string, { id: string, cwd?: string, retainedBy: { mainView?: number } }> }> }
|
|
123
|
+
uiWorkspace: { openWorkspace(id: string, beforeOpen?: (sessionId: string) => void): Promise<void>, startSession(id?: string): void }
|
|
124
|
+
workspaces: { list: DshObservable<{ items: readonly { workspaceId: string, path: string }[] }> }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface Window {
|
|
128
|
+
__DSH_BOOT__?: { entries: { id: string }[], release?: { version: string, commit?: string } }
|
|
129
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ChangesPanel, DocumentView, FilesPanel, HomePanel, PreviewPanel, useWorkbench, WorkbenchProvider } from '@lincyaw/workbench'
|
|
2
|
+
import type { Workbench } from '@lincyaw/workbench'
|
|
3
|
+
import type { ComponentType } from 'react'
|
|
4
|
+
import { Logo } from './brand.tsx'
|
|
5
|
+
import { CLIENT_ID } from './contract.ts'
|
|
6
|
+
import { FRAME_MESSAGES } from './messages.ts'
|
|
7
|
+
|
|
8
|
+
export const FALLBACK_PANEL = 'presmium'
|
|
9
|
+
|
|
10
|
+
function Centre() {
|
|
11
|
+
const document_ = useWorkbench(state => state.document)
|
|
12
|
+
return (
|
|
13
|
+
<div className="pmw-fallback pm-ui" data-pmw-fallback="">
|
|
14
|
+
{document_ === undefined
|
|
15
|
+
? <HomePanel />
|
|
16
|
+
: (
|
|
17
|
+
<div className="pmw-fallback__doc">
|
|
18
|
+
<FilesPanel />
|
|
19
|
+
<DocumentView />
|
|
20
|
+
</div>
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function fallback(ctx: DshClientContext, workbench: Workbench): void {
|
|
27
|
+
const t = () => FRAME_MESSAGES[workbench.state.locale]
|
|
28
|
+
const provided = (Component: ComponentType, className?: string, protectChanges = false) => function PresmiumPanel() {
|
|
29
|
+
return (
|
|
30
|
+
<WorkbenchProvider workbench={workbench} protectChanges={protectChanges}>
|
|
31
|
+
{className ? <div className={className}><Component /></div> : <Component />}
|
|
32
|
+
</WorkbenchProvider>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
ctx.effect(() => ctx.slots.inject('sidebar.panellist', () => ctx.slots.register({ name: 'sidebar.panellist', id: FALLBACK_PANEL, order: -10, label: () => t().product }, () => <Logo size={20} />)), 'presmium: the panel list entry')
|
|
36
|
+
ctx.effect(() => ctx.slots.register({ name: 'main', key: FALLBACK_PANEL }, provided(Centre, undefined, true)), 'presmium: the workbench as a global panel')
|
|
37
|
+
ctx.inject(['sidebarRightTabs'], (inner) => {
|
|
38
|
+
const tabs = [
|
|
39
|
+
{ kind: 'presmium-preview', Panel: PreviewPanel, title: () => `Presmium · ${t().preview}` },
|
|
40
|
+
{ kind: 'presmium-changes', Panel: ChangesPanel, title: () => `Presmium · ${t().changes}` },
|
|
41
|
+
]
|
|
42
|
+
for (const { kind, Panel, title } of tabs) {
|
|
43
|
+
const id = `${CLIENT_ID}/${kind}`
|
|
44
|
+
const Body = provided(Panel, 'pmw-fallback__tab pm-ui')
|
|
45
|
+
inner.effect(() => inner.sidebarRightTabs.register({ id, kind, priority: 'extension', keepMounted: true, title, guide: [{ id: kind, order: 50, title }] }), `presmium: ${kind} tab type`)
|
|
46
|
+
inner.effect(() => inner.slots.inject('sidebar.right.pane.tab', () => inner.slots.register({ name: 'sidebar.right.pane.tab', key: id }, Body)), `presmium: ${kind} tab body`)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|