@jaxzhou/dsh-file-explorer 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 +21 -0
- package/README.md +241 -0
- package/README.zh.md +212 -0
- package/cordis.patch.yml +16 -0
- package/lib/client.js +1073 -0
- package/lib/client.js.map +7 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +7 -0
- package/package.json +98 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client/index.ts", "../src/client/FilesView.tsx", "../src/client/format.ts", "../src/client/face.ts", "../src/client/locales.ts", "../src/client/store.ts", "../src/client/styles.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half: register `files` as one Conversation View.\n *\n * The public path, unmodified: the view into the `conversation.view` list slot\n * that `@deepseek-ai/dsh-client-ui-conversation` owns and renders as the tab\n * strip beside Chat and Trajectory. The registration carries the exclusive\n * per-Session store, the injected Remote face, and the locale namespace; the\n * slot service's effect wrapper means plugin unload removes the tab, its\n * dictionary, and its stylesheet, and disposal of the declaration removes the\n * contribution again if the Conversation shell is remounted.\n *\n * The file split is this package's layering: what the explorer keeps\n * (`store.ts`), how it lists and reads (`face.ts`), what it draws\n * (`FilesView.tsx`), what it looks like (`styles.ts`), what it says\n * (`locales.ts`), and this module, which only wires them together.\n */\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\n// Type-only: pulls the Client Remote face (`ctx.remote`).\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\n// Type-only: pulls the locale service's Context merge (`ctx.locale`).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: the 'conversation.view' SlotMap row and the owner props the view\n// component derives must be in the program for the register call to type.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\n// Type-only: pulls the slot service's Context merge (`ctx.slots`).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-session/client'\nimport { FilesView } from './FilesView.tsx'\nimport { filesFace } from './face.ts'\nimport type { FilesInjected } from './face.ts'\nimport { en, NS, zh } from './locales.ts'\nimport { createFilesStore } from './store.ts'\nimport { installStyles } from './styles.ts'\n\nexport { previewLines, parseJsonDocument } from './FilesView.tsx'\nexport { extensionOf, hasSourceToggle, previewFormatFor } from './format.ts'\nexport type { PreviewFormat, PreviewFormatKind } from './format.ts'\nexport type { FileExplorerKey } from './locales.ts'\nexport type {\n DirLevel, FilesState, LevelState, PreviewContent, PreviewImage, PreviewMode, PreviewState,\n PreviewText,\n} from './store.ts'\nexport type { FilesInjected, WorkspaceFilesRemote } from './face.ts'\nexport type { FilesViewProps } from './FilesView.tsx'\n\n/** This plugin's identity inside the browser plugin tree. */\nexport const name = 'file-explorer'\n\n/**\n * Required browser services: the slot registry, copy, and the Remote carrier\n * with the workspace-files namespace the explorer reads through.\n */\nexport const inject = ['slots', 'locale', 'remote', 'remote.workspaceFiles']\n\n/**\n * The view's id inside `conversation.view`. It is the tab's identity, the key\n * a stored View preference names, and the entry id the header selects by.\n */\nexport const FILES_VIEW_ID = 'files'\n\n/**\n * Client plugin body: register the stylesheet, the dictionaries, and the view.\n * @param ctx - client root context carrying the slot registry, copy, and Remote face.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(installStyles, '@jaxzhou/dsh-file-explorer: stylesheet')\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), '@jaxzhou/dsh-file-explorer: dictionaries')\n // Registration-time text (the view tab label) reads through the bound\n // translate as a thunk, so it follows the active locale without\n // re-registration.\n const t = ctx.locale.bind(NS)\n const store = createFilesStore()\n const lifetime = new AbortController()\n ctx.effect(() => () => { lifetime.abort() }, '@jaxzhou/dsh-file-explorer: request lifetime')\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register({\n name: 'conversation.view',\n id: FILES_VIEW_ID,\n // After Chat (default) and Trajectory (10), so the strip reads\n // Chat | Trajectory | Files.\n order: 20,\n locale: NS,\n label: () => t('view.files'),\n store,\n inject: (\n sessionId: SessionId,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n ): FilesInjected => filesFace(ctx.remote, sessionId, lifetime.signal, actions),\n }, FilesView))\n}\n", "/**\n * The Files Conversation View: the session workspace on the left, one file's\n * contents on the right.\n *\n * The view is one `conversation.view` entry, so it sits beside Chat and\n * Trajectory in the same tab strip and the shell renders it one at a time.\n * Everything the tree keeps lives in its store; everything it asks for goes\n * through its injected face. The component only decides what to draw for each\n * absolute path and what a click means: a directory toggles, a file selects for\n * preview, and anything else is shown but refuses to open.\n *\n * The preview body follows the file's format (see `format.ts`): Markdown renders\n * as a document with a source view behind a toggle, JSON as a collapsible tree\n * with the same toggle, a recognized source suffix through the shared\n * syntax-highlighted code block, an image as the image, and everything else as\n * plain numbered text. All of it comes from `@deepseek-ai/dsh-client-ui-primitives`,\n * which the browser shell shares into its frozen module table \u2014 the one way this\n * bundle may use another package's values at runtime.\n *\n * Switching to another tab unmounts this component but not its Session-scoped\n * store, so expansion, the selected file, and the chosen body survive the round\n * trip. A read left mid-flight when that happens settles into the store anyway,\n * because the face's requests ride the plugin's lifetime, not the component's.\n */\nimport { useEffect, useMemo, type ReactNode } from 'react'\nimport type { ConvViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { InjectFace, PropsLocale, PropsStore, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport {\n CodeBlock, FileTypeIcon, IconFolderClose16, IconFolderOpen16, IconRefreshOutline16, JsonTree,\n MarkdownText, classifyFileType, fileSizeText,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { JsonTreeLabels, MarkdownLabels } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\nimport { hasSourceToggle, previewFormatFor } from './format.ts'\nimport type { PreviewFormat } from './format.ts'\nimport type { FilesInjected } from './face.ts'\nimport type {\n FilesState, LevelState, PreviewContent, PreviewMode, PreviewState, PreviewText,\n createFilesStore,\n} from './store.ts'\nimport type {} from './locales.ts'\n\n/** The view's composed props: the Conversation View seat, its store, its face, and its copy. */\nexport type FilesViewProps =\n & ConvViewProps\n & PropsStore<ReturnType<typeof createFilesStore>>\n & InjectFace<FilesInjected>\n & PropsLocale<'fileExplorer'>\n\n/** Natural, case-insensitive name order, so `file2` precedes `file10`. */\nconst byName = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })\n\n/**\n * Order one level's entries for display: directories first, then everything\n * else, each group by name. The endpoint's order is a listing fact; this is the\n * reader's.\n * @param entries - the listing as the endpoint returned it.\n * @returns a new array, directories first, then by name within each group.\n */\nexport function orderEntries(entries: readonly WorkspaceDirectoryEntry[]): WorkspaceDirectoryEntry[] {\n return [...entries].sort((left, right) => {\n const group = Number(right.type === 'directory') - Number(left.type === 'directory')\n return group !== 0 ? group : byName.compare(left.name, right.name)\n })\n}\n\n/**\n * The absolute path of one child entry.\n *\n * Joined with `/` whatever the parent's separators: the host resolves mixed\n * separators, and the tree only needs a stable key.\n * @param parent - absolute path of the listed directory.\n * @param name - the entry's basename.\n * @returns the child's absolute path.\n */\nexport function childPath(parent: string, name: string): string {\n return `${parent.replace(/[/\\\\]+$/, '')}/${name}`\n}\n\n/**\n * Split a path into its directory prefix and its last segment, for a header\n * that greys the prefix and inks the name.\n * @param path - absolute path.\n * @returns the trailing-slash directory prefix and the basename.\n */\nexport function pathParts(path: string): { directory: string; name: string } {\n const at = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'))\n if (at < 0) return { directory: '', name: path }\n return { directory: path.slice(0, at + 1), name: path.slice(at + 1) }\n}\n\n/**\n * Split one loaded page into its source lines.\n *\n * The page's own line count decides: `0` is a page past the file's last line\n * (an empty file on the first read), which has no lines to draw and must not\n * become one empty row. Otherwise the text is the page's lines joined with\n * `\\n`, so splitting returns exactly what `lines` promised.\n * @param page - the loaded text page.\n * @returns the page's source lines, in order.\n */\nexport function previewLines(page: PreviewText): string[] {\n return page.lines === 0 ? [] : page.text.split('\\n')\n}\n\n/**\n * Parse one complete JSON document for the tree view.\n * @param text - the file's text.\n * @returns the parsed container, or undefined when the text is not JSON a tree\n * can walk (a syntax error, or a bare scalar).\n */\nexport function parseJsonDocument(text: string): object | undefined {\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch {\n return undefined\n }\n return typeof value === 'object' && value !== null ? value : undefined\n}\n\n/**\n * Say why a directory could not be listed, in terms of the directory.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show under the directory.\n */\nexport function treeFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('tree.error.notFound')\n case 'workspace-file/outside-workspace': return t('tree.error.outsideWorkspace')\n case 'workspace-file/not-directory': return t('tree.error.notDirectory')\n // Carrier and unclassified host failures reach the reader as themselves:\n // this tree knows nothing useful to add to a transport-level message.\n default: return t('tree.error.unavailable', { message: failure.message })\n }\n}\n\n/**\n * Say why a file could not be previewed, in terms of the file.\n * @param t - namespace-bound translate.\n * @param failure - the settled Remote failure.\n * @returns the line to show in the preview pane.\n */\nexport function previewFailureLine(\n t: TranslateNS<'fileExplorer'>,\n failure: RemoteFailure,\n): string {\n switch (failure.code) {\n case 'workspace-file/not-found': return t('preview.error.notFound')\n case 'workspace-file/not-text': return t('preview.error.notText')\n case 'workspace-file/too-large': return t('preview.error.tooLarge')\n case 'workspace-file/not-regular-file': return t('preview.error.notRegularFile')\n default: return t('preview.error.unavailable', { message: failure.message })\n }\n}\n\n/** What every level shares: the explorer's state and the three gestures. */\ninterface TreeContext {\n readonly state: FilesState\n readonly onToggle: (path: string) => void\n readonly onOpen: (path: string) => void\n readonly t: TranslateNS<'fileExplorer'>\n}\n\n/** One directory's rows: its state while listing, its entries once listed. */\nfunction Level({ path, tree }: { path: string; tree: TreeContext }): ReactNode {\n const { state, t } = tree\n const level: LevelState | undefined = state.levels[path]\n if (level === undefined || level.kind === 'loading') {\n return <li className=\"dsh-fe-note\" data-files-row=\"loading\">{t('tree.loading')}</li>\n }\n if (level.kind === 'failed') {\n return (\n <li\n className=\"dsh-fe-note dsh-fe-note-error\"\n data-files-row=\"failed\"\n data-files-code={level.failure.code}\n >\n {treeFailureLine(t, level.failure)}\n </li>\n )\n }\n const entries = orderEntries(level.level.entries)\n return (\n <>\n {entries.length === 0 && <li className=\"dsh-fe-note\" data-files-row=\"empty\">{t('tree.empty')}</li>}\n {entries.map(entry => <Entry key={entry.name} parent={path} entry={entry} tree={tree} />)}\n {level.level.truncated && (\n <li className=\"dsh-fe-note\" data-files-row=\"truncated\">{t('tree.truncated')}</li>\n )}\n </>\n )\n}\n\n/** One entry's row, and its children when it is an expanded directory. */\nfunction Entry({\n parent,\n entry,\n tree,\n}: {\n parent: string\n entry: WorkspaceDirectoryEntry\n tree: TreeContext\n}): ReactNode {\n const path = childPath(parent, entry.name)\n if (entry.type === 'directory') {\n const expanded = tree.state.expanded.includes(path)\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"directory\" data-files-path={path}>\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-expanded={expanded}\n onClick={() => { tree.onToggle(path) }}\n >\n {expanded\n ? <IconFolderOpen16 className=\"dsh-fe-icon\" />\n : <IconFolderClose16 className=\"dsh-fe-icon\" />}\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </button>\n {expanded && <ul className=\"dsh-fe-level\"><Level path={path} tree={tree} /></ul>}\n </li>\n )\n }\n if (entry.type === 'file') {\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"file\" data-files-path={path}>\n <button\n type=\"button\"\n className=\"dsh-fe-row\"\n aria-current={tree.state.selected === path}\n onClick={() => { tree.onOpen(path) }}\n >\n <FileTypeIcon kind={classifyFileType(entry.name)} size={16} />\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </button>\n </li>\n )\n }\n return (\n <li className=\"dsh-fe-item\" data-files-entry=\"other\" data-files-path={path}>\n <span className=\"dsh-fe-row dsh-fe-row-static\" title={tree.t('tree.other')}>\n <span className=\"dsh-fe-name\">{entry.name}</span>\n </span>\n </li>\n )\n}\n\n/**\n * One file's source text through the shared syntax highlighter.\n *\n * `CodeBlock` owns the grammar lookup, the numbered gutter, and the copy\n * control; the owning class only replaces the chat card's chrome (margin,\n * radius, grey fill) with the pane's, and states the wrap preference: lines wrap\n * at their spaces and keep every word whole, and a token that cannot fit on a\n * line by itself still breaks rather than sliding under a scrollbar.\n */\nfunction HighlightedSource({\n page,\n lang,\n wrap,\n t,\n}: {\n page: PreviewText\n lang: string | undefined\n wrap: boolean\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n return (\n <div className=\"dsh-fe-source\" data-wrap={wrap} data-preview-lang={lang ?? 'plain'}>\n <CodeBlock\n code={page.text}\n lang={lang}\n lineNumbers\n copyLabel={t('code.copy')}\n copiedLabel={t('code.copied')}\n />\n </div>\n )\n}\n\n/**\n * A Markdown file as a document, and a JSON file as a collapsible tree.\n *\n * The label objects are memoized on the translated strings rather than on `t`:\n * the bound translate keeps one identity across a locale change while its output\n * changes, and a fresh labels object on every render would discard the\n * primitives' parse memos.\n */\nfunction FormattedBody({\n page,\n format,\n jsonDocument,\n t,\n}: {\n page: PreviewText\n format: PreviewFormat\n /** Parsed JSON for a `json` format, decided by the pane before it elected this body. */\n jsonDocument: object | undefined\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n const copyLabel = t('code.copy')\n const copiedLabel = t('code.copied')\n const footnotes = t('footnotes')\n const markdownLabels = useMemo<MarkdownLabels>(\n () => ({ code: { copyLabel, copiedLabel }, footnotes }),\n [copyLabel, copiedLabel, footnotes],\n )\n const copyValue = t('json.copyValue')\n const copyJson = t('json.copyJson')\n const copyPath = t('json.copyPath')\n const copyPrettyJson = t('json.copyPrettyJson')\n const copyCompactJson = t('json.copyCompactJson')\n const copied = t('json.copied')\n const copyFailed = t('json.copyFailed')\n const collapseNode = t('json.collapseNode')\n const expandNode = t('json.expandNode')\n const jsonLabels = useMemo<JsonTreeLabels>(() => ({\n copyValue,\n copyJson,\n copyPath,\n copyPrettyJson,\n copyCompactJson,\n copied,\n copyFailed,\n collapseNode,\n expandNode,\n // Reads the live translate at call time, so the tooltip follows a locale\n // change without rebuilding this object; `t` is stable by contract.\n copyButtonTitle: action => t('json.copyTitle', { action }),\n }), [\n collapseNode, copyCompactJson, copyFailed, copyJson, copyPath, copyPrettyJson,\n copyValue, copied, expandNode, t,\n ])\n\n if (format.kind === 'markdown') {\n return (\n <div className=\"dsh-fe-prose\" data-preview-format=\"markdown\">\n <MarkdownText text={page.text} labels={markdownLabels} />\n </div>\n )\n }\n // The pane elects this body for `json` only once the document parses.\n if (jsonDocument === undefined) return null\n return (\n <div className=\"dsh-fe-json\" data-preview-format=\"json\">\n <JsonTree\n data={jsonDocument}\n label={t('preview.jsonLabel')}\n labels={jsonLabels}\n expandTopLevel={false}\n />\n </div>\n )\n}\n\n/**\n * The preview pane's body for the current selection.\n *\n * Every body owns its own scrollport class (`dsh-fe-scroll`) so the pane header\n * stays put; the body kind decides which of them, and the header's controls\n * follow the same decision.\n */\nfunction PreviewBody({\n state,\n content,\n format,\n body,\n jsonDocument,\n jsonFallback,\n t,\n}: {\n state: FilesState\n content: PreviewContent\n format: PreviewFormat\n body: PreviewBodyKind\n jsonDocument: object | undefined\n /** The reader asked for the tree and the file does not parse; say so above the source. */\n jsonFallback: boolean\n t: TranslateNS<'fileExplorer'>\n}): ReactNode {\n if (content.kind === 'image') {\n const { name } = pathParts(state.selected ?? '')\n return (\n <div className=\"dsh-fe-scroll dsh-fe-imagehost\" data-preview-state=\"ready\" data-preview-format=\"image\">\n <img className=\"dsh-fe-image\" src={content.image.dataUrl} alt={name} data-preview-image />\n </div>\n )\n }\n const page = content.page\n const lines = previewLines(page)\n if (lines.length === 0) {\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines=\"0\"\n >\n <p className=\"dsh-fe-note\" data-preview-row=\"empty\">{t('preview.empty')}</p>\n </div>\n )\n }\n return (\n <div\n className=\"dsh-fe-scroll\"\n data-preview-state=\"ready\"\n data-preview-format={format.kind}\n data-preview-lines={lines.length}\n data-preview-body={body}\n >\n {jsonFallback && (\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-row=\"invalid-json\">\n {t('preview.jsonInvalid')}\n </p>\n )}\n {body === 'rendered'\n ? <FormattedBody page={page} format={format} jsonDocument={jsonDocument} t={t} />\n : <HighlightedSource page={page} lang={format.lang} wrap={state.wrap} t={t} />}\n {!page.eof && (\n <p className=\"dsh-fe-note\" data-preview-row=\"truncated\">\n {t('preview.truncated', { lines: page.lines })}\n </p>\n )}\n </div>\n )\n}\n\n/** Which body the pane is drawing, which is also which header controls apply. */\ntype PreviewBodyKind = 'rendered' | 'source' | 'image'\n\n/**\n * Resolve the body a file's format and the reader's mode choice agree on.\n * @param content - what the read delivered, or null while it has not settled.\n * @param format - the file's format.\n * @param mode - the reader's explicit choice, or null to follow the default.\n * @param jsonWalkable - whether a `json` format's document parsed into a tree.\n * @returns the body kind.\n */\nfunction bodyKindOf(\n content: PreviewContent | null,\n format: PreviewFormat,\n mode: PreviewMode | null,\n jsonWalkable: boolean,\n): PreviewBodyKind {\n if (content !== null && content.kind === 'image') return 'image'\n if (!hasSourceToggle(format)) return 'source'\n if ((mode ?? 'rendered') === 'source') return 'source'\n // JSON the tree cannot walk falls back to its source, so the header offers the\n // body that is actually on screen.\n if (format.kind === 'json' && !jsonWalkable) return 'source'\n return 'rendered'\n}\n\n/**\n * The Files view: the workspace tree, and the preview of the selected file.\n * @param props - the Conversation View seat, store, injected face, and copy.\n * @returns the two-pane explorer.\n */\nexport function FilesView({\n sessionId, useSessions, useStore, actions, list, read, t,\n}: FilesViewProps): ReactNode {\n const cwd = useSessions(sessions => sessions.byId[sessionId]?.cwd)\n const state = useStore(store => store)\n // A JSON document is parsed here, once per settled read, because both the\n // header's body election and the tree body need the answer. Parsing stays a\n // function of the pane's own decision, not of the read: a file the tree cannot\n // walk is still previewed, as source.\n const jsonDocument = useMemo(() => {\n if (state.selected === null) return undefined\n if ((state.mode ?? 'rendered') === 'source') return undefined\n if (previewFormatFor(state.selected).kind !== 'json') return undefined\n const preview = state.preview\n if (preview.kind !== 'ready' || preview.content.kind !== 'text') return undefined\n return parseJsonDocument(preview.content.page.text)\n }, [state.mode, state.preview, state.selected])\n\n useEffect(() => {\n if (cwd === undefined || state.root === cwd) return\n actions.start(cwd)\n list(cwd)\n }, [actions, cwd, list, state.root])\n\n if (cwd === undefined) {\n return (\n <div className=\"dsh-fe-root\" data-files-state=\"no-workspace\">\n <div className=\"dsh-fe-status\">{t('tree.noWorkspace')}</div>\n </div>\n )\n }\n if (state.root === null) return null\n\n const tree: TreeContext = {\n state,\n onToggle: (path) => {\n const loaded = state.levels[path] !== undefined\n actions.toggled(path)\n if (!loaded) list(path)\n },\n onOpen: (path) => { read(path) },\n t,\n }\n // Reload drops every level and asks again for the expanded ones; a collapsed\n // level is fetched again the next time it opens.\n const reloadTree = (): void => {\n actions.reset()\n for (const path of state.expanded) list(path)\n }\n const reloadPreview = (): void => {\n if (state.selected !== null) read(state.selected)\n }\n const root = pathParts(state.root)\n const selected = state.selected === null ? null : pathParts(state.selected)\n const preview: PreviewState = state.preview\n const format: PreviewFormat | null = state.selected === null\n ? null\n : previewFormatFor(state.selected)\n const content = preview.kind === 'ready' ? preview.content : null\n const fallbackFormat: PreviewFormat = { kind: 'text', lang: undefined, mediaType: undefined }\n const body = bodyKindOf(\n content,\n format ?? fallbackFormat,\n state.mode,\n jsonDocument !== undefined,\n )\n const jsonFallback = format?.kind === 'json'\n && (state.mode ?? 'rendered') === 'rendered'\n && jsonDocument === undefined\n && content !== null\n && content.kind === 'text'\n const meta = preview.kind !== 'ready'\n ? null\n : preview.content.kind === 'text'\n ? [\n t('preview.lines', { lines: preview.content.page.lines }),\n preview.content.page.bytes === undefined\n ? null\n : fileSizeText(preview.content.page.bytes),\n ].filter(part => part !== null).join(' \u00B7 ')\n : preview.content.image.bytes === undefined\n ? null\n : fileSizeText(preview.content.image.bytes)\n\n return (\n <div\n className=\"dsh-fe-root\"\n data-files-state=\"tree\"\n data-files-root={state.root}\n data-conversation-composer-overlay=\"\"\n >\n <div className=\"dsh-fe-panes\">\n <div className=\"dsh-fe-tree\" data-files-pane=\"tree\">\n <div className=\"dsh-fe-head\">\n <span className=\"dsh-fe-path\" title={state.root}>\n <span>\n <span className=\"dsh-fe-path-muted\">{root.directory}</span>\n {root.name}\n </span>\n </span>\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('tree.reload')}\n title={t('tree.reload')}\n data-files-reload\n onClick={reloadTree}\n >\n <IconRefreshOutline16 />\n </button>\n </div>\n <div className=\"dsh-fe-scroll\">\n <ul className=\"dsh-fe-level\"><Level path={state.root} tree={tree} /></ul>\n </div>\n </div>\n <div className=\"dsh-fe-preview\" data-files-pane=\"preview\">\n <div className=\"dsh-fe-head\">\n {selected === null\n ? <span className=\"dsh-fe-path dsh-fe-path-muted\">{t('preview.title')}</span>\n : (\n <span className=\"dsh-fe-path\" title={state.selected ?? undefined}>\n <span>\n <span className=\"dsh-fe-path-muted\">{selected.directory}</span>\n {selected.name}\n </span>\n </span>\n )}\n {meta !== null && <span className=\"dsh-fe-meta\" data-preview-meta>{meta}</span>}\n {format !== null && hasSourceToggle(format) && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={body === 'source'}\n aria-label={body === 'source' ? t('preview.rendered') : t('preview.source')}\n title={body === 'source' ? t('preview.rendered') : t('preview.source')}\n data-preview-mode-toggle\n onClick={() => { actions.setMode(body === 'source' ? 'rendered' : 'source') }}\n >\n {body === 'source' ? t('preview.rendered') : t('preview.source')}\n </button>\n )}\n {body === 'source' && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-pressed={state.wrap}\n aria-label={state.wrap ? t('preview.nowrap') : t('preview.wrap')}\n title={state.wrap ? t('preview.nowrap') : t('preview.wrap')}\n data-preview-wrap-toggle\n onClick={() => { actions.setWrap(!state.wrap) }}\n >\n {state.wrap ? '\u21B5' : '\u2192'}\n </button>\n )}\n {state.selected !== null && (\n <button\n type=\"button\"\n className=\"dsh-fe-tool\"\n aria-label={t('preview.reload')}\n title={t('preview.reload')}\n data-preview-reload\n onClick={reloadPreview}\n >\n <IconRefreshOutline16 />\n </button>\n )}\n </div>\n {preview.kind === 'loading' && <div className=\"dsh-fe-status\">{t('preview.loading')}</div>}\n {preview.kind === 'failed' && (\n <div className=\"dsh-fe-scroll\" data-preview-state=\"failed\">\n <p className=\"dsh-fe-note dsh-fe-note-error\" data-preview-code={preview.failure.code}>\n {previewFailureLine(t, preview.failure)}\n </p>\n <button type=\"button\" className=\"dsh-fe-tool\" onClick={reloadPreview}>\n {t('preview.reload')}\n </button>\n </div>\n )}\n {state.selected === null && (\n <div className=\"dsh-fe-status\">{t('preview.placeholder')}</div>\n )}\n {preview.kind === 'ready' && content !== null && format !== null && (\n <PreviewBody\n state={state}\n content={content}\n format={format}\n body={body}\n jsonDocument={jsonDocument}\n jsonFallback={jsonFallback}\n t={t}\n />\n )}\n </div>\n </div>\n </div>\n )\n}\n", "/**\n * What a workspace file is, for preview purposes.\n *\n * The pane picks a display from the file's own name before it reads anything,\n * so the read itself can differ: an image needs its complete bytes, everything\n * else needs a page of text. The decision is a pure function of the path, which\n * keeps it testable and keeps the read path honest \u2014 the pane never reads bytes\n * it will not draw.\n *\n * Only grammars the shared syntax highlighter actually carries are mapped. An\n * unrecognized suffix is plain text on purpose: a wrong grammar colours the\n * file misleadingly, while plain numbered text is merely unadorned.\n */\n\n/** The display a file's contents get. */\nexport type PreviewFormatKind = 'markdown' | 'json' | 'code' | 'image' | 'text'\n\n/** One file's preview format. */\nexport interface PreviewFormat {\n /** Which body the pane draws. */\n readonly kind: PreviewFormatKind\n /** Grammar hint for the highlighted source view; absent when no grammar is known. */\n readonly lang: string | undefined\n /** Media type the read bytes are labelled with; present only for `image`. */\n readonly mediaType: string | undefined\n}\n\n/** Image suffixes and the media type their bytes carry. */\nconst IMAGE_MEDIA_TYPES = new Map<string, string>([\n ['png', 'image/png'],\n ['apng', 'image/apng'],\n ['jpg', 'image/jpeg'],\n ['jpeg', 'image/jpeg'],\n ['jfif', 'image/jpeg'],\n ['gif', 'image/gif'],\n ['webp', 'image/webp'],\n ['avif', 'image/avif'],\n ['bmp', 'image/bmp'],\n ['ico', 'image/x-icon'],\n // An SVG draws as an image here; `<img>` neither runs its scripts nor lets it\n // reach the application origin, so the markup stays inert.\n ['svg', 'image/svg+xml'],\n])\n\n/**\n * Suffix \u2192 grammar id, restricted to the ids the shared highlighter resolves\n * (its own alias list). A suffix whose grammar is absent stays unmapped so the\n * file falls through to plain text rather than a wrong colouring.\n */\nconst GRAMMAR_EXTENSIONS: Readonly<Record<string, readonly string[]>> = {\n typescript: ['ts', 'tsx', 'mts', 'cts', 'js', 'jsx', 'mjs', 'cjs'],\n shellscript: ['sh', 'bash', 'zsh', 'ksh', 'shell'],\n json: ['json', 'jsonc', 'jsonl', 'ndjson', 'map', 'webmanifest'],\n python: ['py', 'pyw', 'pyi'],\n ruby: ['rb', 'rake', 'gemspec', 'ru'],\n go: ['go'],\n rust: ['rs'],\n java: ['java'],\n c: ['c', 'h'],\n cpp: ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx', 'ipp'],\n csharp: ['cs', 'csx'],\n kotlin: ['kt', 'kts'],\n swift: ['swift'],\n php: ['php', 'phtml', 'php3', 'php4', 'php5'],\n yaml: ['yaml', 'yml'],\n toml: ['toml'],\n ini: ['ini', 'cfg', 'conf', 'properties', 'desktop', 'service'],\n markdown: ['md', 'markdown', 'mkd', 'mdown', 'mdwn'],\n mdx: ['mdx'],\n html: ['html', 'htm', 'xhtml', 'shtml'],\n css: ['css'],\n scss: ['scss'],\n less: ['less'],\n sql: ['sql', 'ddl', 'dml'],\n xml: ['xml', 'xsd', 'xsl', 'xslt', 'rss', 'atom', 'plist'],\n lua: ['lua'],\n}\n\nconst GRAMMAR_BY_EXTENSION = new Map(Object.entries(GRAMMAR_EXTENSIONS)\n .flatMap(([grammar, suffixes]) => suffixes.map(suffix => [suffix, grammar] as const)))\n\n/**\n * The lowercase suffix a file name ends in.\n *\n * A name that starts with its dot has no suffix: `.gitignore` is a whole name,\n * not a `gitignore` file. A trailing dot has none either.\n * @param path - absolute or relative path, in either separator style.\n * @returns the suffix without its dot, or undefined.\n */\nexport function extensionOf(path: string): string | undefined {\n const normalized = path.replaceAll('\\\\', '/')\n const name = normalized.slice(normalized.lastIndexOf('/') + 1)\n const at = name.lastIndexOf('.')\n if (at <= 0 || at === name.length - 1) return undefined\n return name.slice(at + 1).toLowerCase()\n}\n\n/**\n * Decide how one file is previewed, from its name alone.\n * @param path - absolute or relative path of the file.\n * @returns its preview format.\n */\nexport function previewFormatFor(path: string): PreviewFormat {\n const extension = extensionOf(path)\n const mediaType = extension === undefined ? undefined : IMAGE_MEDIA_TYPES.get(extension)\n if (mediaType !== undefined) return { kind: 'image', lang: undefined, mediaType }\n const lang = extension === undefined ? undefined : GRAMMAR_BY_EXTENSION.get(extension)\n if (lang === undefined) return { kind: 'text', lang: undefined, mediaType: undefined }\n if (lang === 'json') return { kind: 'json', lang, mediaType: undefined }\n // Markdown gets a rendered body and a source body; MDX stays on the source\n // side, because its JSX would render as prose.\n if (lang === 'markdown') return { kind: 'markdown', lang, mediaType: undefined }\n return { kind: 'code', lang, mediaType: undefined }\n}\n\n/**\n * Whether the pane offers a rendered/source switch for a format.\n * @param format - the file's format.\n * @returns whether both bodies exist.\n */\nexport function hasSourceToggle(format: PreviewFormat): boolean {\n return format.kind === 'markdown' || format.kind === 'json'\n}\n", "/**\n * The explorer's asynchronous half: listing directories, and reading the\n * selected file in whichever form its format needs.\n *\n * The component never awaits anything. It calls `list` / `read`, and this face\n * performs the Remote call and writes the outcome through the store's own\n * actions \u2014 the Slot-standard `inject` shape, so the Session id is resolved by\n * the framework and the write set stays the store's.\n *\n * One level has one listing in force: asking for a level again (the reload\n * gesture, a directory reopened after a reset) retires the listing still in\n * flight for it, whose settlement then writes nothing. The preview is the same\n * with one generation, and the store additionally drops a settlement whose path\n * is no longer selected. Requests carry the plugin's lifetime signal: unloading\n * the plugin abandons everything in flight, and no settlement writes after\n * that.\n */\nimport type { BoundActions } from '@deepseek-ai/dsh-client-store'\nimport type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { SessionId } from '@deepseek-ai/dsh-session/types'\nimport { previewFormatFor } from './format.ts'\nimport type { PreviewText, createFilesStore } from './store.ts'\n\n/** The slice of the Client Remote face this plugin calls. */\nexport type WorkspaceFilesRemote = Pick<ClientRemote, 'workspaceFiles'>\n\n/**\n * Largest number of leading lines one text preview asks for. The host's own\n * `maxLines` default is higher; keeping the page small is this viewer's choice,\n * and a file that does not reach `eof` says so in the header.\n */\nexport const PREVIEW_LINES = 2000\n\n/** The explorer's injected business face, as the component receives it. */\nexport interface FilesInjected {\n /**\n * List one directory into the store, retiring any listing in flight for it.\n * @param path - absolute directory path.\n */\n readonly list: (path: string) => void\n /**\n * Read one file into the store: a page of text, or complete bytes when the\n * file's format draws an image.\n * @param path - absolute file path.\n */\n readonly read: (path: string) => void\n}\n\n/**\n * Bind the explorer's face to one Session's Remote namespace.\n * @param remote - the Client Remote face carrying the `workspaceFiles` namespace.\n * @param sessionId - the Session whose workspace root authorizes every call.\n * @param signal - the plugin's lifetime signal; aborting abandons in-flight work.\n * @param actions - the bound store actions this face writes through.\n * @returns the listing and preview operations the component calls.\n */\nexport function filesFace(\n remote: WorkspaceFilesRemote,\n sessionId: SessionId,\n signal: AbortSignal,\n actions: BoundActions<ReturnType<typeof createFilesStore>>,\n): FilesInjected {\n /** Per absolute path: the listing generation a settlement must match; the latest request wins. */\n const generations = new Map<string, number>()\n /** The preview generation a settlement must match. */\n let previewGeneration = 0\n\n return {\n list(path) {\n if (signal.aborted) return\n const generation = (generations.get(path) ?? 0) + 1\n generations.set(path, generation)\n actions.loading(path)\n void remote.workspaceFiles.list(sessionId, path, signal).then((result) => {\n if (signal.aborted || generations.get(path) !== generation) return\n if (result.ok) {\n actions.loaded(path, {\n entries: result.value.entries,\n truncated: result.value.truncated,\n })\n } else {\n actions.failed(path, result.error)\n }\n })\n },\n read(path) {\n if (signal.aborted) return\n previewGeneration += 1\n const generation = previewGeneration\n const format = previewFormatFor(path)\n actions.selecting(path)\n const settle = (write: () => void): void => {\n if (signal.aborted || previewGeneration !== generation) return\n write()\n }\n // An image needs its bytes whole; everything else needs a page of lines,\n // because the source view can show a bounded prefix of any text file.\n if (format.kind === 'image') {\n void remote.workspaceFiles.readAll(sessionId, path, signal).then((result) => {\n settle(() => {\n if (result.ok) {\n actions.previewLoaded(path, {\n kind: 'image',\n image: {\n dataUrl: `data:${format.mediaType};base64,${result.value.data}`,\n bytes: result.value.bytes,\n },\n })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n return\n }\n void remote.workspaceFiles.read(sessionId, path, { offset: 1, limit: PREVIEW_LINES }, signal)\n .then((result) => {\n settle(() => {\n if (result.ok) {\n const page: PreviewText = {\n text: result.value.text,\n lines: result.value.lines,\n eof: result.value.eof,\n bytes: result.value.bytes,\n }\n actions.previewLoaded(path, { kind: 'text', page })\n } else {\n actions.previewFailed(path, result.error)\n }\n })\n })\n },\n }\n}\n", "/**\n * `fileExplorer` namespace dictionaries, and the namespace declaration.\n *\n * The failure lines name what the tree could not list, one code each, because a\n * directory that is gone, one outside the workspace, and a path that is not a\n * directory each suggest a different next step. The preview lines do the same\n * for the read failures a viewer can meet: not text, too large, not a regular\n * file.\n *\n * The namespace merge lives with its key set so that any module naming\n * `TranslateNS<'fileExplorer'>` or `PropsLocale<'fileExplorer'>` needs only this\n * file, whichever entry a program loads first.\n */\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'fileExplorer'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** View tab label, tree states, and preview states. */\n fileExplorer: FileExplorerKey\n }\n}\n\n/** Simplified Chinese dictionary and key-set source of truth. */\nexport const zh = {\n 'view.files': '\u6587\u4EF6',\n 'tree.root': '\u5DE5\u4F5C\u533A',\n 'tree.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'tree.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'tree.empty': '\u7A7A\u76EE\u5F55',\n 'tree.truncated': '\u6761\u76EE\u8FC7\u591A\uFF0C\u53EA\u663E\u793A\u4E86\u4E00\u90E8\u5206\u3002',\n 'tree.noWorkspace': '\u8FD9\u4E2A\u4F1A\u8BDD\u6CA1\u6709\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002',\n 'tree.other': '\u65E2\u4E0D\u662F\u6587\u4EF6\u4E5F\u4E0D\u662F\u76EE\u5F55\uFF0C\u65E0\u6CD5\u6253\u5F00\u3002',\n 'tree.error.notFound': '\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'tree.error.outsideWorkspace': '\u8BE5\u76EE\u5F55\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u3002',\n 'tree.error.notDirectory': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u76EE\u5F55\u3002',\n 'tree.error.unavailable': '\u8BFB\u53D6\u5931\u8D25\uFF1A{message}',\n 'preview.placeholder': '\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u6587\u4EF6\u8FDB\u884C\u9884\u89C8\u3002',\n 'preview.empty': '\u7A7A\u6587\u4EF6',\n 'preview.rendered': '\u9884\u89C8',\n 'preview.source': '\u6E90\u7801',\n 'preview.jsonLabel': 'JSON \u6587\u6863',\n 'preview.jsonInvalid': '\u8FD9\u4E0D\u662F\u53EF\u5C55\u5F00\u7684 JSON\uFF0C\u5DF2\u663E\u793A\u6E90\u7801\u3002',\n 'code.copy': '\u590D\u5236',\n 'code.copied': '\u5DF2\u590D\u5236',\n 'footnotes': '\u811A\u6CE8',\n 'json.copyValue': '\u590D\u5236\u503C',\n 'json.copyJson': '\u590D\u5236 JSON',\n 'json.copyPath': '\u590D\u5236\u8DEF\u5F84',\n 'json.copyPrettyJson': '\u590D\u5236\u683C\u5F0F\u5316 JSON',\n 'json.copyCompactJson': '\u590D\u5236\u538B\u7F29 JSON',\n 'json.copied': '\u5DF2\u590D\u5236',\n 'json.copyFailed': '\u590D\u5236\u5931\u8D25',\n 'json.collapseNode': '\u6536\u8D77',\n 'json.expandNode': '\u5C55\u5F00',\n 'json.copyTitle': '{action}',\n 'preview.title': '\u9884\u89C8',\n 'preview.loading': '\u6B63\u5728\u8BFB\u53D6\u2026',\n 'preview.reload': '\u91CD\u65B0\u8BFB\u53D6',\n 'preview.truncated': '\u6587\u4EF6\u8F83\u957F\uFF0C\u53EA\u663E\u793A\u524D {lines} \u884C\u3002',\n 'preview.wrap': '\u81EA\u52A8\u6362\u884C',\n 'preview.nowrap': '\u4E0D\u6362\u884C',\n 'preview.lines': '{lines} \u884C',\n 'preview.error.notFound': '\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u79FB\u52A8\u6216\u5220\u9664\u3002',\n 'preview.error.notText': '\u8FD9\u4E0D\u662F\u53EF\u4EE5\u9884\u89C8\u7684\u6587\u672C\u6587\u4EF6\u3002',\n 'preview.error.tooLarge': '\u6587\u4EF6\u592A\u5927\uFF0C\u65E0\u6CD5\u9884\u89C8\u3002',\n 'preview.error.notRegularFile': '\u8FD9\u4E0D\u662F\u4E00\u4E2A\u666E\u901A\u6587\u4EF6\u3002',\n 'preview.error.unavailable': '\u9884\u89C8\u5931\u8D25\uFF1A{message}',\n} satisfies Record<string, string>\n\n/** Files dictionary key union. */\nexport type FileExplorerKey = keyof typeof zh\n\n/** English dictionary, checked against the Chinese key set. */\nexport const en = {\n 'view.files': 'Files',\n 'tree.root': 'Workspace',\n 'tree.reload': 'Reload',\n 'tree.loading': 'Reading\u2026',\n 'tree.empty': 'Empty directory',\n 'tree.truncated': 'Too many entries, showing only some of them.',\n 'tree.noWorkspace': 'This session has no workspace directory.',\n 'tree.other': 'Not a file or a directory, so it cannot be opened.',\n 'tree.error.notFound': 'That directory is gone. It may have been moved or deleted.',\n 'tree.error.outsideWorkspace': 'That directory is outside the workspace, so it cannot be read.',\n 'tree.error.notDirectory': 'That is not a directory.',\n 'tree.error.unavailable': 'Read failed: {message}',\n 'preview.placeholder': 'Pick a file on the left to preview it.',\n 'preview.empty': 'Empty file',\n 'preview.rendered': 'Rendered',\n 'preview.source': 'Source',\n 'preview.jsonLabel': 'JSON document',\n 'preview.jsonInvalid': 'That is not JSON a tree can walk, so the source is shown.',\n 'code.copy': 'Copy',\n 'code.copied': 'Copied',\n 'footnotes': 'Footnotes',\n 'json.copyValue': 'Copy value',\n 'json.copyJson': 'Copy JSON',\n 'json.copyPath': 'Copy path',\n 'json.copyPrettyJson': 'Copy pretty JSON',\n 'json.copyCompactJson': 'Copy compact JSON',\n 'json.copied': 'Copied',\n 'json.copyFailed': 'Copy failed',\n 'json.collapseNode': 'Collapse',\n 'json.expandNode': 'Expand',\n 'json.copyTitle': '{action}',\n 'preview.title': 'Preview',\n 'preview.loading': 'Reading\u2026',\n 'preview.reload': 'Reload',\n 'preview.truncated': 'This file is long, showing the first {lines} lines.',\n 'preview.wrap': 'Wrap long lines',\n 'preview.nowrap': 'Do not wrap long lines',\n 'preview.lines': '{lines} lines',\n 'preview.error.notFound': 'That file is gone. It may have been moved or deleted.',\n 'preview.error.notText': 'That is not a text file this preview can show.',\n 'preview.error.tooLarge': 'That file is too large to preview.',\n 'preview.error.notRegularFile': 'That is not a regular file.',\n 'preview.error.unavailable': 'Preview failed: {message}',\n} satisfies Record<FileExplorerKey, string>\n", "/**\n * The file explorer's view state: what each listed directory holds, which\n * directories are open, and what the preview is showing.\n *\n * The explorer is not one resource. A directory listing per level, expanded\n * lazily, plus one preview read for the selected file, is state the view owns \u2014\n * so it lives in a Slot-standard exclusive store: the framework mints one\n * instance per Session, and the state survives switching to Chat or Trajectory\n * and back, which unmounts the component.\n *\n * Write actions run only after `start`; the store's keys are absolute paths,\n * because a child is its parent joined with the entry name and the host accepts\n * the absolute form for both listing and reading.\n */\nimport { defineStore, type EngineStoreHandle } from '@deepseek-ai/dsh-client-store'\nimport type { RemoteFailure } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { WorkspaceDirectoryEntry } from '@deepseek-ai/dsh-api-workspace-files/types'\n\n/** One directory's contents, as one expanded level of the tree. */\nexport interface DirLevel {\n /** The directory's entries, in the endpoint's order. */\n readonly entries: readonly WorkspaceDirectoryEntry[]\n /** The listing hit the endpoint's entry cap, so entries are missing. */\n readonly truncated: boolean\n}\n\n/** What one directory level is doing right now. */\nexport type LevelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly level: DirLevel }\n | { readonly kind: 'failed'; readonly failure: RemoteFailure }\n\n/** One page of text, as the preview keeps it. */\nexport interface PreviewText {\n /** The page's lines joined with `\\n`. */\n readonly text: string\n /** How many lines the page holds. */\n readonly lines: number\n /** Whether the page includes the file's last line. */\n readonly eof: boolean\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/** Complete image bytes, addressed as a URL the browser can draw directly. */\nexport interface PreviewImage {\n /** `data:` URL carrying the file's complete bytes under its media type. */\n readonly dataUrl: string\n /** Byte size of the complete file, when the backend reports it. */\n readonly bytes: number | undefined\n}\n\n/**\n * What one read delivered. The pane decides which arm it needs from the file's\n * name before reading, so the two never mix: a page of text for everything\n * readable as text, complete bytes for an image.\n */\nexport type PreviewContent =\n | { readonly kind: 'text'; readonly page: PreviewText }\n | { readonly kind: 'image'; readonly image: PreviewImage }\n\n/**\n * Which body the pane draws for a file that has more than one. `null` leaves\n * the choice to the format's own default; selecting a file resets it there, so\n * a mode picked for one file never leaks into the next.\n */\nexport type PreviewMode = 'rendered' | 'source'\n\n/** What the preview is showing right now. */\nexport type PreviewState =\n | { readonly kind: 'idle' }\n | { readonly kind: 'loading'; readonly path: string }\n | { readonly kind: 'ready'; readonly path: string; readonly content: PreviewContent }\n | { readonly kind: 'failed'; readonly path: string; readonly failure: RemoteFailure }\n\n/** The explorer's whole state for one Session. */\nexport interface FilesState {\n /** Absolute path of the workspace root this explorer is rooted at; `null` before it is known. */\n root: string | null\n /** Level state by absolute directory path; a path absent here was never asked for. */\n levels: Record<string, LevelState>\n /** Expanded absolute directory paths, root included, in expansion order. */\n expanded: string[]\n /** Absolute path of the file the preview is about, or `null`. */\n selected: string | null\n /** Preview state for `selected`. */\n preview: PreviewState\n /** Whether the preview wraps long lines instead of scrolling sideways. */\n wrap: boolean\n /** Explicit body choice for a two-body file; `null` follows the format's default. */\n mode: PreviewMode | null\n}\n\n/** The explorer store's write set. */\ntype FilesActions = {\n /** Seed the explorer at its workspace root, with the root expanded. */\n start: (draft: FilesState, root: string) => void\n /** Mark one directory as being listed. */\n loading: (draft: FilesState, path: string) => void\n /** Record one directory's contents. */\n loaded: (draft: FilesState, path: string, level: DirLevel) => void\n /** Record why one directory could not be listed. */\n failed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Open a collapsed directory, or collapse an open one. */\n toggled: (draft: FilesState, path: string) => void\n /** Drop every listed level, keeping what is expanded: the reload gesture's first half. */\n reset: (draft: FilesState) => void\n /** Select one file and mark its preview as being read. */\n selecting: (draft: FilesState, path: string) => void\n /** Record the selected file's contents. */\n previewLoaded: (draft: FilesState, path: string, content: PreviewContent) => void\n /** Record why the selected file could not be read. */\n previewFailed: (draft: FilesState, path: string, failure: RemoteFailure) => void\n /** Drop the preview, keeping nothing selected. */\n clearPreview: (draft: FilesState) => void\n /** Choose whether the preview wraps long lines. */\n setWrap: (draft: FilesState, wrap: boolean) => void\n /** Choose which body a two-body file shows. */\n setMode: (draft: FilesState, mode: PreviewMode) => void\n}\n\n/** The state a freshly created explorer starts from. */\nfunction emptyState(): FilesState {\n return {\n root: null,\n levels: {},\n expanded: [],\n selected: null,\n preview: { kind: 'idle' },\n wrap: true,\n mode: null,\n }\n}\n\n/**\n * Declare the explorer's store.\n *\n * A factory rather than a shared handle: the registration declares it as an\n * exclusive store, so the framework mints one instance per Session and disposes\n * it with that Session's binding.\n * @returns the store handle to declare on the registration.\n */\nexport function createFilesStore(): EngineStoreHandle<FilesState, FilesActions> {\n return defineStore({\n init: emptyState,\n actions: {\n start: (d, root) => {\n d.root = root\n d.levels = {}\n d.expanded = [root]\n d.selected = null\n d.preview = { kind: 'idle' }\n },\n loading: (d, path) => {\n d.levels[path] = { kind: 'loading' }\n },\n loaded: (d, path, level) => {\n d.levels[path] = { kind: 'ready', level }\n },\n failed: (d, path, failure) => {\n d.levels[path] = { kind: 'failed', failure }\n },\n toggled: (d, path) => {\n const at = d.expanded.indexOf(path)\n if (at >= 0) d.expanded.splice(at, 1)\n else d.expanded.push(path)\n },\n reset: (d) => {\n d.levels = {}\n },\n selecting: (d, path) => {\n d.selected = path\n d.preview = { kind: 'loading', path }\n // A body choice belongs to the file it was made for.\n d.mode = null\n },\n previewLoaded: (d, path, content) => {\n // A settlement for a file the reader already left writes nothing: the\n // store keys the preview by the selected path, not by arrival order.\n if (d.selected !== path) return\n d.preview = { kind: 'ready', path, content }\n },\n previewFailed: (d, path, failure) => {\n if (d.selected !== path) return\n d.preview = { kind: 'failed', path, failure }\n },\n clearPreview: (d) => {\n d.selected = null\n d.preview = { kind: 'idle' }\n },\n setWrap: (d, wrap) => {\n d.wrap = wrap\n },\n setMode: (d, mode) => {\n d.mode = mode\n },\n },\n })\n}\n", "/**\n * The explorer's stylesheet, injected as one owned `<style>` tag.\n *\n * A client bundle arriving through the module loader is a single script: the\n * loader fetches `lib/client.js` and nothing else, so a separate stylesheet\n * would never be requested. Injecting the sheet from the bundle is the same\n * mechanism the built-in packages' `clientBundle()` preset uses for global CSS,\n * and the tag is removed when the plugin unloads.\n *\n * Colours come from the shell's `--dsw-alias-*` theme tokens, so the page\n * follows the active appearance without owning any palette of its own.\n */\n\n/** Marker attribute for this plugin's style tag. */\nconst STYLE_ATTRIBUTE = 'data-dsh-file-explorer'\n\nconst CSS = `\n.dsh-fe-root {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n font-size: var(--dsh-content-font-size-secondary, 13px);\n line-height: 1.5;\n}\n\n.dsh-fe-panes {\n display: flex;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n.dsh-fe-tree,\n.dsh-fe-preview {\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.dsh-fe-tree {\n flex: 0 0 auto;\n width: 280px;\n min-width: 160px;\n max-width: 60%;\n resize: horizontal;\n overflow: hidden;\n border-right: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-fe-preview {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.dsh-fe-head {\n display: flex;\n flex: 0 0 auto;\n gap: 4px;\n align-items: center;\n box-sizing: border-box;\n height: 38px;\n padding: 0 6px 0 14px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-fe-path {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n font-size: 12px;\n white-space: nowrap;\n text-overflow: ellipsis;\n direction: rtl;\n text-align: left;\n}\n\n.dsh-fe-path > span {\n direction: ltr;\n unicode-bidi: embed;\n}\n\n.dsh-fe-path-muted {\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-meta {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n white-space: nowrap;\n}\n\n.dsh-fe-tool {\n display: inline-flex;\n flex: none;\n align-items: center;\n justify-content: center;\n min-width: 28px;\n height: 28px;\n padding: 0 6px;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 11px;\n line-height: 1;\n background: transparent;\n border: none;\n border-radius: 14px;\n cursor: pointer;\n}\n\n.dsh-fe-tool:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-tool svg {\n width: 15px;\n height: 15px;\n}\n\n.dsh-fe-scroll {\n flex: 1 1 auto;\n min-height: 0;\n padding: 8px 8px calc(var(--dsh-composer-height, 152px) + 24px);\n overflow: auto;\n scrollbar-gutter: stable;\n}\n\n.dsh-fe-scroll::-webkit-scrollbar-track {\n margin: 2px;\n}\n\n.dsh-fe-level {\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.dsh-fe-level .dsh-fe-level {\n padding-left: 16px;\n}\n\n.dsh-fe-row {\n display: flex;\n gap: 6px;\n align-items: center;\n width: 100%;\n min-width: 0;\n padding: 5px 10px;\n color: inherit;\n font: inherit;\n text-align: left;\n background: transparent;\n border: 0;\n border-radius: 10px;\n cursor: pointer;\n}\n\n.dsh-fe-row:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-fe-row[aria-current='true'] {\n background: var(--dsw-alias-interactive-bg-active);\n}\n\n.dsh-fe-row-static {\n cursor: default;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-row-static:hover {\n background: transparent;\n}\n\n.dsh-fe-icon {\n flex: 0 0 auto;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.dsh-fe-name {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.dsh-fe-note {\n margin: 0;\n padding: 3px 10px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n}\n\n.dsh-fe-note-error {\n color: var(--dsw-alias-label-error);\n}\n\n.dsh-fe-status {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n justify-content: center;\n padding: 24px;\n color: var(--dsw-alias-label-secondary);\n text-align: center;\n}\n\n/* \u2500\u2500 format-aware bodies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Every body the pane can draw comes from the shared primitives, so this sheet\n carries no source-line rail of its own: the code block owns the numbered\n gutter, its counter, and its copy control. */\n\n/* The shared CodeBlock draws the highlighted source; this host only swaps the\n chat card's chrome for the pane's: no margin, no radius, no grey fill, and\n the pane's own scroll container. */\n.dsh-fe-source {\n --dsl-code-block-background: transparent;\n --dsl-code-block-border-radius: 0;\n --dsl-code-block-line-white-space: pre;\n}\n\n.dsh-fe-source .md-code-block {\n margin: 0;\n}\n\n.dsh-fe-source .md-code-block > [data-code-block-banner] > div:first-child:empty {\n display: none;\n}\n\n.dsh-fe-source .md-code-block pre {\n box-sizing: border-box;\n min-width: 100%;\n padding: 8px 0;\n overflow: visible;\n white-space: pre;\n word-break: normal;\n overflow-wrap: normal;\n}\n\n.dsh-fe-source[data-wrap='true'] {\n --dsl-code-block-line-white-space: pre-wrap;\n}\n\n.dsh-fe-source[data-wrap='true'] .md-code-block pre {\n white-space: pre-wrap;\n /* The primitive's default is break-all; keep words whole instead. */\n word-break: normal;\n overflow-wrap: break-word;\n}\n\n/* Rendered Markdown: prose lays out in normal white space, with the pane's\n insets and no monospace inheritance from the source view. */\n.dsh-fe-prose {\n min-width: 0;\n padding: 4px 4px 0;\n font-family: var(--dsw-font-family);\n white-space: normal;\n}\n\n.dsh-fe-json {\n min-width: 0;\n font-family: var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);\n font-size: 12px;\n white-space: normal;\n}\n\n/* An image keeps its intrinsic size, centres while it fits, and scales down to\n the pane rather than overflowing it. */\n.dsh-fe-imagehost {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n}\n\n.dsh-fe-image {\n display: block;\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n`\n\n/**\n * Install the plugin's stylesheet once, and hand back its disposer.\n * @returns a disposer removing the tag this call created, or a no-op when the sheet was already installed.\n */\nexport function installStyles(): () => void {\n if (typeof document === 'undefined') return () => {}\n if (document.querySelector(`style[${STYLE_ATTRIBUTE}]`) !== null) return () => {}\n const tag = document.createElement('style')\n tag.setAttribute(STYLE_ATTRIBUTE, '')\n tag.textContent = CSS\n document.head.appendChild(tag)\n return () => { tag.remove() }\n}\n"],
|
|
5
|
+
"mappings": "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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoRL,SAAS,gBAA4B;AAC1C,MAAI,OAAO,aAAa,YAAa,QAAO,MAAM;AAAA,EAAC;AACnD,MAAI,SAAS,cAAc,SAAS,eAAe,GAAG,MAAM,KAAM,QAAO,MAAM;AAAA,EAAC;AAChF,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,aAAa,iBAAiB,EAAE;AACpC,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC7B,SAAO,MAAM;AAAE,QAAI,OAAO;AAAA,EAAE;AAC9B;;;AN5PO,IAAM,OAAO;AAMb,IAAM,SAAS,CAAC,SAAS,UAAU,UAAU,uBAAuB;AAMpE,IAAM,gBAAgB;AAMtB,SAAS,MAAM,KAAoB;AACxC,MAAI,OAAO,eAAe,wCAAwC;AAClE,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,0CAA0C;AAIhG,QAAM,IAAI,IAAI,OAAO,KAAK,EAAE;AAC5B,QAAM,QAAQ,iBAAiB;AAC/B,QAAM,WAAW,IAAI,gBAAgB;AACrC,MAAI,OAAO,MAAM,MAAM;AAAE,aAAS,MAAM;AAAA,EAAE,GAAG,8CAA8C;AAE3F,MAAI,MAAM,OAAO,qBAAqB,MAAM,IAAI,MAAM,SAAS;AAAA,IAC7D,MAAM;AAAA,IACN,IAAI;AAAA;AAAA;AAAA,IAGJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO,MAAM,EAAE,YAAY;AAAA,IAC3B;AAAA,IACA,QAAQ,CACN,WACA,YACkB,UAAU,IAAI,QAAQ,WAAW,SAAS,QAAQ,OAAO;AAAA,EAC/E,GAAG,SAAS,CAAC;AACf;",
|
|
6
|
+
"names": ["name", "name", "preview"]
|
|
7
|
+
}
|
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Host half of the `@jaxzhou/dsh-file-explorer` plugin.\n *\n * The plugin is browser-only. Its file tree and preview read through the\n * `workspaceFiles` Remote namespace that the Web composition already mounts\n * (`@deepseek-ai/dsh-api-workspace-files`), so the Host side publishes no\n * service, tool, prompt section, or event \u2014 it exists to give the package a\n * Loader row and to publish the `dsh.client` declaration in `package.json`\n * that makes the client module system serve `lib/client.js`.\n */\n\n/** This plugin's identity inside the Host Loader. */\nexport const name = 'file-explorer'\n\n/** No Host-side behavior. */\nexport function apply(): void {}\n"],
|
|
5
|
+
"mappings": ";AAYO,IAAM,OAAO;AAGb,SAAS,QAAc;AAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jaxzhou/dsh-file-explorer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DeepSeek Harness plugin: a workspace file browser with a per-format preview (rendered Markdown, a JSON tree, 24 syntax grammars, images), shown as a Conversation View tab beside Chat and Trajectory",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"lib/index.js",
|
|
15
|
+
"lib/index.js.map",
|
|
16
|
+
"lib/client.js",
|
|
17
|
+
"lib/client.js.map",
|
|
18
|
+
"cordis.patch.yml",
|
|
19
|
+
"README.md",
|
|
20
|
+
"README.zh.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"dsh-plugin",
|
|
26
|
+
"deepseek-harness",
|
|
27
|
+
"dsh",
|
|
28
|
+
"file-explorer",
|
|
29
|
+
"file-browser",
|
|
30
|
+
"file-preview",
|
|
31
|
+
"markdown-preview",
|
|
32
|
+
"json-viewer",
|
|
33
|
+
"syntax-highlighting"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/jaxzhou/dsh-file-explorer.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/jaxzhou/dsh-file-explorer#readme",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/jaxzhou/dsh-file-explorer/issues"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.19.0"
|
|
45
|
+
},
|
|
46
|
+
"dsh": {
|
|
47
|
+
"bundle": {
|
|
48
|
+
"patch": "./cordis.patch.yml"
|
|
49
|
+
},
|
|
50
|
+
"client": {
|
|
51
|
+
"platform": "web",
|
|
52
|
+
"inject": [
|
|
53
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
54
|
+
"@deepseek-ai/dsh-api-workspace-files",
|
|
55
|
+
"@deepseek-ai/dsh-client-locale",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-session"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"registry": "https://registry.npmjs.org",
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
68
|
+
"build": "node scripts/build.mjs",
|
|
69
|
+
"test": "node --test tests/*.test.mjs",
|
|
70
|
+
"check": "npm run typecheck && npm run build && npm test",
|
|
71
|
+
"prepublishOnly": "npm run check"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@deepseek-ai/cordis": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
83
|
+
"@deepseek-ai/dsh-api-gateway": "^0.1.5-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.5-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-api-workspace-files": "0.1.5-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-client-store": "0.1.5-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-session": "0.1.5-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
93
|
+
"@types/react": "~18.3.1",
|
|
94
|
+
"esbuild": "^0.25.0",
|
|
95
|
+
"react": "^18.3.1",
|
|
96
|
+
"typescript": "~5.7.2"
|
|
97
|
+
}
|
|
98
|
+
}
|