@chatcode/cco-better-sidebar 0.18.0-rc.2 → 0.18.0-rc.4
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/README.md +7 -2
- package/README_EN.md +7 -2
- package/lib/client-editor.js +319 -262
- package/lib/client-mermaid.js +300 -258
- package/lib/client-registry.js +1104 -467
- package/lib/client-terminal.js +315 -258
- package/lib/client.js +1107 -470
- package/lib/index.js +49 -3
- package/lib/types/client/PluginMarketSettingsSection.d.ts +7 -0
- package/lib/types/client/PluginMarketView.d.ts +7 -0
- package/lib/types/client/api.d.ts +15 -0
- package/lib/types/client/builtins/tabs.d.ts +6 -3
- package/lib/types/client/locales.d.ts +21 -0
- package/lib/types/client/plugin-market-catalog.d.ts +25 -0
- package/lib/types/client/ui-tweaks/index.d.ts +1 -1
- package/lib/types/client/ui-tweaks/typography.d.ts +9 -0
- package/lib/types/plugin-market-api.d.ts +5 -0
- package/lib/types/plugin-market-shared.d.ts +37 -0
- package/lib/types/pty-deps.d.ts +1 -1
- package/lib/types/ui-tweaks/config.d.ts +5 -6
- package/package.json +2 -2
- package/src/client/PluginMarketSettingsSection.tsx +55 -0
- package/src/client/PluginMarketView.module.css +35 -0
- package/src/client/PluginMarketView.tsx +154 -0
- package/src/client/api.ts +7 -0
- package/src/client/builtins/tabs.tsx +16 -3
- package/src/client/index.tsx +10 -1
- package/src/client/locales-ja.ts +21 -0
- package/src/client/locales.ts +42 -0
- package/src/client/plugin-market-catalog.ts +90 -0
- package/src/client/ui-tweaks/branding.tsx +15 -7
- package/src/client/ui-tweaks/index.tsx +36 -99
- package/src/client/ui-tweaks/typography.ts +38 -0
- package/src/index.ts +2 -0
- package/src/plugin-market-api.ts +42 -0
- package/src/plugin-market-shared.ts +21 -0
- package/src/pty-deps.ts +2 -2
- package/src/ui-tweaks/config.ts +10 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* (editor / git / terminal / browser / diff) through
|
|
2
|
+
* The 6 built-in tab descriptors: the plugin registers its own pages
|
|
3
|
+
* (editor / git / terminal / browser / diff / plugin-market) through
|
|
4
4
|
* the same {@link BetterSidebarService} external plugins use — eating its
|
|
5
5
|
* own dogfood. The terminal descriptor owns its quota (`TERMINAL_LIMIT`)
|
|
6
6
|
* and mints `terminal:<uuid>` ids through `createTab`; the browser mints
|
|
@@ -17,6 +17,8 @@ import { lazyChunkComponent } from '../lazy-chunk.tsx'
|
|
|
17
17
|
import { GitView } from '../GitView.tsx'
|
|
18
18
|
import { DiffTab } from '../DiffTab.tsx'
|
|
19
19
|
import { BrowserView } from '../BrowserView.tsx'
|
|
20
|
+
import { PluginMarketView } from '../PluginMarketView.tsx'
|
|
21
|
+
import { createPluginMarketCatalog, type PluginMarketCatalog } from '../plugin-market-catalog.ts'
|
|
20
22
|
import {
|
|
21
23
|
IconBranchOutline16,
|
|
22
24
|
IconCodeOutline16,
|
|
@@ -63,6 +65,8 @@ export const TERMINAL_LIMIT = 3
|
|
|
63
65
|
export interface BuiltinTabOptions {
|
|
64
66
|
/** Returns the display title for newly opened terminal tabs. */
|
|
65
67
|
terminalTitle?: () => string
|
|
68
|
+
/** Shared app-lifetime catalog; production supplies the activation's store. */
|
|
69
|
+
pluginMarketCatalog?: PluginMarketCatalog
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
/** A client-side uuid for terminal tab identity (not shown in the UI). */
|
|
@@ -80,8 +84,9 @@ function uiTerminalCount(state: SidebarState): number {
|
|
|
80
84
|
.filter(tab => tab.type === 'terminal' && !isAgentTabId(tab.id)).length
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
/** The
|
|
87
|
+
/** The 6 built-in tab descriptors. */
|
|
84
88
|
export function builtinTabs(ctx: Context, options: BuiltinTabOptions = {}): readonly TabDescriptor[] {
|
|
89
|
+
const pluginMarketCatalog = options.pluginMarketCatalog ?? createPluginMarketCatalog()
|
|
85
90
|
return [
|
|
86
91
|
{
|
|
87
92
|
id: 'editor',
|
|
@@ -266,6 +271,14 @@ export function builtinTabs(ctx: Context, options: BuiltinTabOptions = {}): read
|
|
|
266
271
|
}),
|
|
267
272
|
component: (props) => <BrowserView {...props} />,
|
|
268
273
|
},
|
|
274
|
+
{
|
|
275
|
+
id: 'plugin-market',
|
|
276
|
+
title: () => t('pluginMarketTitle'),
|
|
277
|
+
icon: (size: number) => <IconGlobeOutline16 size={size} />,
|
|
278
|
+
order: 60,
|
|
279
|
+
single: true,
|
|
280
|
+
component: ({ scope, visible }) => <PluginMarketView scope={scope} visible={visible} catalog={pluginMarketCatalog} />,
|
|
281
|
+
},
|
|
269
282
|
{
|
|
270
283
|
id: 'diff',
|
|
271
284
|
title: () => t('git'),
|
package/src/client/index.tsx
CHANGED
|
@@ -39,6 +39,8 @@ import { SideCardSection } from './SideCardSection.tsx'
|
|
|
39
39
|
import { UsageHeatmapSection } from './UsageHeatmapSection.tsx'
|
|
40
40
|
import { AiRatioSection } from './AiRatioSection.tsx'
|
|
41
41
|
import { UpdateSettingsSection } from './UpdateSettingsSection.tsx'
|
|
42
|
+
import { registerPluginMarketSettings } from './PluginMarketSettingsSection.tsx'
|
|
43
|
+
import { createPluginMarketCatalog } from './plugin-market-catalog.ts'
|
|
42
44
|
import { api } from './api.ts'
|
|
43
45
|
import { LOCALE_NS, attachLocale, attachBetterLocale, t, zh, en,
|
|
44
46
|
ja, de, fr, pt, ko, ar, hi, id, tr, vi, th, ru, it, nl, sv, pl,
|
|
@@ -142,6 +144,11 @@ export function apply(ctx: Context): void {
|
|
|
142
144
|
// registrations (the official createXXXStore() factory rule — no
|
|
143
145
|
// module-level singleton).
|
|
144
146
|
const sidebarStore = createSidebarStore()
|
|
147
|
+
const pluginMarketCatalog = createPluginMarketCatalog()
|
|
148
|
+
ctx.effect(() => {
|
|
149
|
+
pluginMarketCatalog.start()
|
|
150
|
+
return () => pluginMarketCatalog.dispose()
|
|
151
|
+
}, 'dsh-better-sidebar: plugin catalogs')
|
|
145
152
|
// Extend (rather than replace) DSH's single-owner Workspace browser with a
|
|
146
153
|
// durable Pinned sessions peer group and an extra native row-menu action.
|
|
147
154
|
// The native browser keeps ownership of search, workspace management and
|
|
@@ -180,7 +187,7 @@ export function apply(ctx: Context): void {
|
|
|
180
187
|
// service (eating our own dogfood). The disposer unregisters them on
|
|
181
188
|
// fiber disposal (HMR-safe).
|
|
182
189
|
ctx.effect(
|
|
183
|
-
() => registerBuiltins(ctx, service, { terminalTitle: () => terminalTitle }),
|
|
190
|
+
() => registerBuiltins(ctx, service, { terminalTitle: () => terminalTitle, pluginMarketCatalog }),
|
|
184
191
|
'dsh-better-sidebar: register built-in tabs and viewers',
|
|
185
192
|
)
|
|
186
193
|
// A failure anywhere in the client lifecycle must never take the app down
|
|
@@ -481,6 +488,8 @@ export function apply(ctx: Context): void {
|
|
|
481
488
|
'dsh-better-sidebar: skill market navigation icon fallback',
|
|
482
489
|
)
|
|
483
490
|
|
|
491
|
+
registerPluginMarketSettings(ctx, pluginMarketCatalog)
|
|
492
|
+
|
|
484
493
|
// The "Side card" settings section: appears in the DSH Settings shell
|
|
485
494
|
// once the shell's declaration is on the ledger (slots.inject waits for
|
|
486
495
|
// it); the section reads/writes the prefs through the plugin's own
|
package/src/client/locales-ja.ts
CHANGED
|
@@ -16,6 +16,27 @@
|
|
|
16
16
|
|
|
17
17
|
/** The ja dictionary (key-set-equal to zh, enforced by the type annotation in locales.ts). */
|
|
18
18
|
export const ja: Record<string, string> = {
|
|
19
|
+
pluginMarketTitle: 'プラグインマーケット',
|
|
20
|
+
pluginMarketProjectDirectory: 'プロジェクトディレクトリ:{path}',
|
|
21
|
+
pluginMarketInstalledTab: 'インストール済み',
|
|
22
|
+
pluginMarketRefresh: '更新',
|
|
23
|
+
pluginMarketLoading: 'プラグインを読み込み中…',
|
|
24
|
+
pluginMarketRefreshing: '一覧を更新中…',
|
|
25
|
+
pluginMarketEmpty: 'プラグインはありません。',
|
|
26
|
+
pluginMarketBack: '戻る',
|
|
27
|
+
pluginMarketInstall: 'インストール',
|
|
28
|
+
pluginMarketUninstall: 'アンインストール',
|
|
29
|
+
pluginMarketProject: 'プロジェクト',
|
|
30
|
+
pluginMarketUser: 'ユーザー',
|
|
31
|
+
pluginMarketInstalled: 'プラグインをインストールしました。',
|
|
32
|
+
pluginMarketUninstalled: 'プラグインをアンインストールしました。',
|
|
33
|
+
pluginMarketSubtitle: 'マーケットを参照し、プロジェクトまたはユーザー環境にインストールします。',
|
|
34
|
+
pluginMarketSearch: 'マーケットやプラグインを検索',
|
|
35
|
+
pluginMarketDefault: 'デフォルト',
|
|
36
|
+
pluginMarketBrowse: 'プラグインを見る',
|
|
37
|
+
pluginMarketNoDescription: '説明なし',
|
|
38
|
+
pluginMarketInstalling: 'インストール中…',
|
|
39
|
+
pluginMarketUninstalling: 'アンインストール中…',
|
|
19
40
|
accountMenuSettings: '設定',
|
|
20
41
|
accountMenuLogout: 'ログアウト',
|
|
21
42
|
accountMenuLabel: 'アカウントメニュー',
|
package/src/client/locales.ts
CHANGED
|
@@ -23,6 +23,27 @@
|
|
|
23
23
|
|
|
24
24
|
/** The zh dictionary (also registered into the ChatCode CLI locale registry under {@link LOCALE_NS}). */
|
|
25
25
|
export const zh = {
|
|
26
|
+
pluginMarketTitle: '插件市场',
|
|
27
|
+
pluginMarketProjectDirectory: '项目目录:{path}',
|
|
28
|
+
pluginMarketInstalledTab: '已安装',
|
|
29
|
+
pluginMarketRefresh: '刷新',
|
|
30
|
+
pluginMarketLoading: '正在加载插件…',
|
|
31
|
+
pluginMarketRefreshing: '正在刷新列表…',
|
|
32
|
+
pluginMarketEmpty: '暂无插件。',
|
|
33
|
+
pluginMarketBack: '返回',
|
|
34
|
+
pluginMarketInstall: '安装',
|
|
35
|
+
pluginMarketUninstall: '卸载',
|
|
36
|
+
pluginMarketProject: '项目级',
|
|
37
|
+
pluginMarketUser: '用户级',
|
|
38
|
+
pluginMarketInstalled: '插件安装成功。',
|
|
39
|
+
pluginMarketUninstalled: '插件卸载成功。',
|
|
40
|
+
pluginMarketSubtitle: '浏览插件市场,选择安装到当前项目或用户环境。',
|
|
41
|
+
pluginMarketSearch: '搜索市场或插件',
|
|
42
|
+
pluginMarketDefault: '默认',
|
|
43
|
+
pluginMarketBrowse: '查看插件',
|
|
44
|
+
pluginMarketNoDescription: '暂无描述',
|
|
45
|
+
pluginMarketInstalling: '安装中…',
|
|
46
|
+
pluginMarketUninstalling: '卸载中…',
|
|
26
47
|
accountMenuSettings: '设置',
|
|
27
48
|
accountMenuLogout: '退出登录',
|
|
28
49
|
accountMenuLabel: '账户菜单',
|
|
@@ -494,6 +515,27 @@ export const zh = {
|
|
|
494
515
|
|
|
495
516
|
/** The en dictionary (key-set-equal to zh, enforced by the type annotation). */
|
|
496
517
|
export const en: Record<keyof typeof zh, string> = {
|
|
518
|
+
pluginMarketTitle: 'Plugin marketplace',
|
|
519
|
+
pluginMarketProjectDirectory: 'Project directory: {path}',
|
|
520
|
+
pluginMarketInstalledTab: 'Installed',
|
|
521
|
+
pluginMarketRefresh: 'Refresh',
|
|
522
|
+
pluginMarketLoading: 'Loading plugins…',
|
|
523
|
+
pluginMarketRefreshing: 'Refreshing list…',
|
|
524
|
+
pluginMarketEmpty: 'No plugins.',
|
|
525
|
+
pluginMarketBack: 'Back',
|
|
526
|
+
pluginMarketInstall: 'Install',
|
|
527
|
+
pluginMarketUninstall: 'Uninstall',
|
|
528
|
+
pluginMarketProject: 'Project',
|
|
529
|
+
pluginMarketUser: 'User',
|
|
530
|
+
pluginMarketInstalled: 'Plugin installed.',
|
|
531
|
+
pluginMarketUninstalled: 'Plugin uninstalled.',
|
|
532
|
+
pluginMarketSubtitle: 'Browse marketplaces and install plugins for your project or user.',
|
|
533
|
+
pluginMarketSearch: 'Search marketplaces or plugins',
|
|
534
|
+
pluginMarketDefault: 'Default',
|
|
535
|
+
pluginMarketBrowse: 'Browse plugins',
|
|
536
|
+
pluginMarketNoDescription: 'No description',
|
|
537
|
+
pluginMarketInstalling: 'Installing…',
|
|
538
|
+
pluginMarketUninstalling: 'Uninstalling…',
|
|
497
539
|
accountMenuSettings: 'Settings',
|
|
498
540
|
accountMenuLogout: 'Log out',
|
|
499
541
|
accountMenuLabel: 'Account menu',
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** App-lifetime remote catalogs, shared by Settings and the sidebar tab. */
|
|
2
|
+
import { api } from './api.ts'
|
|
3
|
+
import type { MarketplaceCatalog, MarketplacePlugin } from '../plugin-market-shared.ts'
|
|
4
|
+
|
|
5
|
+
interface Entry<T> {
|
|
6
|
+
readonly status: 'loading' | 'ready' | 'error'
|
|
7
|
+
readonly data?: T
|
|
8
|
+
readonly error?: string
|
|
9
|
+
readonly warning?: string
|
|
10
|
+
}
|
|
11
|
+
interface Snapshot {
|
|
12
|
+
readonly markets?: Entry<MarketplaceCatalog>
|
|
13
|
+
readonly plugins: Readonly<Record<string, Entry<MarketplacePlugin[]>>>
|
|
14
|
+
readonly refresh?: { readonly status: 'loading' | 'ready' | 'error'; readonly error?: string }
|
|
15
|
+
}
|
|
16
|
+
export interface PluginMarketCatalog {
|
|
17
|
+
subscribe(listener: () => void): () => void
|
|
18
|
+
getSnapshot(): Snapshot
|
|
19
|
+
start(): void
|
|
20
|
+
ensure(marketplaceId: string): void
|
|
21
|
+
refresh(marketplaceId?: string): void
|
|
22
|
+
dispose(): void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createPluginMarketCatalog(): PluginMarketCatalog {
|
|
26
|
+
let snapshot: Snapshot = { plugins: {} }
|
|
27
|
+
let disposed = false
|
|
28
|
+
const listeners = new Set<() => void>()
|
|
29
|
+
const controllers = new Set<AbortController>()
|
|
30
|
+
const requests = new Map<string, Promise<string | undefined>>()
|
|
31
|
+
const publish = (next: Snapshot): void => {
|
|
32
|
+
if (disposed) return
|
|
33
|
+
snapshot = next
|
|
34
|
+
listeners.forEach(listener => listener())
|
|
35
|
+
}
|
|
36
|
+
const load = <T>(
|
|
37
|
+
key: string,
|
|
38
|
+
current: () => Entry<T> | undefined,
|
|
39
|
+
update: (entry: Entry<T>) => void,
|
|
40
|
+
request: (signal: AbortSignal) => Promise<{ data: T; warning?: string }>,
|
|
41
|
+
): Promise<string | undefined> => {
|
|
42
|
+
if (disposed) return Promise.resolve(undefined)
|
|
43
|
+
const pending = requests.get(key)
|
|
44
|
+
if (pending) return pending
|
|
45
|
+
const entry: Entry<T> = { status: 'loading', data: current()?.data }
|
|
46
|
+
const controller = new AbortController()
|
|
47
|
+
controllers.add(controller)
|
|
48
|
+
update(entry)
|
|
49
|
+
const result = request(controller.signal).then(
|
|
50
|
+
({ data, warning }) => { if (current() === entry) update({ status: 'ready', data, ...(warning ? { warning } : {}) }); return undefined },
|
|
51
|
+
cause => {
|
|
52
|
+
const error = cause instanceof Error ? cause.message : String(cause)
|
|
53
|
+
if (current() === entry) update({ status: 'error', data: entry.data, error })
|
|
54
|
+
return error
|
|
55
|
+
},
|
|
56
|
+
).finally(() => { controllers.delete(controller); requests.delete(key) })
|
|
57
|
+
requests.set(key, result)
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
const markets = (force = false): Promise<string | undefined> => {
|
|
61
|
+
if (!force && snapshot.markets) return Promise.resolve(undefined)
|
|
62
|
+
return load('markets', () => snapshot.markets, entry => publish({ ...snapshot, markets: entry }), async signal => ({ data: await api.pluginMarketplaces(signal) }))
|
|
63
|
+
}
|
|
64
|
+
const plugins = (id: string, force = false): Promise<string | undefined> => {
|
|
65
|
+
if (!force && snapshot.plugins[id]) return Promise.resolve(undefined)
|
|
66
|
+
return load(`plugins:${id}`, () => snapshot.plugins[id], entry => publish({ ...snapshot, plugins: { ...snapshot.plugins, [id]: entry } }), async signal => {
|
|
67
|
+
const result = await api.pluginCatalog(id, signal)
|
|
68
|
+
return Array.isArray(result) ? { data: result } : { data: result.plugins, warning: result.warning }
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
subscribe: listener => { listeners.add(listener); return () => { listeners.delete(listener) } },
|
|
73
|
+
getSnapshot: () => snapshot,
|
|
74
|
+
start: () => { void markets() },
|
|
75
|
+
ensure: id => { void plugins(id) },
|
|
76
|
+
refresh: id => {
|
|
77
|
+
if (disposed || snapshot.refresh?.status === 'loading') return
|
|
78
|
+
// Refresh every previously visited catalog before unlocking navigation.
|
|
79
|
+
// Keep cached cards for layout continuity; views disable their controls.
|
|
80
|
+
const ids = new Set(Object.keys(snapshot.plugins))
|
|
81
|
+
if (id) ids.add(id)
|
|
82
|
+
publish({ ...snapshot, refresh: { status: 'loading' } })
|
|
83
|
+
void Promise.all([markets(true), ...[...ids].map(key => plugins(key, true))]).then(results => {
|
|
84
|
+
const errors = [...new Set(results.filter((value): value is string => value !== undefined))]
|
|
85
|
+
publish({ ...snapshot, refresh: errors.length ? { status: 'error', error: errors.join('\n') } : { status: 'ready' } })
|
|
86
|
+
})
|
|
87
|
+
},
|
|
88
|
+
dispose: () => { disposed = true; controllers.forEach(controller => controller.abort()); controllers.clear(); requests.clear(); listeners.clear() },
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -30,6 +30,8 @@ const PROJECT_NAME = 'ChatCode CLI'
|
|
|
30
30
|
const HERO_TITLE = '与世界,深一度对话'
|
|
31
31
|
const LEGACY_HOST_BRAND_TEXT = /DSH\s*(?:Local\s*Build|本地构建)|DeepSeek\s*Harness|ChatCode\s*CLI/giu
|
|
32
32
|
const LEGACY_HOST_BRAND_LABEL_TEXT = /^(?:DSH\s*(?:Local\s*Build|本地构建)|ChatCode\s*CLI)$/iu
|
|
33
|
+
const BRAND_SURFACE = '[class*="brandIdentity"],[class*="brandName"],[class*="headline"],.dut-sidebar-brand-name'
|
|
34
|
+
const USER_CONTENT = 'input,textarea,[contenteditable],[role="textbox"],pre,code,script,style'
|
|
33
35
|
const HOST_BUILD_VERSION_TEXT = /^v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)*$/iu
|
|
34
36
|
const HERO_HEADLINE = /探索未至之境/u
|
|
35
37
|
const HERO_PREVIEW_TEXT = /^(?:预览版|Preview)$/iu
|
|
@@ -104,6 +106,9 @@ body[data-ds-dark-theme] .dut-unicom-primary-action:not(:hover){background:#9B0B
|
|
|
104
106
|
`
|
|
105
107
|
|
|
106
108
|
function hasPrimaryLabel(button: HTMLButtonElement): boolean {
|
|
109
|
+
// Plugin cards own their appearance. Inventory disclosure buttons include
|
|
110
|
+
// descriptions, where words like "install" do not identify a primary action.
|
|
111
|
+
if (button.closest('[data-chatcode-plugin-market], [data-plugin-module]')) return false
|
|
107
112
|
const label = `${button.textContent ?? ''} ${button.getAttribute('aria-label') ?? ''} ${button.title}`.trim().toLowerCase()
|
|
108
113
|
return /(?:^|\s)(new session|send|install)(?:\s|$)|新会话|发送|(?:^|\s)安装(?:\s|$)/u.test(label)
|
|
109
114
|
}
|
|
@@ -193,19 +198,22 @@ function syncBranding(): void {
|
|
|
193
198
|
|
|
194
199
|
// The sidebar label identifies the precise icon to its left. Process it
|
|
195
200
|
// before the generic text pass so the sibling relationship is still intact.
|
|
196
|
-
document.querySelectorAll<HTMLElement>(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
201
|
+
document.querySelectorAll<HTMLElement>(BRAND_SURFACE).forEach((surface) => {
|
|
202
|
+
surface.querySelectorAll<HTMLElement>('*').forEach((element) => {
|
|
203
|
+
if (element.closest(USER_CONTENT) !== null) return
|
|
204
|
+
if (element.childElementCount !== 0 || !LEGACY_HOST_BRAND_LABEL_TEXT.test(element.textContent?.trim() ?? '')) return
|
|
205
|
+
if (element.textContent !== PROJECT_NAME) element.textContent = PROJECT_NAME
|
|
206
|
+
element.classList.add('dut-brand-title')
|
|
207
|
+
replaceLogoBesideBrandLabel(element)
|
|
208
|
+
hideVersionBesideBrandLabel(element)
|
|
209
|
+
})
|
|
202
210
|
})
|
|
203
211
|
|
|
204
212
|
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT)
|
|
205
213
|
let node: Text | null
|
|
206
214
|
while ((node = walker.nextNode() as Text | null) !== null) {
|
|
207
215
|
const parent = node.parentElement
|
|
208
|
-
if (parent
|
|
216
|
+
if (parent === null || parent.closest(USER_CONTENT) !== null || parent.closest(BRAND_SURFACE) === null) continue
|
|
209
217
|
if (LEGACY_HOST_BRAND_TEXT.test(node.data)) {
|
|
210
218
|
LEGACY_HOST_BRAND_TEXT.lastIndex = 0
|
|
211
219
|
node.data = node.data.replace(LEGACY_HOST_BRAND_TEXT, PROJECT_NAME)
|
|
@@ -36,6 +36,7 @@ import { WhaleIndicator, installWhaleStyles } from './whale.tsx'
|
|
|
36
36
|
import { PreciseCacheHitEntry } from './cachehit.tsx'
|
|
37
37
|
import { installTaskNotifier, previewAlerts, requestNotifyPermission } from './notifier.ts'
|
|
38
38
|
import { HeroProjectLogo, installBranding, ProjectLogoIcon, SidebarProjectLogo, SidebarProjectName } from './branding.tsx'
|
|
39
|
+
import { buildFontCss } from './typography.ts'
|
|
39
40
|
|
|
40
41
|
const NS = 'ui-tweaks'
|
|
41
42
|
const SETTINGS_ROUTE = '/_dsh/ui-tweaks/settings'
|
|
@@ -43,12 +44,12 @@ const SETTINGS_ROUTE = '/_dsh/ui-tweaks/settings'
|
|
|
43
44
|
/** Client context narrowed to the browser sessions face in this combined build. */
|
|
44
45
|
type UITweaksClientContext = ClientContext & { sessions: ISessions }
|
|
45
46
|
|
|
46
|
-
const DEFAULT_FONT_SIZE =
|
|
47
|
+
const DEFAULT_FONT_SIZE = 14
|
|
47
48
|
const MIN_FONT_SIZE = 10
|
|
48
49
|
const MAX_FONT_SIZE = 32
|
|
49
50
|
/** Legacy: code font as a percentage of the message font size (81% = stock 13/16). */
|
|
50
51
|
const DEFAULT_CODE_FONT_SCALE = 81
|
|
51
|
-
/**
|
|
52
|
+
/** Default absolute code-block size, paired with 14px prose. */
|
|
52
53
|
const DEFAULT_CODE_FONT_SIZE = 13
|
|
53
54
|
const MIN_CODE_FONT_SIZE = 8
|
|
54
55
|
const MAX_CODE_FONT_SIZE = 32
|
|
@@ -134,7 +135,7 @@ const en = {
|
|
|
134
135
|
fontSize: 'Message font size',
|
|
135
136
|
fontSizeHint: `Number between ${MIN_FONT_SIZE} and ${MAX_FONT_SIZE}; applies to message text, headings, tables and code.`,
|
|
136
137
|
codeFontSize: 'Code font size',
|
|
137
|
-
codeFontSizeHint: `Absolute code size in px (${MIN_CODE_FONT_SIZE}–${MAX_CODE_FONT_SIZE}); ${DEFAULT_CODE_FONT_SIZE}px is ChatCode CLI's default at a
|
|
138
|
+
codeFontSizeHint: `Absolute code size in px (${MIN_CODE_FONT_SIZE}–${MAX_CODE_FONT_SIZE}); ${DEFAULT_CODE_FONT_SIZE}px is ChatCode CLI's default at a 14px body. Applies to code blocks; inline code follows proportionally.`,
|
|
138
139
|
lineHeight: 'Line spacing',
|
|
139
140
|
lineHeightHint: `Base vertical spacing in px: gaps between message rows (Think ↔ tool cards) and blocks inside one reply, plus text line-height, paragraph and list margins, all scale from it; ${DEFAULT_LINE_HEIGHT} is ChatCode CLI's default.`,
|
|
140
141
|
tableStyle: 'Table style',
|
|
@@ -334,7 +335,7 @@ const zh: Record<LocaleKey, string> = {
|
|
|
334
335
|
fontSize: '消息字体大小',
|
|
335
336
|
fontSizeHint: `取值 ${MIN_FONT_SIZE}–${MAX_FONT_SIZE},作用于消息正文、标题、表格与代码。`,
|
|
336
337
|
codeFontSize: '代码字号',
|
|
337
|
-
codeFontSizeHint: `代码绝对字号,取值 ${MIN_CODE_FONT_SIZE}–${MAX_CODE_FONT_SIZE}px;${DEFAULT_CODE_FONT_SIZE}px 为 ChatCode CLI 默认(正文
|
|
338
|
+
codeFontSizeHint: `代码绝对字号,取值 ${MIN_CODE_FONT_SIZE}–${MAX_CODE_FONT_SIZE}px;${DEFAULT_CODE_FONT_SIZE}px 为 ChatCode CLI 默认(正文 14 时)。作用于代码块,行内代码按比例跟随。`,
|
|
338
339
|
lineHeight: '行高',
|
|
339
340
|
lineHeightHint: `回复区垂直间距的基准值:消息之间(如 Think ↔ 工具卡片)、回复内块之间、正文行高、段落与列表边距都按它缩放;取值 ${MIN_LINE_HEIGHT}–${MAX_LINE_HEIGHT}px,${DEFAULT_LINE_HEIGHT} 为 ChatCode CLI 默认。`,
|
|
340
341
|
tableStyle: '表格样式',
|
|
@@ -533,11 +534,13 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
533
534
|
|
|
534
535
|
function resolveValue(value: TweaksValue | undefined): ResolvedTweaks {
|
|
535
536
|
const fontSize = typeof value?.fontSize === 'number' ? value.fontSize : DEFAULT_FONT_SIZE
|
|
536
|
-
|
|
537
|
-
//
|
|
537
|
+
const codeFontScale = value?.codeFontScale ?? DEFAULT_CODE_FONT_SCALE
|
|
538
|
+
// Explicit code sizing wins; the stock preference keeps readable 13px code.
|
|
538
539
|
const codeFontSize = typeof value?.codeFontSize === 'number'
|
|
539
540
|
? Math.min(MAX_CODE_FONT_SIZE, Math.max(MIN_CODE_FONT_SIZE, value.codeFontSize))
|
|
540
|
-
:
|
|
541
|
+
: codeFontScale === DEFAULT_CODE_FONT_SCALE
|
|
542
|
+
? DEFAULT_CODE_FONT_SIZE
|
|
543
|
+
: Math.max(8, Math.round(fontSize * (13 / 16) * (codeFontScale / DEFAULT_CODE_FONT_SCALE)))
|
|
541
544
|
return {
|
|
542
545
|
fontSize,
|
|
543
546
|
codeFontSize,
|
|
@@ -559,50 +562,6 @@ function resolveValue(value: TweaksValue | undefined): ResolvedTweaks {
|
|
|
559
562
|
}
|
|
560
563
|
}
|
|
561
564
|
|
|
562
|
-
/** Scale one theme px value proportionally to the chosen base size. */
|
|
563
|
-
function rel(base: number, fontSize: number): number {
|
|
564
|
-
return Math.max(8, Math.round((base / DEFAULT_FONT_SIZE) * fontSize))
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
/** Rebuild the markdown font tokens for the chosen base size, keeping the theme faces. */
|
|
568
|
-
function buildFontCss(fontSize: number, lineHeight: number, codeFontSize: number): string {
|
|
569
|
-
const cs = getComputedStyle(document.body)
|
|
570
|
-
const fam = (name: string, fallback: string): string => {
|
|
571
|
-
const value = cs.getPropertyValue(name).trim()
|
|
572
|
-
return value.length > 0 ? value : fallback
|
|
573
|
-
}
|
|
574
|
-
const base = fam('--dsw-font-markdown-base-font-family', '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif')
|
|
575
|
-
const code = fam('--dsw-font-markdown-code-font-family', '"SF Mono", Consolas, monospace')
|
|
576
|
-
const codeBlock = fam('--dsw-font-markdown-code-block-font-family', '"SF Mono", Consolas, monospace')
|
|
577
|
-
|
|
578
|
-
const parts: string[] = []
|
|
579
|
-
// `baseLine` is the token's line-height at the 16px baseline; it scales with
|
|
580
|
-
// both the chosen font size and the 行高 unit, so at the default (16) it
|
|
581
|
-
// reproduces the stock rhythm exactly.
|
|
582
|
-
const token = (shorthand: string, size: number, baseLine: number, family: string): void => {
|
|
583
|
-
const line = Math.max(12, Math.round((baseLine / DEFAULT_FONT_SIZE) * fontSize * (lineHeight / DEFAULT_LINE_HEIGHT)))
|
|
584
|
-
parts.push(`--${shorthand}:${size}px/${line}px ${family}`)
|
|
585
|
-
parts.push(`--${shorthand}-font-size:${size}px`)
|
|
586
|
-
parts.push(`--${shorthand}-line-height:${line}px`)
|
|
587
|
-
}
|
|
588
|
-
// Code fonts hang off the absolute code-block size (stock: 13px at a 16px
|
|
589
|
-
// body), with inline code slightly larger and the small variant slightly
|
|
590
|
-
// smaller, preserving DSH's hierarchy (14/13 and 12/13 of the block).
|
|
591
|
-
const codePx = (blockRatio: number): number => Math.max(8, Math.round(codeFontSize * blockRatio))
|
|
592
|
-
token('dsw-font-markdown-base', fontSize, 28, base)
|
|
593
|
-
token('dsw-font-markdown-base-strong', fontSize, 28, base)
|
|
594
|
-
token('dsw-font-markdown-base-italic', fontSize, 28, base)
|
|
595
|
-
token('dsw-font-markdown-base-strong-italic', fontSize, 28, base)
|
|
596
|
-
token('dsw-font-markdown-h1', rel(24, fontSize), 34, base)
|
|
597
|
-
token('dsw-font-markdown-h2', rel(22, fontSize), 32, base)
|
|
598
|
-
token('dsw-font-markdown-h3', rel(20, fontSize), 30, base)
|
|
599
|
-
token('dsw-font-markdown-h4', fontSize, 28, base)
|
|
600
|
-
token('dsw-font-markdown-code', codePx(14 / 13), 22, code)
|
|
601
|
-
token('dsw-font-markdown-code-block', codePx(1), 22, codeBlock)
|
|
602
|
-
token('dsw-font-markdown-code-block-small', codePx(12 / 13), 18, codeBlock)
|
|
603
|
-
return `body{${parts.join(';')}}`
|
|
604
|
-
}
|
|
605
|
-
|
|
606
565
|
/**
|
|
607
566
|
* Claude Desktop-ish markdown table look: light-gray rounded cell cards with
|
|
608
567
|
* small gaps, no borders. Cells share the theme's inline-code background and
|
|
@@ -610,7 +569,7 @@ function buildFontCss(fontSize: number, lineHeight: number, codeFontSize: number
|
|
|
610
569
|
* renderer, so headers and cells always match.
|
|
611
570
|
*/
|
|
612
571
|
const CLAUDE_TABLE_CSS = `
|
|
613
|
-
div[data-slot="conversation.chat.node"] table{
|
|
572
|
+
div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table{
|
|
614
573
|
border-collapse:separate !important;
|
|
615
574
|
border-spacing:3px !important;
|
|
616
575
|
width:100% !important;
|
|
@@ -621,7 +580,7 @@ div[data-slot="conversation.chat.node"] table{
|
|
|
621
580
|
border:none !important;
|
|
622
581
|
font-size:var(--dsw-font-markdown-base-font-size) !important;
|
|
623
582
|
}
|
|
624
|
-
div[data-slot="conversation.chat.node"] table thead th{
|
|
583
|
+
div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table thead th{
|
|
625
584
|
background:var(--dsw-alias-markdown-inline-code) !important;
|
|
626
585
|
color:inherit !important;
|
|
627
586
|
font-weight:400 !important;
|
|
@@ -630,7 +589,7 @@ div[data-slot="conversation.chat.node"] table thead th{
|
|
|
630
589
|
border:none !important;
|
|
631
590
|
border-radius:6px !important;
|
|
632
591
|
}
|
|
633
|
-
div[data-slot="conversation.chat.node"] table tbody td{
|
|
592
|
+
div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table tbody td{
|
|
634
593
|
background:var(--dsw-alias-markdown-inline-code) !important;
|
|
635
594
|
color:inherit !important;
|
|
636
595
|
font-size:inherit !important;
|
|
@@ -639,8 +598,8 @@ div[data-slot="conversation.chat.node"] table tbody td{
|
|
|
639
598
|
border:none !important;
|
|
640
599
|
border-radius:6px !important;
|
|
641
600
|
}
|
|
642
|
-
div[data-slot="conversation.chat.node"] table code,
|
|
643
|
-
div[data-slot="conversation.chat.node"] table pre{
|
|
601
|
+
div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table code,
|
|
602
|
+
div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table pre{
|
|
644
603
|
background:transparent !important;
|
|
645
604
|
border:none !important;
|
|
646
605
|
box-shadow:none !important;
|
|
@@ -650,52 +609,32 @@ div[data-slot="conversation.chat.node"] table pre{
|
|
|
650
609
|
function buildRuntimeCss(value: ResolvedTweaks): string {
|
|
651
610
|
const rules: string[] = []
|
|
652
611
|
rules.push(buildFontCss(value.fontSize, value.lineHeight, value.codeFontSize))
|
|
653
|
-
//
|
|
654
|
-
// route them through the same base so they follow the fontSize setting too.
|
|
612
|
+
// Sent messages share the reply metrics, including explicit UI Tweaks sizes.
|
|
655
613
|
// `steering` messages are sent while the agent is busy (busyEnter: steer);
|
|
656
614
|
// `[data-pending-steering]` is their in-flight bubble before it becomes durable.
|
|
657
|
-
rules.push(`[data-chat-flow-kind="user"] [class
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
//
|
|
662
|
-
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
// Markdown table cells are pinned by DSH (15px); let every table style follow
|
|
667
|
-
// the fontSize setting as well.
|
|
668
|
-
rules.push(`div[data-slot="conversation.chat.node"] table th,div[data-slot="conversation.chat.node"] table td{font-size:var(--dsw-font-markdown-base-font-size) !important}`)
|
|
669
|
-
// Inline code is pinned by DSH to 0.875em of the surrounding text (it ignores
|
|
670
|
-
// the code token); scale that em by how far the chosen code size sits from
|
|
671
|
-
// the stock ratio (a 13px block at this body size).
|
|
672
|
-
const stockCodeBlock = value.fontSize * (13 / 16)
|
|
615
|
+
rules.push(`[data-chat-flow-kind="user"] [class*="_bubble"],[data-chat-flow-kind="steering"] [class*="_bubble"],[data-pending-steering] [class*="_bubble"]{font-size:var(--dsw-font-markdown-base-font-size) !important;line-height:var(--dsw-font-markdown-base-line-height) !important}`)
|
|
616
|
+
// Current contenteditable composers and older textarea composers inherit
|
|
617
|
+
// one size/leading pair, including placeholders and measurement layers.
|
|
618
|
+
rules.push(`[data-composer-card]{font:var(--dsw-font-markdown-base) !important}`)
|
|
619
|
+
// Tables use the same body size and leading in both table styles.
|
|
620
|
+
rules.push(`div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table th,div[data-slot="conversation.chat.node"] [class*="_markdown"]:not([data-markdown-variant="compact"]) table td{font-size:var(--dsw-font-markdown-base-font-size) !important;line-height:var(--dsw-font-markdown-base-line-height) !important}`)
|
|
621
|
+
// Inline code normally uses 0.92em. Explicit code sizing follows the
|
|
622
|
+
// same ratio relative to the 14px body / 13px code baseline.
|
|
623
|
+
const stockCodeBlock = value.fontSize * (13 / 14)
|
|
673
624
|
if (Math.abs(value.codeFontSize - stockCodeBlock) > 0.5) {
|
|
674
|
-
const em = (0.
|
|
675
|
-
rules.push(`div[data-slot="conversation.chat.node"] div[class*="
|
|
625
|
+
const em = (0.92 * (value.codeFontSize / stockCodeBlock)).toFixed(3)
|
|
626
|
+
rules.push(`div[data-slot="conversation.chat.node"] div[class*="_markdown"]:not([data-markdown-variant="compact"]) :not(pre)>code{font-size:${em}em !important}`)
|
|
676
627
|
}
|
|
677
|
-
//
|
|
678
|
-
// the stock 16px, every vertical spacing scales from it proportionally:
|
|
679
|
-
// ① gaps between message rows (Think ↔ tool cards like pwsh, user ↔
|
|
680
|
-
// assistant) — the chat flow column;
|
|
681
|
-
// ② gaps between content blocks inside one assistant reply (Think ↔ text) —
|
|
682
|
-
// the Assistant Markdown block container (`Sxvs8a_body`, CSS-module
|
|
683
|
-
// hashed, stable within the pinned rc.8 conversation package);
|
|
684
|
-
// ③ the markdown typography rhythm: paragraphs (`p` has a stock `margin:
|
|
685
|
-
// 16px 0` that must be overridden on both axes, not just bottom, or it
|
|
686
|
-
// can never tighten below 16), list margins, list-item gaps (stock 6px),
|
|
687
|
-
// paragraphs inside list items (stock 8px), code blocks, headings and
|
|
688
|
-
// rules — all scaled from the 行高 unit.
|
|
689
|
-
// Text line-height itself scales in buildFontCss above.
|
|
628
|
+
// Custom spacing scales prose without expanding compact thinking.
|
|
690
629
|
if (value.lineHeight !== DEFAULT_LINE_HEIGHT) {
|
|
691
630
|
const n = value.lineHeight
|
|
692
|
-
const md = `div[data-slot="conversation.chat.node"] div[class*="
|
|
693
|
-
const liGap = Math.max(2, Math.round((
|
|
631
|
+
const md = `div[data-slot="conversation.chat.node"] div[class*="_markdown"]:not([data-markdown-variant="compact"])`
|
|
632
|
+
const liGap = Math.max(2, Math.round((4 * n) / DEFAULT_LINE_HEIGHT))
|
|
694
633
|
const liPGap = Math.max(2, Math.round((8 * n) / DEFAULT_LINE_HEIGHT))
|
|
695
|
-
rules.push(`div[data-slot="conversation.view"]
|
|
696
|
-
rules.push(`div[data-slot="conversation.chat.node"]
|
|
697
|
-
rules.push(`${md} p{margin:${n}px 0 !important}`)
|
|
698
|
-
rules.push(`${md} ul,${md} ol{margin:${n}px 0 !important}`)
|
|
634
|
+
rules.push(`div[data-slot="conversation.view"] [data-chat-flow]{gap:${n}px !important}`)
|
|
635
|
+
rules.push(`div[data-slot="conversation.chat.node"] [data-assistant-body]{--dsh-assistant-block-gap:${n}px}`)
|
|
636
|
+
rules.push(`${md} p{margin:${Math.round(0.75 * n)}px 0 !important}`)
|
|
637
|
+
rules.push(`${md} ul,${md} ol{margin:${Math.round(0.75 * n)}px 0 !important}`)
|
|
699
638
|
rules.push(`${md} pre{margin:${n}px 0 !important}`)
|
|
700
639
|
// Code blocks: the pre's inner vertical padding is stock 16px (shiki
|
|
701
640
|
// default); scale it so the block's own height follows 行高 too. Only
|
|
@@ -711,13 +650,11 @@ function buildRuntimeCss(value: ResolvedTweaks): string {
|
|
|
711
650
|
}
|
|
712
651
|
rules.push(`${md} li:not(:first-child){margin-top:${liGap}px !important}`)
|
|
713
652
|
rules.push(`${md} li>p{margin:${liPGap}px 0 !important}`)
|
|
714
|
-
rules.push(`${md} h1,${md} h2,${md} h3{margin:${
|
|
653
|
+
rules.push(`${md} h1,${md} h2,${md} h3{margin:${Math.round(1.5 * n)}px 0 ${Math.round(0.75 * n)}px !important}`)
|
|
715
654
|
rules.push(`${md} h4,${md} h5,${md} h6{margin:${n}px 0 !important}`)
|
|
716
655
|
rules.push(`${md} hr{margin:${2 * n}px 0 !important}`)
|
|
717
656
|
rules.push(`${md} blockquote{margin:${n}px 0 0 !important}`)
|
|
718
|
-
//
|
|
719
|
-
// margin (stock `>*:first-child` / `>*:last-child`), so a message starts
|
|
720
|
-
// and ends tight and the spacing to the next node stays the column gap.
|
|
657
|
+
// Preserve the first and last block's flush edges.
|
|
721
658
|
rules.push(`${md} > :first-child{margin-top:0 !important}`)
|
|
722
659
|
rules.push(`${md} > :last-child{margin-bottom:0 !important}`)
|
|
723
660
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Font overrides for the conversation, preserving the live theme families. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Apply the UI Tweaks size and spacing preferences to the host's prose tokens.
|
|
5
|
+
* @param fontSize - Selected body size in pixels.
|
|
6
|
+
* @param lineHeight - Vertical-spacing unit; 16 retains the normal rhythm.
|
|
7
|
+
* @param codeFontSize - Selected code-block size in pixels.
|
|
8
|
+
* @returns Theme-token overrides for a runtime stylesheet.
|
|
9
|
+
*/
|
|
10
|
+
export function buildFontCss(fontSize: number, lineHeight: number, codeFontSize: number): string {
|
|
11
|
+
const parts: string[] = []
|
|
12
|
+
const base = 'var(--dsw-font-markdown-base-font-family, var(--dsw-font-family))'
|
|
13
|
+
const code = 'var(--dsw-font-markdown-code-block-font-family, var(--ds-font-family-code))'
|
|
14
|
+
const token = (name: string, size: number, leading: number, family: string, weight = 400, style = 'normal'): void => {
|
|
15
|
+
const line = Math.max(12, Math.round(leading * (lineHeight / 16)))
|
|
16
|
+
parts.push(`--${name}:${style} ${weight} ${size}px/${line}px ${family}`)
|
|
17
|
+
parts.push(`--${name}-font-size:${size}px`)
|
|
18
|
+
parts.push(`--${name}-line-height:${line}px`)
|
|
19
|
+
parts.push(`--${name}-font-weight:${weight}`)
|
|
20
|
+
parts.push(`--${name}-font-style:${style}`)
|
|
21
|
+
}
|
|
22
|
+
const bodyLine = fontSize + 10
|
|
23
|
+
token('dsw-font-markdown-base', fontSize, bodyLine, base)
|
|
24
|
+
token('dsw-font-markdown-base-strong', fontSize, bodyLine, base, 600)
|
|
25
|
+
token('dsw-font-markdown-base-italic', fontSize, bodyLine, base, 400, 'italic')
|
|
26
|
+
token('dsw-font-markdown-base-strong-italic', fontSize, bodyLine, base, 600, 'italic')
|
|
27
|
+
token('dsw-font-markdown-h1', fontSize + 4, bodyLine + 4, base, 600)
|
|
28
|
+
token('dsw-font-markdown-h2', fontSize + 2, bodyLine + 2, base, 600)
|
|
29
|
+
token('dsw-font-markdown-h3', fontSize + 1, bodyLine + 1, base, 600)
|
|
30
|
+
token('dsw-font-markdown-h4', fontSize, bodyLine, base, 600)
|
|
31
|
+
token('dsw-font-markdown-table', fontSize, bodyLine, base)
|
|
32
|
+
token('dsw-font-markdown-table-head', fontSize, bodyLine, base, 500)
|
|
33
|
+
token('dsw-font-markdown-code', codeFontSize, codeFontSize + 7,
|
|
34
|
+
'var(--dsw-font-markdown-code-font-family, var(--ds-font-family-code))')
|
|
35
|
+
token('dsw-font-markdown-code-block', codeFontSize, codeFontSize + 7, code)
|
|
36
|
+
token('dsw-font-markdown-code-block-small', Math.max(8, codeFontSize - 2), codeFontSize + 3, code)
|
|
37
|
+
return `body{${parts.join(';')}}`
|
|
38
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -54,6 +54,7 @@ import { CodingPlanCatalogue } from './codingplan.ts'
|
|
|
54
54
|
import { checkOneUpdate, CHATCODE_UPDATE_PACKAGES, performAllUpdates } from './chatcode-update.ts'
|
|
55
55
|
import { readJsonBody, requireString, SidebarError, writeError, writeJson, writeOk } from './wire.ts'
|
|
56
56
|
import { apply as applyUiTweaksHost } from './ui-tweaks/index.ts'
|
|
57
|
+
import { buildPluginMarketApi } from './plugin-market-api.ts'
|
|
57
58
|
|
|
58
59
|
export { Config }
|
|
59
60
|
export type { SidebarConfig, ResolvedSidebarConfig }
|
|
@@ -491,6 +492,7 @@ function buildApi(
|
|
|
491
492
|
const { sessionId, cwd } = await cwdOf(payload)
|
|
492
493
|
return { sessionId, cwd, root: rootLabel(cwd), parent: parentOf(cwd) ?? null }
|
|
493
494
|
},
|
|
495
|
+
...buildPluginMarketApi(ctx, cwdOf),
|
|
494
496
|
'fs.tree': async (payload) => {
|
|
495
497
|
const { cwd } = await cwdOf(payload)
|
|
496
498
|
const record = payload as { path?: unknown }
|