Files
apes-sidebar/src/config.ts
T
Menghuan1918 223b2545d9 refactor(shell): retire the plugin's own right panel and free windows
DSH 0.1.5 owns the right column (its native Sidebar hosts every tab type
this plugin registers), so the plugin's own right panel, its width drag /
new-session seed prefs, and the free-window feature are gone. The bottom
workbench is the only plugin-owned surface left, and its expand/collapse
control is registered into DSH's session-header utilities list slot (the
header's corner seat belongs to the native sidebar's own control).

- state: one `bottomSplits` tree; `makeDefaultState()` takes no arguments;
  `toggleBottomPanel` / `openTabInBottomPane` are the only landing
  reducers; `setBottomHeight` and `sanitizeState` share the
  `CONVERSATION_MIN` cap (a persisted tall height no longer snaps on the
  first drag).
- service: every landing in the workbench (creation or focus) expands it;
  `SIDEBAR_FEATURES` drops `'floatWindows'`; the open/close/activate tab
  pools cover the single tree.
- shell: `Sidebar.tsx` renders only the bottom workbench; the toggle lives
  in `sidebar/bottom-toggle.tsx`; layout-push pushes height only (no width,
  no `--dsh-sidebar-width`).
- prefs: `openByDefault` / `defaultWidthPercent` / `changesDiffFloat`
  removed from the schema, defaults, client parser, settings page, and all
  20 dictionaries; the retired keys in stored documents are ignored.
- tests: free-window specs deleted, panel/float assertions dropped,
  remaining suites adapted (124 files / 1287 tests); the mount lane asserts
  the header toggle and sweeps the native sidebar's guide page.
- docs: README / README_EN / guide §0,§11 / AGENTS test map.
2026-09-09 15:18:06 +08:00

146 lines
6.0 KiB
TypeScript

/**
* Serializable configuration and defaults for the sidebar host half. Loader
* schema validation normally fills defaults; {@link resolveSidebarConfig}
* applies the same defaults for direct callers that bypass the Loader.
* @module dsh-better-sidebar/config
*/
import z from 'schemastery'
import {
TERMINAL_FONT_SIZE_DEFAULT,
TERMINAL_FONT_SIZE_MAX,
TERMINAL_FONT_SIZE_MIN,
TITLE_BAR_STRIP_DEFAULT,
TITLE_BAR_STRIP_MAX,
TITLE_BAR_STRIP_MIN,
type SidebarPrefs,
} from './prefs-shared.ts'
export {
SIDEBAR_PREFS_DEFAULTS,
SIDEBAR_PREFS_NS,
TERMINAL_FONT_SIZE_DEFAULT,
TERMINAL_FONT_SIZE_MAX,
TERMINAL_FONT_SIZE_MIN,
TITLE_BAR_STRIP_DEFAULT,
TITLE_BAR_STRIP_MAX,
TITLE_BAR_STRIP_MIN,
type SidebarPrefs,
} from './prefs-shared.ts'
/** Tunable sidebar host limits (every field optional; defaults fill in). */
export interface SidebarConfig {
/** Read cap of one text file (bytes); larger files return truncated. */
readLimit?: number
/** Media route cap (bytes); larger binaries are refused. */
mediaLimit?: number
/** Upload route cap (bytes); larger files are refused without touching disk. */
uploadLimit?: number
/** Explorer row bound of one level. */
listLimit?: number
/** Terminals per session. */
terminalsPerSession?: number
/** How long a disconnected terminal process survives awaiting a reconnect. */
reconnectGraceMs?: number
/**
* Terminal shell (absolute path or bare executable name) for BOTH the UI
* terminal tabs and the model-facing `terminal_*` tools. Empty = auto:
* POSIX follows `$SHELL` then the account login shell; Windows follows
* `DSH_SIDEBAR_SHELL`, then probes for `pwsh.exe`, then falls back to the
* inbox `powershell.exe` (5.1). Set it from `cordis.patch.yml` / profile
* plugin config, e.g. `config: { shell: /bin/zsh }`.
*/
shell?: string
/**
* Optional arguments passed to the shell executable. When non-empty these
* REPLACE the automatic platform defaults (POSIX `-l` / Windows none), so
* the deployment has full control over how the shell starts. When omitted
* the existing default behavior is kept.
*/
shellArgs?: string[]
}
/** Schemastery schema for the plugin configuration. */
export const Config: z<SidebarConfig> = z.object({
readLimit: z.number().step(1).min(1).default(512 * 1024),
mediaLimit: z.number().step(1).min(1).default(20 * 1024 * 1024),
uploadLimit: z.number().step(1).min(1).default(128 * 1024 * 1024),
listLimit: z.number().step(1).min(1).default(1000),
terminalsPerSession: z.number().step(1).min(1).default(3),
reconnectGraceMs: z.number().step(1).min(0).default(30_000),
shell: z.string().default(''),
shellArgs: z.array(z.string()).default([]),
})
/** Fully defaulted sidebar host settings. */
export interface ResolvedSidebarConfig {
readLimit: number
mediaLimit: number
uploadLimit: number
listLimit: number
terminalsPerSession: number
reconnectGraceMs: number
/** The configured terminal shell; empty means the host auto-resolves it. */
shell: string
/** Explicit shell arguments; empty means use the platform defaults. */
shellArgs: string[]
}
/**
* Apply direct-call defaults after Loader schema validation has normally run.
*
* @param config - Deployment-provided sidebar host settings.
* @returns Complete settings consumed by the host half.
*/
export function resolveSidebarConfig(config: SidebarConfig | undefined): ResolvedSidebarConfig {
return {
readLimit: config?.readLimit ?? 512 * 1024,
mediaLimit: config?.mediaLimit ?? 20 * 1024 * 1024,
uploadLimit: config?.uploadLimit ?? 128 * 1024 * 1024,
listLimit: config?.listLimit ?? 1000,
terminalsPerSession: config?.terminalsPerSession ?? 3,
reconnectGraceMs: config?.reconnectGraceMs ?? 30_000,
shell: config?.shell?.trim() ?? '',
shellArgs: config?.shellArgs ?? [],
}
}
// ── User-facing "Side card" preferences ─────────────────────────────────────
/** Schemastery schema for the user-facing preferences (validated by the settings service). */
export const PrefsSchema: z<SidebarPrefs> = z.object({
autoOpenSubagent: z.boolean().default(true),
autoOpenJobs: z.boolean().default(true),
agentTerminalTools: z.boolean().default(false),
agentOpenTools: z.boolean().default(false),
bottomPanelAutoTerminal: z.boolean().default(true),
terminalFontFamily: z.string().default(''),
terminalFontSize: z.number().step(1).min(TERMINAL_FONT_SIZE_MIN).max(TERMINAL_FONT_SIZE_MAX).default(TERMINAL_FONT_SIZE_DEFAULT),
editorExplorer: z.boolean().default(false),
workspaceFence: z.boolean().default(true),
terminalShell: z.string().default(''),
terminalShellArgs: z.string().default(''),
titleBarScheme: z.union([z.const('auto'), z.const('web'), z.const('preset'), z.const('custom')]),
titleBarPresetId: z.string(),
customCss: z.string(),
titleBarCompat: z.boolean().default(false),
titleBarStripPx: z.number().step(1).min(TITLE_BAR_STRIP_MIN).max(TITLE_BAR_STRIP_MAX).default(TITLE_BAR_STRIP_DEFAULT),
htmlViewerNoSandbox: z.boolean().default(false),
htmlViewerDefaultUnsafe: z.boolean().default(false),
browserNoSandbox: z.boolean().default(false),
browserInterceptLinks: z.boolean().default(true),
browserInterceptHttp: z.boolean().default(true),
browserInterceptHttps: z.boolean().default(false),
browserAllowedLoopback: z.string().default(''),
// Per-feature enable switches are OPEN maps (any tab/viewer id, built-in or
// external): an absent key means enabled, so old documents resolve to {}
// (everything on) with no migration. Non-boolean values fail validation.
tabsEnabled: z.dict(z.boolean()).default({}),
viewersEnabled: z.dict(z.boolean()).default({}),
// Plugin-owned settings blobs (v0.12.0+) are an OPEN nested map: any
// descriptor id may carry any JSON-serializable values. This is the
// "settings seam" opening — without it the seam would drop third-party
// keys as unknown schema fields.
pluginSettings: z.dict(z.dict(z.any())).default({}),
})