@lmzhen/dsh-evolution-ui 0.0.0-stage → 0.32.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.
Files changed (36) hide show
  1. package/README.md +7 -2
  2. package/lib/client.js +3656 -0
  3. package/lib/index.js +323 -0
  4. package/lib/types/client/index.d.ts +9 -0
  5. package/lib/types/index.d.ts +12 -0
  6. package/lib/types/panels/history/client/Panel.d.ts +27 -0
  7. package/lib/types/panels/history/client/api.d.ts +142 -0
  8. package/lib/types/panels/history/client/atoms.d.ts +73 -0
  9. package/lib/types/panels/history/client/chrome.d.ts +76 -0
  10. package/lib/types/panels/history/client/document.d.ts +71 -0
  11. package/lib/types/panels/history/client/guards.d.ts +13 -0
  12. package/lib/types/panels/history/client/ids.d.ts +3 -0
  13. package/lib/types/panels/history/client/index.d.ts +11 -0
  14. package/lib/types/panels/history/client/markdown.d.ts +56 -0
  15. package/lib/types/panels/history/client/messages.d.ts +30 -0
  16. package/lib/types/panels/history/client/styles.d.ts +5 -0
  17. package/lib/types/panels/history/host.d.ts +25 -0
  18. package/lib/types/panels/history/routes.d.ts +50 -0
  19. package/lib/types/panels/history/unit-undoable.d.ts +18 -0
  20. package/lib/types/panels/settings/DiagnosticsCard.d.ts +29 -0
  21. package/lib/types/panels/settings/ParamCard.d.ts +107 -0
  22. package/lib/types/panels/settings/PendingCard.d.ts +45 -0
  23. package/lib/types/panels/settings/RowConfigCard.d.ts +30 -0
  24. package/lib/types/panels/settings/SettingsSection.d.ts +19 -0
  25. package/lib/types/panels/settings/api.d.ts +64 -0
  26. package/lib/types/panels/settings/diagnostics-state.d.ts +28 -0
  27. package/lib/types/panels/settings/generated-about.d.ts +11 -0
  28. package/lib/types/panels/settings/generated-params.d.ts +51 -0
  29. package/lib/types/panels/settings/guards.d.ts +13 -0
  30. package/lib/types/panels/settings/index.d.ts +50 -0
  31. package/lib/types/panels/settings/messages.d.ts +134 -0
  32. package/lib/types/panels/settings/pending-state.d.ts +74 -0
  33. package/lib/types/panels/settings/seam.d.ts +178 -0
  34. package/lib/types/panels/settings/source.d.ts +88 -0
  35. package/lib/types/panels/settings/styles.d.ts +32 -0
  36. package/package.json +58 -4
@@ -0,0 +1,51 @@
1
+ /**
2
+ * GENERATED by packages/scripts/gen-param-client-view.mjs — do not edit.
3
+ *
4
+ * One card per settings namespace; fields are the E3 rows of the registry, so
5
+ * the browser half names exactly what the Host half registers.
6
+ */
7
+ /** One field a card renders a control for. */
8
+ export interface ClientParamField {
9
+ id: string;
10
+ group: string;
11
+ /** U-4: the sibling id this field may not go below (an empty string when the row declares no relation). */
12
+ atLeast: string;
13
+ /** The registry summary (English), kept for parity with PARAMETERS.md. */
14
+ doc: string;
15
+ /** Card label, from the registry UI tail (Chinese). */
16
+ label: string;
17
+ /** Card help text, from the registry UI tail (Chinese). */
18
+ hint: string;
19
+ /** Which control the card renders. */
20
+ control: 'number' | 'switch' | 'select' | 'text';
21
+ /** Unit suffix for a number field ('' when it carries none). */
22
+ unit: string;
23
+ /** Allowed values for a select field ([] otherwise). */
24
+ values: readonly string[];
25
+ /** Display names for `values`, same order ([] shows the raw value). */
26
+ valueLabels: readonly string[];
27
+ }
28
+ /** One card: the settings namespace it writes through, the SEAT key its source is looked up by,
29
+ * and the fields it exposes. A73 (`ClientParamSection.namespace` 一名字两义): the two roles are
30
+ * spelled separately so a Host may key them differently — `namespace` is what a save writes into
31
+ * (`update(namespace, patch)`) and what the card title is looked up by; `seatKey` is what the
32
+ * shell's `sources.sourceFor(...)` probes. Both are the LOADER ROW ID today. */
33
+ export interface ClientParamSection {
34
+ namespace: string;
35
+ /** The settings-card seat key the shell probes for this section's source. */
36
+ seatKey: string;
37
+ fields: readonly ClientParamField[];
38
+ }
39
+ export declare const CLIENT_PARAM_SECTIONS: readonly ClientParamSection[];
40
+ /**
41
+ * One bundle that mounts family rows: the plugins.row.config key of a row is
42
+ * <bundle package name>#<row id>. The names are the SOURCE-plane spellings -
43
+ * the release rescopes them with every other package literal.
44
+ */
45
+ export interface ClientRowSeat {
46
+ bundle: string;
47
+ rows: readonly string[];
48
+ }
49
+ /** Every (bundle, row) pair the Plugins page can open a configuration page for. */
50
+ export declare const CLIENT_ROW_SEATS: readonly ClientRowSeat[];
51
+ //# sourceMappingURL=generated-params.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Is this a non-null, non-array object (what JSON calls an object)?
3
+ * @param value - the value to test.
4
+ * @returns whether it is a plain object.
5
+ */
6
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
7
+ /**
8
+ * Read a value as a record, or undefined when it is not one.
9
+ * @param value - the value to read.
10
+ * @returns the value typed as a record, or undefined.
11
+ */
12
+ export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
13
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Browser half: one settings section ("自进化") hosting one card per family row, plus the
3
+ * Plugins page's per-row configuration entry for each of those rows.
4
+ *
5
+ * Until 0.7.0 the cards lived inside the platform's own 插件 section, because
6
+ * this bundle injected `@deepseek-ai/dsh-client-ui-settings-plugins` and claimed
7
+ * its keyed `settings.plugin.item` slot. The parameters are not a plugin
8
+ * inventory though — the other feature areas (market, skins, Web plugins, side
9
+ * cards) each register their OWN section, so this bundle does the same: inject
10
+ * the settings shell, register `settings.section`, and host the cards in a keyed
11
+ * child slot of that section.
12
+ *
13
+ * Since G2 that section is one of TWO render sites for the same card body. The other is
14
+ * the platform's Plugins page: each family row can be opened there, and the page renders
15
+ * whatever a plugin registered under key `<bundle package name>#<row id>`
16
+ * (`plugins.row.config`). The page owns that entry's subscription and hands the row's form
17
+ * in, so that path needs no seat of ours; our own section binds the same card to the
18
+ * client settings seat (`configForms`).
19
+ *
20
+ * The join key is the row's ENTRY ID — the platform derives a settings namespace from the
21
+ * Loader entry id, and that id is the registry's owner name — so the card, the seat lookup
22
+ * and the page key all spell one id. The legacy namespace strings in the registry
23
+ * (`evolution-memory`, …) are the G3 migration source, never a live lookup key.
24
+ *
25
+ * A row the Host does not serve renders nothing, and a deployment that drops this row
26
+ * shows no section at all: the pair carries no Host-side behaviour.
27
+ * @module @lmzhen/dsh-evolution-ui/panels/settings/client
28
+ */
29
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
30
+ import { PENDING_CARD_KEY } from './seam.ts';
31
+ /**
32
+ * Required client services: the slot registry and the locale seat.
33
+ *
34
+ * The settings seat (`configForms`) is deliberately NOT here. A required inject for a
35
+ * service the deployment did not compose leaves this whole browser half PENDING forever —
36
+ * no error, no section, no UI — and the family is also installed without a settings
37
+ * surface. It is probed instead, and the cards say what is missing.
38
+ */
39
+ export declare const inject: string[];
40
+ /** Section id and nav order (the platform's own sections sit at 0/10/15/20). */
41
+ export declare const SECTION_ID = "evolution";
42
+ export declare const SECTION_ORDER = 25;
43
+ /** The pending-window card's key, re-exported from `seam.ts` (the section names it as well). */
44
+ export { PENDING_CARD_KEY };
45
+ /**
46
+ * Register the locale namespace, the section, its cards, and the per-row configuration entries.
47
+ * @param ctx - client cordis context.
48
+ */
49
+ export declare function apply(ctx: ClientContext): void;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Copy for the evolution settings section and its parameter cards.
3
+ *
4
+ * The section is registered through the platform's locale seat (`ctx.locale`),
5
+ * which is the same route the other out-of-repo client plugins take. The earlier
6
+ * note in this package's README — that an outside package cannot bind the locale
7
+ * seat — is wrong: `locale.register(namespace, { zh, en })` is the documented
8
+ * call, and it is what makes the section title and its copy follow the UI
9
+ * language instead of shipping one hard-coded dictionary.
10
+ */
11
+ /** Locale namespace this bundle registers. */
12
+ export declare const NS = "evolution-settings";
13
+ /** Chinese copy (the family's primary language). */
14
+ export declare const zh: {
15
+ readonly title: "自进化";
16
+ readonly subtitle: "待你决定的写入在最上面;下面按功能列出你能改的参数,没有列出的由安装时的配置决定。";
17
+ readonly overridden: "已覆盖";
18
+ readonly deployment: "默认";
19
+ readonly loading: "正在读取设置…";
20
+ readonly unavailable: "这个功能没有启动(对应的插件没有安装)";
21
+ readonly seatMissing: "这个界面没有提供设置服务,参数只能看不能改";
22
+ readonly projectionFailed: "这一块读设置时出错,已记入日志;其它参数不受影响";
23
+ readonly readonly: "本次会话不允许写入设置(临时运行模式)";
24
+ readonly apply: "保存";
25
+ readonly reset: "恢复默认值";
26
+ readonly empty: "这里没有可改项";
27
+ readonly changed: "{n} 项已覆盖";
28
+ readonly discard: "放弃修改";
29
+ readonly saving: "保存中…";
30
+ readonly resetting: "恢复中…";
31
+ readonly writeTargetHost: "保存去向:宿主配置(对所有会话生效)";
32
+ readonly invalidNumber: "需要一个数字";
33
+ readonly invalidSelect: "不在允许的取值里";
34
+ readonly invalidAtLeast: "不能小于同组的 {n}";
35
+ readonly approvalConfirmNamed: "即将批准:{n}";
36
+ readonly approvalConfirmYes: "确认批准";
37
+ readonly approvalConfirmNo: "取消";
38
+ readonly resetTo: "复位为 {value}";
39
+ readonly refused: "这个值没有被接受(界面上保持原样)。想看原因:在对话里让它说明。";
40
+ readonly resetRefused: "恢复默认值没有生效(这一行还是你的值)。想看原因:在对话里让它说明。";
41
+ readonly cardReview: "会话回顾";
42
+ readonly cardMemory: "长期记忆";
43
+ readonly cardToolMemory: "记忆写入";
44
+ readonly cardCurator: "技能整理";
45
+ readonly cardSkills: "技能写入规则";
46
+ readonly cardApproval: "写入审批";
47
+ readonly cardCommands: "维护扫描";
48
+ readonly cardMerge: "技能自动合并";
49
+ readonly approvalCardHint: "写入审批=使用中生成/修改;合并审批=两轮工作流。";
50
+ readonly mergeCardHint: "写入审批=使用中生成/修改;合并审批=两轮工作流。";
51
+ readonly approvalTitle: "待批写入";
52
+ readonly approvalLoading: "正在读取待批项…";
53
+ readonly approvalEmpty: "没有待批的写入";
54
+ readonly approvalFailed: "待批项读取失败";
55
+ readonly approvalRetry: "重试";
56
+ readonly approvalApprove: "批准";
57
+ readonly approvalReject: "拒绝";
58
+ readonly approvalCount: "{n} 项待批";
59
+ readonly approvalRunningCount: "{n} 项执行中";
60
+ readonly approvalRefresh: "刷新";
61
+ readonly approvalExecuting: "执行中:这条的写效果可能已经落地,不要重复批准;要清掉它请用拒绝。";
62
+ readonly approvalKindMemory: "记忆";
63
+ readonly approvalKindSkill: "技能";
64
+ readonly approvalKindCapability: "能力";
65
+ readonly approvalAgeNow: "刚刚";
66
+ readonly approvalAgeMinutes: "{n} 分钟前";
67
+ readonly approvalAgeHours: "{n} 小时前";
68
+ readonly approvalAgeDays: "{n} 天前";
69
+ readonly approvalAgeMonths: "{n} 个月前";
70
+ readonly approvalPreview: "预览";
71
+ readonly approvalPreviewClose: "收起";
72
+ readonly approvalPreviewReading: "正在读取预览…";
73
+ readonly approvalPreviewNone: "看不了预览:";
74
+ readonly approvalDiffAdded: "{n} 行新增";
75
+ readonly approvalDiffRemoved: "{n} 行删除";
76
+ readonly approvalDiffCut: "改动区域已截断";
77
+ readonly diagTitle: "诊断";
78
+ readonly diagSubtitle: "运行状态的只读自查;不会改动任何东西。";
79
+ readonly diagRuntimeTitle: "运行状态";
80
+ readonly diagSummaryOk: "运行正常";
81
+ readonly diagSummaryWarn: "{n} 项需要注意";
82
+ readonly diagSummaryFail: "{n} 项异常";
83
+ readonly diagSummaryUnknown: "{n} 项待确认";
84
+ readonly diagRefresh: "刷新";
85
+ readonly diagRefreshing: "刷新中…";
86
+ readonly diagStatusOk: "正常";
87
+ readonly diagStatusWarn: "注意";
88
+ readonly diagStatusFail: "异常";
89
+ readonly diagStatusUnknown: "未知";
90
+ readonly diagRemedy: "建议:";
91
+ readonly diagNoRows: "没有可显示的运行状态";
92
+ readonly stateLoading: "正在读取…";
93
+ readonly failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。";
94
+ readonly failureForbidden: "平台拒绝了这个请求(身份不是 owner)。";
95
+ readonly failureUnmounted: "该功能没有挂载。";
96
+ readonly failureServer: "读取失败,可以手动重试。";
97
+ readonly fallbackHint: "可以改用命令:{command}";
98
+ };
99
+ /** English copy, key for key with {@link zh}. */
100
+ export declare const en: Record<keyof typeof zh, string>;
101
+ /**
102
+ * Card titles per settings namespace. A namespace without an entry falls back to
103
+ * its raw name, so a Host that registers a new namespace still renders — the
104
+ * title is presentation, the namespace is the join key.
105
+ */
106
+ export declare const NAMESPACE_TITLES: Readonly<Record<string, string>>;
107
+ /** One message key. */
108
+ export type MessageKey = keyof typeof zh;
109
+ /**
110
+ * Resolve one message without the locale seat (tests and the fallback path).
111
+ * @param key - the message key.
112
+ * @param language - which dictionary to read; defaults to English (the platform FALLBACK_LOCALE).
113
+ * @returns the copy this bundle carries.
114
+ */
115
+ export declare function message(key: MessageKey, language?: 'zh' | 'en'): string;
116
+ /**
117
+ * Fill one TEMPLATE's `{slot}` placeholders (G5 C4). The seat's translator takes the values, so the
118
+ * rule lives here instead of at every call site — `t('changed').replace('{n}', …)` used to be
119
+ * written seven times across three components. An UNKNOWN slot is left standing: a missing value
120
+ * must not print "undefined" into the operator's UI.
121
+ * @param template - the resolved copy, with `{slot}` placeholders.
122
+ * @param values - substitutions keyed by the slot name inside the braces.
123
+ * @returns the filled copy.
124
+ */
125
+ export declare function fill(template: string, values: Readonly<Record<string, string | number>>): string;
126
+ /**
127
+ * Resolve one message and fill its slots in one step: exactly what the seat's `t` does (G5 C4),
128
+ * for specs and stand-alone callers that hold no seat.
129
+ * @param key - the copy key.
130
+ * @param values - substitutions, or undefined for the plain copy.
131
+ * @returns the copy a reader sees.
132
+ */
133
+ export declare function formatText(key: MessageKey, values?: Readonly<Record<string, string | number>>): string;
134
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The pending card's state machine, kept OUT of the component so the three states the review asked for
3
+ * can be asserted without a browser (and so a second rendering of the same data cannot invent a fourth).
4
+ *
5
+ * The one rule worth naming: LOADING and EMPTY are different states with different copy. The skill
6
+ * history panel shipped them merged once and a reader could not tell "nothing is staged" from "the
7
+ * answer has not arrived" (E7).
8
+ * @module @lmzhen/dsh-evolution-ui/panels/settings/client/pending-state
9
+ */
10
+ import type { PendingRow, PreviewAnswer } from './api.ts';
11
+ import type { RouteFailure } from '@lmzhen/dsh-evolution-client-kit/generated/route-client';
12
+ /** What one row's preview is showing while it is open. */
13
+ export type PreviewView = {
14
+ readonly kind: 'loading';
15
+ } | {
16
+ readonly kind: 'ready';
17
+ readonly answer: Extract<PreviewAnswer, {
18
+ available: true;
19
+ }>;
20
+ } | {
21
+ readonly kind: 'unavailable';
22
+ readonly reason: string;
23
+ } | {
24
+ readonly kind: 'failed';
25
+ readonly failure: RouteFailure;
26
+ };
27
+ /** What the card is showing right now. */
28
+ export type PendingView = {
29
+ readonly kind: 'loading';
30
+ } | {
31
+ readonly kind: 'ready';
32
+ readonly rows: readonly PendingRow[];
33
+ } | {
34
+ readonly kind: 'empty';
35
+ } | {
36
+ readonly kind: 'failed';
37
+ readonly failure: RouteFailure;
38
+ };
39
+ /** The whole card state: the view, the ids with an action in flight, and the last refusal line. */
40
+ export interface PendingState {
41
+ readonly view: PendingView;
42
+ readonly busy: readonly string[];
43
+ readonly notice: string | null;
44
+ /** The row whose preview is open, or null. One at a time: a preview is a thing being read. */
45
+ readonly open: string | null;
46
+ readonly preview: PreviewView | null;
47
+ }
48
+ /** The state a card mounts in: still loading, nothing in flight, nothing said yet. */
49
+ export declare const INITIAL_PENDING_STATE: PendingState;
50
+ /** Open one row's preview: the row is now the open one and its answer is on the way. */
51
+ export declare function expanded(state: PendingState, id: string): PendingState;
52
+ /** Close whatever preview is open. */
53
+ export declare function collapsed(state: PendingState): PendingState;
54
+ /**
55
+ * A preview answer arrived. An answer for a row the reader has already left is DROPPED: it would
56
+ * otherwise paint one row's diff under another row's heading.
57
+ */
58
+ export declare function previewArrived(state: PendingState, id: string, view: PreviewView): PendingState;
59
+ /** The first load, or a refresh after a decision: back to loading, keeping no stale rows. */
60
+ export declare function loading(state: PendingState): PendingState;
61
+ /**
62
+ * A list arrived. An empty list is its OWN state — the reader is told the window is clear rather than
63
+ * being shown the same line a slow answer shows.
64
+ */
65
+ export declare function loaded(state: PendingState, rows: readonly PendingRow[]): PendingState;
66
+ /** The list could not be read: the classified failure stays on screen next to a retry control. */
67
+ export declare function failed(state: PendingState, failure: RouteFailure): PendingState;
68
+ /** Mark one record as having an action in flight (its buttons are disabled until it ends). */
69
+ export declare function beginAction(state: PendingState, id: string): PendingState;
70
+ /** The action ended, whatever it answered. */
71
+ export declare function endAction(state: PendingState, id: string): PendingState;
72
+ /** One refusal or failure line under the list; `null` clears it (a later success does). */
73
+ export declare function noticed(state: PendingState, message: string | null): PendingState;
74
+ //# sourceMappingURL=pending-state.d.ts.map
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Structural view of the client seams this bundle uses.
3
+ *
4
+ * The package is distributed OUTSIDE the platform repository, so it declares the
5
+ * seams it consumes instead of importing another plugin's values (forbidden by the
6
+ * client bundle-purity rule) or its types (which would drag the platform sources
7
+ * into this project). The shapes are the shell's contract: a `settings.section`
8
+ * registration hosting a keyed card slot, the keyed `plugins.row.config` seat the
9
+ * Plugins page opens per row, the client settings seat (`configForms`, one form per
10
+ * Host plugin entry id), and the locale seat the section's copy resolves through.
11
+ * @module @lmzhen/dsh-evolution-ui/panels/settings/client
12
+ */
13
+ import type { ReactNode } from 'react';
14
+ /** Our card slot, hosted by the section this bundle registers. */
15
+ export declare const CARD_SLOT = "evolution.namespace.card";
16
+ /** The Plugins page's per-row configuration seat (declared by `ui-plugin-manager`). */
17
+ export declare const ROW_CONFIG_SLOT = "plugins.row.config";
18
+ /**
19
+ * The key the Plugins page files a row's configuration under.
20
+ *
21
+ * Spelled here because the page's own helper cannot be imported (a value import from
22
+ * another plugin's client half is forbidden):
23
+ * `ui-plugin-manager`'s config-ledger module is the definition this mirrors.
24
+ * @param bundle - the bundle package name that declares the row.
25
+ * @param rowId - the row id that bundle's patch declares.
26
+ * @returns the `plugins.row.config` key.
27
+ */
28
+ export declare function rowConfigKey(bundle: string, rowId: string): string;
29
+ /**
30
+ * The pending-window card's slot key.
31
+ *
32
+ * The section pairs cards by key and the parameter cards use their settings namespace; this card has no
33
+ * namespace (its data comes from the host's approval routes), so it carries a key of its own. The constant
34
+ * lives HERE and not in `index.ts` because the SECTION has to name it too, and `index.ts` already imports
35
+ * the section — one definition in the module both sides already share is what keeps that pair acyclic.
36
+ */
37
+ export declare const PENDING_CARD_KEY = "approval-pending";
38
+ /** The diagnostics card's slot key: read-only, no settings namespace (its data comes from HOST_ROUTES.diagnostics). */
39
+ export declare const DIAGNOSTICS_CARD_KEY = "diagnostics";
40
+ /** Snapshot of one settings row, as the client seat reports it. */
41
+ export interface ParamSectionSnapshot {
42
+ status: 'loading' | 'ready' | 'unavailable';
43
+ value: Record<string, unknown> | undefined;
44
+ /** Raw user section: a key's PRESENCE marks the override, not its value. */
45
+ user: unknown;
46
+ writable: boolean;
47
+ /** U-5: the composition layer a cleared field reverts to (platform `base`, previously dropped). */
48
+ base: Record<string, unknown> | undefined;
49
+ /**
50
+ * Why an `unavailable` snapshot is unavailable: this bundle found no settings seat at
51
+ * all, or the seat is there and serves no such row. The two read differently to the
52
+ * operator (one is the UI's own gap, the other is a row that is not composed).
53
+ */
54
+ reason?: 'seat-missing' | 'not-served' | 'projection-failed';
55
+ }
56
+ /** The reactive source the renderer binds to a `use<Name>` seat. */
57
+ export interface ParamSectionSource {
58
+ getSnapshot(): ParamSectionSnapshot;
59
+ subscribe(listener: () => void): () => void;
60
+ /**
61
+ * T5-11/A66: the seat ARRIVED (or was replaced) — bind it and wake every listener.
62
+ *
63
+ * The source re-probes on every READ, which covers a card that has not rendered yet; it does not
64
+ * cover a card that already rendered its "no settings surface" state, because nothing tells React to
65
+ * read again. `ctx.inject` is the platform's own arrival channel (it runs the callback when the
66
+ * service appears and re-runs it when the service changes), so the bundle subscribes there instead of
67
+ * inventing a timer.
68
+ */
69
+ revive(): void;
70
+ }
71
+ /** One ordered field operation, as the client settings seat takes them. */
72
+ export interface SettingsPathOpLike {
73
+ readonly op: 'set' | 'unset';
74
+ readonly path: readonly string[];
75
+ readonly value?: unknown;
76
+ }
77
+ /** One row's client form state, as `configForms.get(entryId)` reports it. */
78
+ export interface ConfigFormSnapshotLike<T = Record<string, unknown>> {
79
+ status: 'loading' | 'ready' | 'unavailable';
80
+ value: T | undefined;
81
+ /** Composition layer the Host resolved `value` over; what a cleared field reverts to. */
82
+ base: unknown;
83
+ /** Raw user layer: a key's PRESENCE marks the override. */
84
+ user: unknown;
85
+ revision: number | undefined;
86
+ writable: boolean;
87
+ }
88
+ /** One row's client write face (0.2.x `configForms`). */
89
+ export interface ConfigFormLike<T = Record<string, unknown>> {
90
+ getSnapshot(): ConfigFormSnapshotLike<T>;
91
+ subscribe(listener: () => void): () => void;
92
+ set(field: string, value: unknown): Promise<boolean>;
93
+ unset(field: string): Promise<boolean>;
94
+ mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<boolean>;
95
+ }
96
+ /**
97
+ * The client settings seat, probed rather than injected: a deployment may compose the
98
+ * family without the settings surface, and a REQUIRED inject for a missing service
99
+ * leaves the whole browser half pending forever (no error, no UI).
100
+ */
101
+ export interface ConfigFormsSeat {
102
+ /** One Host plugin entry id to its form. */
103
+ get<T>(entryId: string): ConfigFormLike<T>;
104
+ }
105
+ /** Registration options the keyed card slot under our own section takes. */
106
+ export interface SlotRegisterOptions {
107
+ name: typeof CARD_SLOT;
108
+ /** The settings row this card edits — the slot's key. */
109
+ key: string;
110
+ inject: () => unknown;
111
+ }
112
+ /** Registration options the Plugins page's per-row config seat takes. */
113
+ export interface RowSeatRegisterOptions {
114
+ name: typeof ROW_CONFIG_SLOT;
115
+ /** `<bundle package name>#<row id>`, as the page's ledger builds it. */
116
+ key: string;
117
+ inject: () => unknown;
118
+ }
119
+ /** Options a `settings.section` registration carries (the shell's own shape). */
120
+ export interface SectionRegisterOptions {
121
+ name: 'settings.section';
122
+ /** Section id; the shell derives the nav entry's DOM id from it. */
123
+ id: string;
124
+ /** Nav order: the platform's own sections sit at 0/10/15/20. */
125
+ order: number;
126
+ /** Lazy label: the shell re-reads it when the locale changes. */
127
+ label: () => string;
128
+ /** Locale namespace the label and the section's copy resolve through. */
129
+ locale: string;
130
+ /** Child slots this section hosts, declared for the shell's renderer. */
131
+ children?: Record<string, {
132
+ kind: 'keyed' | 'list' | 'single';
133
+ scope: 'root';
134
+ }>;
135
+ }
136
+ /**
137
+ * The face the shell hands a section component: a translator bound to our locale
138
+ * namespace and the renderer for the child slots we declared.
139
+ */
140
+ export interface SectionFace {
141
+ t: (key: string) => string;
142
+ /** Render one child slot; `filter` selects keyed entries (e.g. `{ entryKey }`). */
143
+ renderSlot: (name: string, props?: Record<string, unknown>, filter?: Record<string, unknown>) => ReactNode;
144
+ }
145
+ /**
146
+ * The values and commands the Plugins page passes to a `plugins.row.config` entry:
147
+ * the page owns the subscription (it re-renders the entry), so the entry renders from
148
+ * `state` and writes through `mutate`. Both views receive this face; `summary` uses it
149
+ * only to say whether anything is overridden.
150
+ */
151
+ export interface RowConfigViewProps {
152
+ /** `summary` is the row's one-line description fallback; `page` is the whole form. */
153
+ readonly view: 'summary' | 'page';
154
+ /** Absent when the Host serves no configuration for this row. */
155
+ readonly form?: RowConfigForm | undefined;
156
+ }
157
+ /** The row form the page derived from its settings seat. */
158
+ export interface RowConfigForm {
159
+ readonly state: ConfigFormSnapshotLike;
160
+ readonly mutate: (ops: readonly SettingsPathOpLike[], revision?: number) => Promise<boolean>;
161
+ }
162
+ /** The locale seat: register this bundle's namespace, bind a translator. */
163
+ export interface LocaleSeat {
164
+ register(namespace: string, dictionaries: {
165
+ zh: Record<string, string>;
166
+ en: Record<string, string>;
167
+ }): () => void;
168
+ bind(namespace: string): (key: string) => string;
169
+ }
170
+ /** The slice of the client context this plugin consumes. */
171
+ export interface ClientSeam {
172
+ slots: {
173
+ inject(name: string, callback: () => Generator<unknown, void, unknown>): unknown;
174
+ register(options: SlotRegisterOptions | RowSeatRegisterOptions | SectionRegisterOptions, component: (props: never) => unknown): () => void;
175
+ };
176
+ locale: LocaleSeat;
177
+ }
178
+ //# sourceMappingURL=seam.d.ts.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The adapter between the platform's client settings seat and this bundle's cards.
3
+ *
4
+ * One place maps `configForms.get(entryId)` to the three things a card needs — a
5
+ * snapshot, a subscription and the two write calls — so our own section's cards and the
6
+ * Plugins page's per-row entry cannot drift apart in how they read or write a row.
7
+ *
8
+ * Two invariants live here, both required by the renderer's hook seam:
9
+ *
10
+ * - **Stable references.** The renderer binds this source through
11
+ * `useSyncExternalStoreWithSelector`, which compares the projected snapshot by reference.
12
+ * A `getSnapshot()` that builds a new object on every call never compares equal, so the
13
+ * card re-renders until React aborts it (minified invariant #185) and the slot error
14
+ * boundary replaces the card with an empty placeholder. The projection therefore caches
15
+ * per raw snapshot reference.
16
+ * - **Never throws.** A throw from `getSnapshot()` happens inside the renderer's render pass,
17
+ * where this bundle cannot catch it and a card-level `try`/`catch` around a hook call is
18
+ * not legal React. Projection failures return a frozen unavailable snapshot instead.
19
+ *
20
+ * The seat itself is probed LAZILY: `apply()` runs before the settings shell may be active
21
+ * (the shell declares `settings.section` and provides `configForms`), so reading the seat at
22
+ * apply time would capture `undefined` for the whole life of the fiber.
23
+ * @module @lmzhen/dsh-evolution-ui/panels/settings/client
24
+ */
25
+ import type { ConfigFormLike, ConfigFormSnapshotLike, ConfigFormsSeat, ParamSectionSnapshot, ParamSectionSource, SettingsPathOpLike } from './seam.ts';
26
+ /** What a card shows when this UI found no settings seat at all. */
27
+ export declare const SEAT_MISSING: ParamSectionSnapshot;
28
+ /** What a card shows when the seat served the row but projecting it failed. A frozen constant
29
+ * so a failing row keeps ONE reference: a fresh object per call would reintroduce the
30
+ * render loop this module exists to prevent. */
31
+ export declare const PROJECTION_FAILED: ParamSectionSnapshot;
32
+ import { isRecord as isSection } from './guards.ts';
33
+ export { isSection };
34
+ /**
35
+ * Project one platform snapshot into the card's own shape.
36
+ * @param snapshot - the seat's snapshot.
37
+ * @returns the card's snapshot, with the reason an unavailable one is unavailable.
38
+ */
39
+ export declare function sectionSnapshotOf(snapshot: ConfigFormSnapshotLike): ParamSectionSnapshot;
40
+ /**
41
+ * Bind one row's form to a projection that is stable per raw snapshot and never throws.
42
+ * @param form - the row's client form, as the settings seat hands it over.
43
+ * @returns the projection the card's hook reads.
44
+ */
45
+ export declare function stableProjection(form: ConfigFormLike): () => ParamSectionSnapshot;
46
+ /**
47
+ * The lazily probed client settings seat.
48
+ *
49
+ * Probed at read time rather than captured at apply time: this row may activate before the
50
+ * settings shell, and a captured `undefined` would pin every card to the empty state.
51
+ * @returns the seat, or undefined while no settings surface is composed.
52
+ */
53
+ export type SeatProbe = () => ConfigFormsSeat | undefined;
54
+ /**
55
+ * The live source one row's card binds.
56
+ * @param probe - the lazily probed settings seat.
57
+ * @param namespace - the row's entry id.
58
+ * @returns the source; without a seat it reports the stated empty state, and it re-probes on
59
+ * every read so a seat that appears later is picked up without a timer.
60
+ */
61
+ export declare function sourceFor(probe: SeatProbe, namespace: string): ParamSectionSource;
62
+ /**
63
+ * The write pair for one row.
64
+ * @param probe - the lazily probed settings seat.
65
+ * @param namespace - the row's entry id.
66
+ * @returns the two calls a card's save/reset path uses; without a seat they resolve without writing.
67
+ */
68
+ export declare function writeFaceFor(probe: SeatProbe, namespace: string): {
69
+ mutateOnce: (ops: readonly SettingsPathOpLike[]) => Promise<boolean>;
70
+ clear: (field: string) => Promise<boolean>;
71
+ };
72
+ /**
73
+ * One source object per namespace, for the life of the plugin fiber.
74
+ *
75
+ * The renderer caches a hook binding per SOURCE OBJECT, so building a source per render
76
+ * would re-bind the hook and remount the card. The cache is owned by the caller's fiber and
77
+ * cleared on dispose.
78
+ * @param probe - the lazily probed settings seat.
79
+ * @returns the per-namespace source cache and its disposer.
80
+ */
81
+ export declare function createSourceCache(probe: SeatProbe): {
82
+ sourceFor: (namespace: string) => ParamSectionSource;
83
+ /** T5-11/A66: the seat arrived — wake every source this cache built (the cached ones are exactly the
84
+ * ones whose cards are already mounted and waiting). */
85
+ revive: () => void;
86
+ dispose: () => void;
87
+ };
88
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1,32 @@
1
+ /** Tag id that makes the injection idempotent. */
2
+ export declare const CSS_TAG_ID = "@lmzhen/dsh-evolution-ui/settings.css";
3
+ /** The stylesheet the section injects once. */
4
+ export declare const CSS: string;
5
+ /** The slice of the DOM the injection touches (this package typechecks without DOM lib). */
6
+ interface StyleHost {
7
+ querySelector(selector: string): unknown;
8
+ /** The element shape the injection uses. `remove()` is on the real DOM element and is what the
9
+ * S5-08 disposer calls, so the host shape names it too - otherwise the package cannot typecheck
10
+ * without the DOM lib while still using the disposal the browser offers. */
11
+ createElement(tag: string): {
12
+ dataset: Record<string, string>;
13
+ textContent: string;
14
+ remove(): void;
15
+ };
16
+ head: {
17
+ appendChild(node: unknown): void;
18
+ };
19
+ }
20
+ /**
21
+ * Inject the stylesheet once per document.
22
+ *
23
+ * S5-08: it RETURNS the disposer. The tag check alone answered only half the question - "is it
24
+ * there" - and left "who takes it away" to nobody, so a hot reload (unmount, remount) found the old
25
+ * tag, skipped the injection, and kept serving the PREVIOUS bundle CSS. skill-history hands its
26
+ * disposer back to the fiber; this is the same shape for the same concern.
27
+ * @param host - the document to inject into; defaults to the global one.
28
+ * @returns the disposal; a no-op when there is no document or the tag was already present.
29
+ */
30
+ export declare function injectStyles(host?: StyleHost): () => void;
31
+ export {};
32
+ //# sourceMappingURL=styles.d.ts.map