@t4r71/dsh-dual-axis-ui 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.
@@ -0,0 +1,161 @@
1
+ /** `settings.permission` namespace dictionaries (the Permission row's copy). */
2
+ /** Locale namespace shared by both current-session permission pickers. */
3
+ export declare const PERMISSION_ACCESS_NS = "permission.access";
4
+ /** Simplified Chinese dictionary (the key-set source of truth). */
5
+ export declare const zh: {
6
+ title: string;
7
+ description: string;
8
+ loading: string;
9
+ unavailable: string;
10
+ 'preset.readOnly': string;
11
+ 'preset.workspaceWrite': string;
12
+ 'preset.fullAccess': string;
13
+ };
14
+ /** The settings.permission namespace key union. */
15
+ export type PermissionSettingsKey = keyof typeof zh;
16
+ /** English dictionary, checked complete against the zh key set. */
17
+ export declare const en: {
18
+ title: string;
19
+ description: string;
20
+ loading: string;
21
+ unavailable: string;
22
+ 'preset.readOnly': string;
23
+ 'preset.workspaceWrite': string;
24
+ 'preset.fullAccess': string;
25
+ };
26
+ /**
27
+ * Simplified Chinese dictionary for the two current-session axis pickers. Each
28
+ * axis is a four-value dropdown (`deny / workspace / all / custom`), and the two
29
+ * dropdowns must stay unambiguous for assistive technology, for role-based
30
+ * locators, and for the eye: the two pickers sit side by side with no visible
31
+ * caption, so every value states its own axis in the copy itself. `custom` is the
32
+ * one shared value — "自定义" opens a path editor whose contents say which axis it
33
+ * belongs to, and the two pickers are already told apart by the other three.
34
+ */
35
+ export declare const accessZh: {
36
+ 'axis.read.deny': string;
37
+ 'axis.read.workspace': string;
38
+ 'axis.read.all': string;
39
+ 'axis.write.deny': string;
40
+ 'axis.write.workspace': string;
41
+ 'axis.write.all': string;
42
+ 'axis.custom': string;
43
+ 'axis.unknown': string;
44
+ 'editor.readTitle': string;
45
+ 'editor.writeTitle': string;
46
+ 'editor.base': string;
47
+ 'editor.readBase': string;
48
+ 'editor.writeBase': string;
49
+ 'editor.baseDeny': string;
50
+ 'editor.baseWorkspace': string;
51
+ 'editor.baseAll': string;
52
+ 'editor.allow': string;
53
+ 'editor.readAllow': string;
54
+ 'editor.writeAllow': string;
55
+ 'editor.deny': string;
56
+ 'editor.readDeny': string;
57
+ 'editor.writeDeny': string;
58
+ 'editor.hint': string;
59
+ 'editor.submit': string;
60
+ 'editor.sending': string;
61
+ 'editor.cancel': string;
62
+ 'editor.failed': string;
63
+ 'editor.notStored': string;
64
+ 'editor.problemBase': string;
65
+ 'editor.problemPath': string;
66
+ 'editor.problemComma': string;
67
+ 'editor.groups': string;
68
+ 'editor.groupBoth': string;
69
+ 'editor.groupRead': string;
70
+ 'editor.groupWrite': string;
71
+ 'editor.groupNone': string;
72
+ 'editor.groupMissing': string;
73
+ 'editor.groupsEmpty': string;
74
+ 'narrowing.title': string;
75
+ 'narrowing.body': string;
76
+ 'narrowing.removed': string;
77
+ 'narrowing.none': string;
78
+ 'narrowing.lostUnbounded': string;
79
+ 'narrowing.marker': string;
80
+ 'editor.problemGroupId': string;
81
+ readLabel: string;
82
+ writeLabel: string;
83
+ customSummary: string;
84
+ customAllow: string;
85
+ customDeny: string;
86
+ customNone: string;
87
+ close: string;
88
+ 'preset.readOnly': string;
89
+ 'preset.workspaceWrite': string;
90
+ 'preset.fullAccess': string;
91
+ 'confirm.title': string;
92
+ 'confirm.description': string;
93
+ 'confirm.acknowledge': string;
94
+ 'confirm.cancel': string;
95
+ 'confirm.enable': string;
96
+ };
97
+ /** Current-session axis-picker key union. */
98
+ export type PermissionAccessKey = keyof typeof accessZh;
99
+ /** English dictionary for the two current-session axis pickers. */
100
+ export declare const accessEn: {
101
+ 'axis.read.deny': string;
102
+ 'axis.read.workspace': string;
103
+ 'axis.read.all': string;
104
+ 'axis.write.deny': string;
105
+ 'axis.write.workspace': string;
106
+ 'axis.write.all': string;
107
+ 'axis.custom': string;
108
+ 'axis.unknown': string;
109
+ 'editor.readTitle': string;
110
+ 'editor.writeTitle': string;
111
+ 'editor.base': string;
112
+ 'editor.readBase': string;
113
+ 'editor.writeBase': string;
114
+ 'editor.baseDeny': string;
115
+ 'editor.baseWorkspace': string;
116
+ 'editor.baseAll': string;
117
+ 'editor.allow': string;
118
+ 'editor.readAllow': string;
119
+ 'editor.writeAllow': string;
120
+ 'editor.deny': string;
121
+ 'editor.readDeny': string;
122
+ 'editor.writeDeny': string;
123
+ 'editor.hint': string;
124
+ 'editor.submit': string;
125
+ 'editor.sending': string;
126
+ 'editor.cancel': string;
127
+ 'editor.failed': string;
128
+ 'editor.notStored': string;
129
+ 'editor.problemBase': string;
130
+ 'editor.problemPath': string;
131
+ 'editor.problemComma': string;
132
+ 'editor.groups': string;
133
+ 'editor.groupBoth': string;
134
+ 'editor.groupRead': string;
135
+ 'editor.groupWrite': string;
136
+ 'editor.groupNone': string;
137
+ 'editor.groupMissing': string;
138
+ 'editor.groupsEmpty': string;
139
+ 'narrowing.title': string;
140
+ 'narrowing.body': string;
141
+ 'narrowing.removed': string;
142
+ 'narrowing.none': string;
143
+ 'narrowing.lostUnbounded': string;
144
+ 'narrowing.marker': string;
145
+ 'editor.problemGroupId': string;
146
+ readLabel: string;
147
+ writeLabel: string;
148
+ customSummary: string;
149
+ customAllow: string;
150
+ customDeny: string;
151
+ customNone: string;
152
+ close: string;
153
+ 'preset.readOnly': string;
154
+ 'preset.workspaceWrite': string;
155
+ 'preset.fullAccess': string;
156
+ 'confirm.title': string;
157
+ 'confirm.description': string;
158
+ 'confirm.acknowledge': string;
159
+ 'confirm.cancel': string;
160
+ 'confirm.enable': string;
161
+ };
@@ -0,0 +1,167 @@
1
+ /**
2
+ * One access axis. The 0.1.7 workspace's permission domain exports only the
3
+ * whole-value preset catalog (`PermissionCatalog` / `PermissionSelection`), so
4
+ * the two-axis vocabulary is owned here rather than imported.
5
+ */
6
+ export type PermissionAxis = 'read' | 'write';
7
+ /** A selectable value on one axis. */
8
+ export type PermissionAxisValue = 'deny' | 'workspace' | 'all' | 'custom';
9
+ /**
10
+ * One axis as the host half publishes it on the wire: the kind, plus the custom
11
+ * detail when the kind is custom. The client's own model keeps only the kind,
12
+ * so this is the wire's JSON shape rather than a second axis vocabulary.
13
+ */
14
+ export interface DualAxisAxisWire {
15
+ kind: string;
16
+ base?: string;
17
+ /**
18
+ * Ids of the rule groups this axis references, in reference order.
19
+ *
20
+ * Declared here because the stored record carries them and the editor reads
21
+ * them back as its initial selection: omitting the member would make a group
22
+ * pick look like it never happened on the next open.
23
+ */
24
+ groups?: readonly string[];
25
+ allow?: readonly string[];
26
+ deny?: readonly string[];
27
+ }
28
+ /**
29
+ * The session's two axes, as the host half stores them in the settings document
30
+ * (`dual-axis-sessions`, partitioned by session id). Not a session projection
31
+ * value any more: the axes are outside the session log, so the shape here is the
32
+ * stored JSON rather than a wire view.
33
+ */
34
+ export interface DualAxisAxesWire {
35
+ readonly read: DualAxisAxisWire;
36
+ readonly write: DualAxisAxisWire;
37
+ /**
38
+ * What the read axis removed from the write range, as the HOST's resolution
39
+ * computed it (`resolveEffectiveAxes` in `@t4r71/dsh-dual-axis`), or
40
+ * `undefined` when the record carries no report — a record written before this
41
+ * member existed, or one the intersection left alone.
42
+ *
43
+ * Published rather than re-derived here: the host's resolution canonicalizes
44
+ * every root with `realpath` and derives a `workspace` base from
45
+ * `os.tmpdir()`, so a browser cannot reproduce its root list. A surface that
46
+ * reports a narrowing the fence does not enforce — or misses one it does — is
47
+ * worse than one that reports nothing.
48
+ */
49
+ readonly narrowing?: DualAxisNarrowingWire;
50
+ }
51
+ /**
52
+ * The host's report of what the read axis removed from the write range.
53
+ *
54
+ * Mirrors `WriteNarrowing` in `@t4r71/dsh-dual-axis/src/groups.ts`; that
55
+ * module is the producer and this is only the JSON spelling the document carries.
56
+ */
57
+ export interface DualAxisNarrowingWire {
58
+ /** Whether the read axis makes the effective write range strictly smaller. */
59
+ readonly narrowed: boolean;
60
+ /**
61
+ * Absolute roots the write axis permitted and the effective range does not,
62
+ * already canonicalized by the host.
63
+ */
64
+ readonly droppedRoots: readonly string[];
65
+ /** Whether an unbounded write axis became bounded, which no root list states. */
66
+ readonly lostUnbounded: boolean;
67
+ }
68
+ /**
69
+ * Narrow one axis's wire kind to a value the dropdowns can show.
70
+ * @param kind - the kind field as it arrives on the wire.
71
+ * @returns one of the four values, or undefined when the kind is unrecognized.
72
+ */
73
+ export declare function axisValueOf(kind: string): PermissionAxisValue | undefined;
74
+ /**
75
+ * Locale dictionary key for one axis value's label. Every value states its own
76
+ * axis except `custom`, whose editor says which axis it belongs to — the two
77
+ * pickers have no visible caption beside them, so the copy has to carry it.
78
+ */
79
+ export type AxisLabelKey = 'axis.read.deny' | 'axis.read.workspace' | 'axis.read.all' | 'axis.write.deny' | 'axis.write.workspace' | 'axis.write.all' | 'axis.custom' | 'axis.unknown';
80
+ /** Locale dictionary key for a built-in permission preset label. */
81
+ export type PermissionPresetLabelKey = 'preset.readOnly' | 'preset.workspaceWrite' | 'preset.fullAccess';
82
+ /**
83
+ * Render one axis value under its product label. The axis is required because
84
+ * the two pickers share no visible caption, so the label itself says which axis
85
+ * it belongs to ("工作区读" under the read picker, "工作区写" under the write one).
86
+ * @param value - the axis value.
87
+ * @param axis - the axis the value belongs to, which selects the copy.
88
+ * @param t - optional locale dictionary lookup for the built-in labels.
89
+ * @returns the localized label, or the English label without a locale seat.
90
+ */
91
+ export declare function axisLabel(value: PermissionAxisValue, axis: PermissionAxis, t?: (key: AxisLabelKey) => string): string;
92
+ /**
93
+ * The NAME half of one dropdown's trigger label: one word per value.
94
+ *
95
+ * A `custom` axis renders as the bare word and nothing else. It used to append
96
+ * its base and group count, which is how a group pick on an already-custom axis
97
+ * became visible; the trigger does not have room for that text, and the editor a
98
+ * `custom` pick opens states the base, the groups and both path lists in full,
99
+ * so the detail lives there instead of on the trigger. Nothing else observes the
100
+ * difference: a submission is judged by the stored record the read-back
101
+ * compares against, never by the trigger's text.
102
+ *
103
+ * An absent value (no stored record, or a kind this build does not know) says so
104
+ * instead of borrowing a value: the two dropdowns must render even when nothing
105
+ * can be claimed about the axis, and "未设置" is the honest label for that.
106
+ * @param value - the axis value to name, or `undefined` when there is none.
107
+ * @param axis - which axis this name belongs to.
108
+ * @param t - locale lookup for the label.
109
+ * @returns the name to interpolate into the trigger's `readLabel`/`writeLabel`.
110
+ */
111
+ export declare function axisValueName(value: PermissionAxisValue | undefined, axis: PermissionAxis, t: (key: AxisLabelKey) => string): string;
112
+ /**
113
+ * One line of the widening report that sits beside the write axis, or nothing to
114
+ * render.
115
+ *
116
+ * The content comes from the HOST's resolution of the same inputs — the roots it
117
+ * names are the roots it will refuse — so this function only decides how to spell
118
+ * them, never whether the narrowing happened. An absent report and a report that
119
+ * removed nothing both yield `undefined`: the notice appears exactly when the
120
+ * fence is narrower than the write axis.
121
+ */
122
+ export interface NarrowingNotice {
123
+ /** The heading, naming the invariant. */
124
+ readonly title: string;
125
+ /** What the intersection removed, in the roots' own spelling. */
126
+ readonly removed: string;
127
+ /** The unbounded-to-bounded sentence, or empty when the roots carry the loss. */
128
+ readonly lostUnbounded: string;
129
+ /**
130
+ * Whether the write trigger's corner marker is shown. It is the only part of
131
+ * the report visible with the editor closed, and it carries no text: the
132
+ * trigger's label is the picked value and nothing else.
133
+ */
134
+ readonly marked: boolean;
135
+ /**
136
+ * The marker's accessible name — the whole report in one sentence, since the
137
+ * marker itself has no room and no text. Built HERE rather than at the call
138
+ * site so the sentences that need parameters are interpolated with them: a
139
+ * caller that forwards a key-only lookup leaves literal placeholders on screen.
140
+ */
141
+ readonly marker: string;
142
+ }
143
+ /** The locale lookups one notice needs; the dictionary itself owns the copy. */
144
+ export type NarrowingText = (key: NarrowingKey, params?: Record<string, string>) => string;
145
+ /** Dictionary keys the notice is built from. */
146
+ export type NarrowingKey = 'narrowing.title' | 'narrowing.body' | 'narrowing.removed' | 'narrowing.none' | 'narrowing.lostUnbounded' | 'narrowing.marker';
147
+ /**
148
+ * Build the write axis's narrowing notice.
149
+ * @param narrowing - the host's report, or `undefined` when there is none.
150
+ * @param t - locale lookup for every sentence.
151
+ * @returns the notice to render, or `undefined` when nothing was removed.
152
+ */
153
+ export declare function narrowingNotice(narrowing: DualAxisNarrowingWire | undefined, t: NarrowingText): NarrowingNotice | undefined;
154
+ /**
155
+ * Convert conventional kebab-case preset names into user-facing title case.
156
+ * @param name - host-supplied preset label or key.
157
+ * @returns the title-cased conventional key, or a non-kebab label unchanged.
158
+ */
159
+ export declare function displayPresetName(name: string): string;
160
+ /**
161
+ * Render a permission preset under its product label.
162
+ * @param value - preset machine value.
163
+ * @param name - host-supplied preset name.
164
+ * @param t - optional locale dictionary lookup for built-in product labels.
165
+ * @returns the localized built-in label, or the host's own name VERBATIM.
166
+ */
167
+ export declare function displayPermissionPreset(value: string, name: string, t?: (key: PermissionPresetLabelKey) => string): string;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * 会话轴在配置文档里的形状,以及按会话 id 取值的纯读取。
3
+ *
4
+ * 单独一个模块,唯一的理由是它必须能在**不被 store 依赖污染**的程序里被直接测到:
5
+ * 本包的宿主半边把它存进自有的设置命名空间 `dual-axis-sessions`,字段名与结构是
6
+ * 两边之间的接口,改一处就要改另一处。订阅与快照那一层在 `./session-axes.ts`。
7
+ *
8
+ * 这里只管**文档里有没有这条会话的记录**。「没有记录时该显示哪一对」是另一件事,
9
+ * 由 `./session-axes-seed.ts` 按与宿主同一份计算给出 —— 那一对是设置页那一行
10
+ * (加上子代理继承),不是内置默认对。
11
+ *
12
+ * @module @deepseek-ai/dsh-client-ui-permission-presets/client/session-axes-data
13
+ */
14
+ import type { DualAxisAxesWire, DualAxisNarrowingWire } from './presentation.ts';
15
+ export { SESSION_AXES_FALLBACK, sessionAxesSeed } from './session-axes-seed.ts';
16
+ export type { SeededAxes, SeededAxis } from './session-axes-seed.ts';
17
+ /** 宿主存轴用的设置命名空间(Loader entry id)。 */
18
+ export declare const SESSION_AXES_NAMESPACE = "dual-axis-sessions";
19
+ /** 设置页那一行的设置命名空间(Loader entry id):新会话的**种子**来源。 */
20
+ export declare const DUAL_AXIS_ROW_NAMESPACE = "dual-axis";
21
+ /** 该命名空间里那个按会话 id 分区的字段名。 */
22
+ export declare const SESSION_AXES_FIELD = "axes";
23
+ /** 快照里本命名空间那一行 —— 只声明这里真的会读的两个成员。 */
24
+ export interface SessionAxesSectionView {
25
+ readonly ns: string;
26
+ readonly value: unknown;
27
+ }
28
+ /**
29
+ * 从一条记录里读出宿主发布的收窄结论。
30
+ *
31
+ * 未信任输入:文档可能被人手改过,也可能带着旧版本写下的、没有这个成员的记录。
32
+ * 前者读不懂就**丢掉**(当作没有收窄可显示),而不是把一份读不出来的报告渲染成
33
+ * 「什么也没被削」—— 那正是最坏的那一种错。后者(成员整个缺席)本来就是「没有收窄」,
34
+ * 与宿主围栏在那种记录下执行的范围一致。
35
+ * @param value - 记录里的 `narrowing` 成员,未信任。
36
+ * @returns 收窄结论,或 undefined(缺席 / 读不懂 / 两侧都说没有)。
37
+ */
38
+ export declare function narrowingOf(value: unknown): DualAxisNarrowingWire | undefined;
39
+ /**
40
+ * 一份 describe 快照里,本命名空间那一行的取值。
41
+ * @param namespaces - 快照里的全部命名空间行,或 undefined(还没有读到答案)。
42
+ * @returns 那一行的取值,或 undefined(宿主没服务这一节)。
43
+ */
44
+ export declare function sessionAxesSectionValue(namespaces: readonly SessionAxesSectionView[] | undefined): unknown;
45
+ /**
46
+ * 一份 describe 快照里,设置页那一行(`dual-axis`)的取值 —— 新会话的种子。
47
+ *
48
+ * 它与 {@link sessionAxesSectionValue} 是**两节**,不能混:那一行的 read/write/defaultGroups
49
+ * 描述的是**将要建立**的会话,判定路径不读它;而「这条会话没有记录」时判定路径执行的
50
+ * 是种子算出来的那一对,所以界面必须能读到同一行才能显示同一个值。
51
+ * @param namespaces - 快照里的全部命名空间行,或 undefined。
52
+ * @returns 那一行的取值,或 undefined(宿主没服务这一节)。
53
+ */
54
+ export declare function sessionAxesRowValue(namespaces: readonly SessionAxesSectionView[] | undefined): unknown;
55
+ /**
56
+ * 按会话 id 从一份文档快照里取出那一对轴。
57
+ *
58
+ * 未信任输入:文档可能被人手改过、也可能由别的写入方改过,所以只在形状真的读得懂时才
59
+ * 返回取值;读不懂时返回 `undefined`,由 {@link sessionAxesView} 决定那是「照种子
60
+ * 显示」还是「无从得知」。这里**不抛**:一个读不懂的会话轴不该让整个输入条崩掉。
61
+ * @param value - 本命名空间那一行的取值。
62
+ * @param sessionId - 要读的那条会话。
63
+ * @returns 那条会话的轴对,或 undefined。
64
+ */
65
+ export declare function sessionAxesOf(value: unknown, sessionId: string | undefined): DualAxisAxesWire | undefined;
66
+ /**
67
+ * What the two dropdowns display for one session, and why.
68
+ *
69
+ * Three states, because two of them look alike and mean different things:
70
+ *
71
+ * - `record` — the document carries this session's pair. This is the ordinary
72
+ * state and the only one that has an answer of its own.
73
+ * - `default` — the document was read and does NOT carry this session. The host
74
+ * is holding the session to its SEED right now — the pair `pin` pins a new
75
+ * session with, recomputed by {@link sessionAxesSeed} from the settings row —
76
+ * so that is what is shown. Showing nothing here is the defect this type exists
77
+ * to prevent: it reads to a person as "the feature is gone". Showing
78
+ * `SESSION_AXES_FALLBACK` here instead would be the OTHER defect: it labels the
79
+ * axis with a range the host does not enforce.
80
+ * - `unknown` — there is nothing to read: no session is selected, or the settings
81
+ * mirror has not delivered a document yet. No value can be claimed to agree
82
+ * with the host, so the dropdowns say so instead of inventing one.
83
+ */
84
+ export type SessionAxesView = {
85
+ readonly state: 'record';
86
+ readonly axes: DualAxisAxesWire;
87
+ } | {
88
+ readonly state: 'default';
89
+ readonly axes: DualAxisAxesWire;
90
+ } | {
91
+ readonly state: 'unknown';
92
+ };
93
+ /**
94
+ * Resolve what the two dropdowns show for one session.
95
+ *
96
+ * Never returns nothing and never throws: an unreadable document, an unknown
97
+ * session id, and a missing record each have a state of their own.
98
+ * @param namespaces - every namespace row of one describe snapshot, or `undefined`
99
+ * when the mirror has not delivered a document (which is NOT the same as a
100
+ * document that carries no record for this session).
101
+ * @param sessionId - the session to read; `undefined` when none is selected.
102
+ * @param parentSessionId - the session's parent when it is a subagent child, which
103
+ * changes the seed: a child inherits its parent's STORED pair rather than the
104
+ * settings row, exactly as `inheritedAxes` does on the host. `undefined` for a
105
+ * top-level session (and for a child whose lineage the client has not learned).
106
+ * @returns the view the dropdowns render from.
107
+ */
108
+ export declare function sessionAxesView(namespaces: readonly SessionAxesSectionView[] | undefined, sessionId: string | undefined, parentSessionId?: string | undefined): SessionAxesView;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * 记录缺席时两个下拉该显示的那一对轴:宿主 `seedPair` 的逐字镜像。
3
+ *
4
+ * 宿主那一份在 `@t4r71/dsh-dual-axis` 的 `src/session-store.ts`,是
5
+ * `seedAxesFor` 的纯核:设置页那一行(`read` / `write` / `defaultGroups`),
6
+ * 加上子代理子会话从**父会话当刻的记录**继承来的那一对,折成「这条会话在新存储里
7
+ * 还没有记录时被钉下、也被判定路径执行的那一对」。记录落地之前,活读路径(模型提示词、
8
+ * 读围栏、`/axis` 的兜底)交回的就是它,所以界面必须显示同一个值 —— 显示别的就是
9
+ * 「界面显示 A、宿主执行 B」:设置页默认若是「禁止读」,那一轮仍然被当成「全盘读」。
10
+ *
11
+ * 为什么另起一个模块:它必须能在**零依赖**的程序里被直接加载,好让宿主包的
12
+ * `tests/seed-parity.spec.ts` 把两侧喂同一组输入、断言输出逐字相等(这是防止两份
13
+ * 实现以后各自漂移的唯一手段)。因此本文件不 import 任何东西,下面每一步都与宿主那
14
+ * 一份一一对应:取值集合、校验顺序、键的插入顺序、连报错条件都一致。
15
+ *
16
+ * 与 `./session-axes-data.ts` 的分工:那边负责「文档里有没有这条会话的记录」,
17
+ * 这边只负责「没有记录时那一对该是什么」。
18
+ *
19
+ * @module @deepseek-ai/dsh-client-ui-permission-presets/client/session-axes-seed
20
+ */
21
+ /** 一条轴在文档里的拼写:闭合取值只有 `kind`。 */
22
+ export interface SeededAxis {
23
+ /** 四种取值之一:`deny` / `workspace` / `all` / `custom`。 */
24
+ readonly kind: string;
25
+ /** `custom` 的比较底座。 */
26
+ readonly base?: string;
27
+ /** `custom` 引用的规则组 id。 */
28
+ readonly groups?: readonly string[];
29
+ /** `custom` 在底座之上追加的绝对路径。 */
30
+ readonly allow?: readonly string[];
31
+ /** `custom` 从(底座 ∪ allow)里排除的绝对路径。 */
32
+ readonly deny?: readonly string[];
33
+ }
34
+ /** 一条会话的一对轴。 */
35
+ export interface SeededAxes {
36
+ /** 读轴。 */
37
+ readonly read: SeededAxis;
38
+ /** 写轴。 */
39
+ readonly write: SeededAxis;
40
+ }
41
+ /**
42
+ * 读不懂任何输入时的那一对:宿主 `DEFAULT_AXES` 的同一对取值
43
+ * (`packages/bundle/dual-axis/src/axis.ts` 的 `DEFAULT_READ_SCOPE` /
44
+ * `DEFAULT_WRITE_SCOPE`)。
45
+ *
46
+ * 它**不再**是「会话没有记录」时的答案 —— 那是 {@link sessionAxesSeed} 算出来的
47
+ * 种子。这一对只在设置页那一行整个读不懂时出现,而宿主在那同一种情形下交回的也正是
48
+ * 它(`seedPair` 的 catch 与 `ensure` 的 catch 都答 `DEFAULT_AXES`)。
49
+ */
50
+ export declare const SESSION_AXES_FALLBACK: SeededAxes;
51
+ /**
52
+ * 记录缺席时该显示的那一对轴 —— 与宿主 `seedPair` 同一份计算、同一个兜底。
53
+ *
54
+ * 永不抛:设置页那一行读不懂、父会话记录读不懂,一律答
55
+ * {@link SESSION_AXES_FALLBACK},与宿主在同一种输入上的答案相同。
56
+ * @param row - 设置页那一行(`dual-axis` 命名空间)的取值,未信任。
57
+ * @param inherited - 子代理子会话从父会话记录继承来的那一对,或 `undefined`。
58
+ * @returns 这条会话此刻被钉下、也被被执行的那一对轴。
59
+ */
60
+ export declare function sessionAxesSeed(row: unknown, inherited: SeededAxes | undefined): SeededAxes;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 会话轴在本包浏览器半边的取值面:从共享的 settings describe 镜像里,按**会话 id**
3
+ * 读出这条会话的那一对轴。
4
+ *
5
+ * 轴不再随会话日志下发(宿主半边把它存进自有的设置命名空间 `dual-axis-sessions`,
6
+ * 见 `@t4r71/dsh-dual-axis/session-store`),于是客户端唯一的取数通道就是那份
7
+ * 配置文档的镜像。变更通知因此是免费的:宿主写文档后发 `settings/document-updated`,
8
+ * 该事件在转发白名单上,共享镜像已订阅它并重读(`ui-settings/src/client/index.ts:43`),
9
+ * 本目录只是跟着镜像的快照走。
10
+ *
11
+ * 读的是**另一个命名空间**里的按会话分区字段,不是设置页那一行
12
+ * (`dual-axis`):那一行的 read/write/defaultGroups 是新会话的种子,已经在跑的
13
+ * 会话只认自己的记录。但**记录缺席**时两者交汇:那时宿主执行的正是那一行算出来的
14
+ * 种子(见 `./session-axes-seed.ts`),所以本目录在没有记录时也要读那一行,才能显示
15
+ * 与宿主同一个值。分组库仍由那一行的表单拥有,见 `./axis-editor.ts`。
16
+ *
17
+ * @module @deepseek-ai/dsh-client-ui-permission-presets/client/session-axes
18
+ */
19
+ import type { SettingsDescribeFace, SettingsMirrorSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
20
+ import type { SessionAxesView } from './session-axes-data.ts';
21
+ /**
22
+ * 一条会话的父会话 id,子代理子会话才有。
23
+ *
24
+ * 由组合层注入:子会话的种子取父会话**当刻的记录**,所以界面必须知道这条会话是不是
25
+ * 子会话、父是谁。取不到(不是子会话、或本进程还没学到这条血缘)时返回 undefined,
26
+ * 那时种子退回设置页那一行 —— 与宿主「父会话没有记录时」同一条分支。
27
+ */
28
+ export type SessionAxesParentOf = (sessionId: string) => string | undefined;
29
+ /**
30
+ * 这条会话该显示的那一对轴,以及那份取值的来源。
31
+ *
32
+ * 返回的永远是三态而不是「有/没有」:没有记录与读不到文档是两件事,前者宿主当刻执行的
33
+ * 就是内置默认对(所以照它显示),后者没有任何可声称与宿主一致的值(所以如实说不知道)。
34
+ * 见 `./session-axes-data.ts` 的 `SessionAxesView`。
35
+ */
36
+ export type SessionAxesReader = (sessionId: string | undefined) => SessionAxesView;
37
+ /**
38
+ * 会话轴目录:把共享镜像的快照换成一个能直接喂给 `useSyncExternalStore` 的稳定值。
39
+ *
40
+ * 快照在这里重新发布,而不是把镜像的快照原样透出去:镜像每次重读都换一个对象,
41
+ * 而重渲染的代价只有在这一层做一次比较才能省掉。比较是序列化比较 —— 镜像只在这份
42
+ * 文档真的变了之后才发布(宿主只在 entry 的 raw 变化时发 `settings/document-updated`,
43
+ * 见 `settings/src/index.ts:311-317`),所以那次序列化每次文档变更只做一次。
44
+ */
45
+ export declare class SessionAxesDirectory {
46
+ private readonly describeFace;
47
+ private readonly parentOf;
48
+ private readonly local;
49
+ private readonly following;
50
+ /**
51
+ * @param describeFace - 共享的 describe 镜像(`ctx.configForms.describe()`)。
52
+ * @param parentOf - 这条会话的父会话 id;默认「不是子会话」,于是种子只按设置页那一行算。
53
+ */
54
+ constructor(describeFace: SettingsDescribeFace, parentOf?: SessionAxesParentOf);
55
+ /** 稳定快照,直接交给 `useSyncExternalStore`。 */
56
+ readonly getSnapshot: () => SettingsMirrorSnapshot;
57
+ /** 订阅本地快照的替换。 */
58
+ readonly subscribe: (listener: () => void) => (() => void);
59
+ /**
60
+ * 一条会话当刻该显示的轴。同步,供编辑器取初值与提交后的核对使用。
61
+ * @param sessionId - 当前会话 id;没有选中会话时是 undefined。
62
+ * @returns 三态取值:有记录 / 照这条会话的种子显示 / 无从得知。
63
+ */
64
+ readonly axesOf: SessionAxesReader;
65
+ /** 停掉对镜像的跟随;之后的发布不再进入本目录。 */
66
+ dispose(): void;
67
+ private fold;
68
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Permission default-settings controller. 权限描述符来自共享的 describe 镜像,可选
3
+ * 取值来自进程级 {@link PermissionCatalogDirectory}(0.1.7 起动态预设枚举不再从
4
+ * namespace schema 里挖,改由权限目录的 `defaultOptions` 提供);写入只针对
5
+ * `defaultPreset`,带上描述符 revision,并把宿主接受的答案折回镜像。
6
+ */
7
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
8
+ import type { SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client';
9
+ import { type SnapshotStore } from '@deepseek-ai/dsh-client-store';
10
+ import type { SettingsDescribeFace } from '@deepseek-ai/dsh-client-ui-settings/client';
11
+ import type { PermissionCatalog } from '@deepseek-ai/dsh-permission-presets/client';
12
+ import type { PermissionCatalogDirectory } from './catalog.ts';
13
+ /** Permission's settings namespace on the host wire. */
14
+ export declare const PERMISSION_SETTINGS_NS = "permission";
15
+ /** One selectable new-session default. */
16
+ export interface PermissionDefaultOption {
17
+ /** Preset key written to Settings. */
18
+ id: string;
19
+ /** Host-supplied label or a title-cased preset key. */
20
+ label: string;
21
+ }
22
+ /** Permission settings-row snapshot. */
23
+ export interface PermissionSettingsState {
24
+ status: 'idle' | 'loading' | 'ready' | 'saving' | 'unavailable' | 'error';
25
+ error: string | null;
26
+ writable: boolean;
27
+ currentValue: string;
28
+ options: readonly PermissionDefaultOption[];
29
+ revision: number;
30
+ }
31
+ /**
32
+ * Resolve the new-session choices from the permission domain's catalog.
33
+ * @param view - 当前配置描述符。
34
+ * @param catalog - 权限目录:可选预设与生效默认值。
35
+ * @returns 当前取值,以及它允许的那些取值的标签。
36
+ */
37
+ export declare function permissionDefaultOf(view: SettingsNamespaceView, catalog: PermissionCatalog): {
38
+ currentValue: string;
39
+ options: PermissionDefaultOption[];
40
+ };
41
+ /** Controller deriving the row from the shared mirror and writing the default through it. */
42
+ export declare class PermissionPresetSettingsController {
43
+ private readonly describeFace;
44
+ private readonly ctx;
45
+ private readonly catalog;
46
+ /** Row snapshot consumed through a bound selector hook. */
47
+ readonly store: SnapshotStore<PermissionSettingsState>;
48
+ private following;
49
+ private followingCatalog;
50
+ private saving;
51
+ private disposed;
52
+ /**
53
+ * @param describeFace - the shared mirror's read/fold face (descriptor and schema source).
54
+ * @param ctx - the row plugin's context, whose `remote.settings` namespace
55
+ * carries the `defaultPreset` write.
56
+ * @param catalog - 已配置的权限可选值与生效默认值(进程级目录,与斜杠弹窗共用一份)。
57
+ */
58
+ constructor(describeFace: SettingsDescribeFace, ctx: ClientContext, catalog: Pick<PermissionCatalogDirectory, 'store' | 'load'>);
59
+ /**
60
+ * Begin following the mirror (idempotent) and reflect its current answer.
61
+ * @returns settlement once the snapshot reflects the mirror.
62
+ */
63
+ load(): Promise<void>;
64
+ /**
65
+ * Persist one preset as the default for subsequently created sessions.
66
+ * A selection made while one is already saving is ignored — the row's
67
+ * control is disabled during the save, so this only drops programmatic
68
+ * double-submits rather than user intent.
69
+ * @param preset - advertised preset key.
70
+ * @returns nothing; {@link store} carries success or failure.
71
+ */
72
+ select(preset: string): Promise<void>;
73
+ /** Stop following the mirror; later publishes leave the snapshot alone. */
74
+ dispose(): void;
75
+ private derive;
76
+ private fail;
77
+ }