@qilin-agent/client-ui-settings 0.0.0-stage → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +58 -0
- package/README.md +112 -2
- package/README.zh.md +113 -0
- package/lib/client.js +1545 -0
- package/lib/index.js +20 -0
- package/lib/types/client/config-form-types.d.ts +75 -0
- package/lib/types/client/config-form.d.ts +159 -0
- package/lib/types/client/contract/slots.d.ts +155 -0
- package/lib/types/client/developer-tools.d.ts +22 -0
- package/lib/types/client/index.d.ts +36 -0
- package/lib/types/client/schema.d.ts +72 -0
- package/lib/types/client/settings-mirror.d.ts +118 -0
- package/lib/types/developer-tools-settings.d.ts +16 -0
- package/lib/types/index.d.ts +6 -0
- package/package.json +63 -3
package/lib/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import z from "@qilin-agent/schemastery";
|
|
2
|
+
//#region lib/types/developer-tools-settings.js
|
|
3
|
+
/** Shared developer-tool preference stored by the Host user-settings document. */
|
|
4
|
+
/** Namespace for developer UI and HTML preview capabilities. */
|
|
5
|
+
const DEVELOPER_TOOLS_NAMESPACE = "ui-settings";
|
|
6
|
+
/** New installations and missing values enable the full interface. */
|
|
7
|
+
const DeveloperToolsSettingsFields = { enabled: z.boolean().default(true) };
|
|
8
|
+
/** Durable developer-tools schema; also the wire envelope the browser scope validates against. */
|
|
9
|
+
const DeveloperToolsSettingsSchema = z.object({ enabled: DeveloperToolsSettingsFields["enabled"] });
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region lib/types/index.js
|
|
12
|
+
/** Host registration for the shared developer-tool preference. */
|
|
13
|
+
/** Register the durable developer-tools section when a provider exists. */
|
|
14
|
+
function apply(ctx) {
|
|
15
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
16
|
+
settingsCtx.settings.register(DEVELOPER_TOOLS_NAMESPACE, DeveloperToolsSettingsSchema);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { DEVELOPER_TOOLS_NAMESPACE, apply };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client configuration values and atomic write operations.
|
|
3
|
+
*/
|
|
4
|
+
import type { SettingsPathOpView } from '@qilin-agent/api-remotes/client';
|
|
5
|
+
/** Client-side sync state of one settings namespace. */
|
|
6
|
+
export interface ConfigFormSnapshot<T> {
|
|
7
|
+
/**
|
|
8
|
+
* `loading` until the first accepted section, `ready` while one stands, and
|
|
9
|
+
* `unavailable` when the namespace is not exposed to this client or the
|
|
10
|
+
* connection keeps preferences process-local (memory mode).
|
|
11
|
+
*/
|
|
12
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
13
|
+
/** Last accepted schema-resolved section; undefined before the first acceptance. */
|
|
14
|
+
value: T | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Composition layer the Host resolved {@link value} over, when the owning
|
|
17
|
+
* plugin declared one. What a field reverts to once cleared.
|
|
18
|
+
*/
|
|
19
|
+
base: unknown;
|
|
20
|
+
/**
|
|
21
|
+
* Raw user layer as stored, when one exists. A field's PRESENCE here is what
|
|
22
|
+
* marks it overridden — an override whose value equals the composition
|
|
23
|
+
* default is still an override, and comparing values could not see it.
|
|
24
|
+
*/
|
|
25
|
+
user: unknown;
|
|
26
|
+
/** Namespace revision fencing the next write; undefined before the first Host view. */
|
|
27
|
+
revision: number | undefined;
|
|
28
|
+
/** Whether the Host document accepts writes; memory mode never does. */
|
|
29
|
+
writable: boolean;
|
|
30
|
+
/** `host` syncs with the Host document; `memory` keeps a remote browser process-local. */
|
|
31
|
+
mode: 'host' | 'memory';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Accepted values and serialized writes shared by editors of one Host entry.
|
|
35
|
+
*/
|
|
36
|
+
export interface ConfigForm<T> {
|
|
37
|
+
/** @returns the current sync snapshot (stable reference until the next change). */
|
|
38
|
+
getSnapshot(): ConfigFormSnapshot<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Observe snapshot replacements.
|
|
41
|
+
* @param listener - invoked after each snapshot change.
|
|
42
|
+
* @returns the disposer removing this listener.
|
|
43
|
+
*/
|
|
44
|
+
subscribe(listener: () => void): () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Queue one atomic namespace mutation. All operations share one revision
|
|
47
|
+
* fence, Host validation, persistence decision, and recovery read. Supplying
|
|
48
|
+
* `expectedRevision` preserves an earlier read as the fence instead of using
|
|
49
|
+
* the latest queued or mirrored revision.
|
|
50
|
+
* @param ops - ordered field operations copied when queued.
|
|
51
|
+
* @param expectedRevision - optional fixed revision read by the domain editor.
|
|
52
|
+
* @returns true for Host acceptance, false for refusal or skipped writes, after any latest-write recovery.
|
|
53
|
+
* Transport failures reject.
|
|
54
|
+
*/
|
|
55
|
+
mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Queue one field write. Rapid writes preserve mutation order, each carries
|
|
58
|
+
* the latest known namespace revision, and only the latest settlement may
|
|
59
|
+
* publish; a Host-refused latest write reloads Host state instead.
|
|
60
|
+
* @param field - scalar field inside the namespace section.
|
|
61
|
+
* @param value - JSON-shaped value selected by the user.
|
|
62
|
+
* @returns true for Host acceptance, false for refusal or skipped writes, after any latest-write recovery.
|
|
63
|
+
* Transport failures reject.
|
|
64
|
+
*/
|
|
65
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Queue one field clear, so the field re-inherits the composition layer.
|
|
68
|
+
* Shares {@link set}'s ordering, revision, and recovery contract.
|
|
69
|
+
* @param field - scalar field inside the namespace section.
|
|
70
|
+
* @returns true for Host acceptance, false for refusal or skipped writes, after any latest-write recovery.
|
|
71
|
+
* Transport failures reject.
|
|
72
|
+
*/
|
|
73
|
+
unset(field: string): Promise<boolean>;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=config-form-types.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/** Shared entry values and ordered writes over the Host configuration mirror. */
|
|
2
|
+
import { DeveloperToolsPreference } from './developer-tools.ts';
|
|
3
|
+
import { Service } from '@qilin-agent/kylin';
|
|
4
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
5
|
+
import type { SettingsPathOpView } from '@qilin-agent/api-remotes/client';
|
|
6
|
+
import type { SettingsSchemaService } from './schema.ts';
|
|
7
|
+
import type { ConfigForm, ConfigFormSnapshot } from './config-form-types.ts';
|
|
8
|
+
import { SettingsDescribeMirror, type SettingsDescribeFace } from './settings-mirror.ts';
|
|
9
|
+
/** Domain-owned description of one settings namespace consumed by a browser plugin. */
|
|
10
|
+
interface ConfigFormSpec<T> {
|
|
11
|
+
/** Settings namespace registered by the owning Host plugin. */
|
|
12
|
+
namespace: string;
|
|
13
|
+
/**
|
|
14
|
+
* Narrow one wire section; undefined keeps the last accepted value. The
|
|
15
|
+
* default validates the section against the namespace's own serialized wire
|
|
16
|
+
* schema, so domains add a decoder only to narrow beyond that schema.
|
|
17
|
+
*/
|
|
18
|
+
decode?: (section: unknown) => T | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One namespace's derived view over the shared describe mirror, plus that
|
|
22
|
+
* namespace's serialized Host writes. Writes carry the latest known namespace
|
|
23
|
+
* revision, fold their answers back into the mirror, and teardown waits for
|
|
24
|
+
* the operation already crossing the wire.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ConfigFormController<T> implements ConfigForm<T> {
|
|
27
|
+
private readonly ctx;
|
|
28
|
+
private readonly spec;
|
|
29
|
+
private readonly mirror;
|
|
30
|
+
private readonly persistence;
|
|
31
|
+
private readonly schema;
|
|
32
|
+
private readonly store;
|
|
33
|
+
private tail;
|
|
34
|
+
private writeGeneration;
|
|
35
|
+
private disposed;
|
|
36
|
+
private readonly unsubscribe;
|
|
37
|
+
/**
|
|
38
|
+
* Revision answered by a superseded write still ahead of the mirror: the
|
|
39
|
+
* mirror only folds the LATEST settlement in, so a queued successor takes
|
|
40
|
+
* its fence from here first.
|
|
41
|
+
*/
|
|
42
|
+
private pendingRevision;
|
|
43
|
+
/**
|
|
44
|
+
* @param ctx - the providing plugin's context, whose `remote.settings`
|
|
45
|
+
* namespace carries this form's writes (reads ride the mirror).
|
|
46
|
+
* @param spec - namespace identity and optional narrowing decoder.
|
|
47
|
+
* @param mirror - the shared describe mirror this form derives from.
|
|
48
|
+
* @param persistence - client-selected Host persistence; non-loopback pages may remain process-local.
|
|
49
|
+
* @param schema - settings-owned schema operations.
|
|
50
|
+
*/
|
|
51
|
+
constructor(ctx: Context, spec: ConfigFormSpec<T>, mirror: SettingsDescribeMirror, persistence: 'host' | 'memory', schema: SettingsSchemaService);
|
|
52
|
+
/** @returns the current sync snapshot (stable reference until the next change). */
|
|
53
|
+
getSnapshot(): ConfigFormSnapshot<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Observe snapshot replacements.
|
|
56
|
+
* @param listener - invoked after each snapshot change.
|
|
57
|
+
* @returns the disposer removing this listener.
|
|
58
|
+
*/
|
|
59
|
+
subscribe(listener: () => void): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Queue one field write; see {@link ConfigForm.set} for the ordering,
|
|
62
|
+
* revision, and recovery contract.
|
|
63
|
+
* @param field - scalar field inside the namespace section.
|
|
64
|
+
* @param value - JSON-shaped value selected by the user.
|
|
65
|
+
* @returns whether the Host accepted the write, after any recovery read.
|
|
66
|
+
*/
|
|
67
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Queue one field clear; see {@link ConfigForm.unset} for the ordering,
|
|
70
|
+
* revision, and recovery contract.
|
|
71
|
+
* @param field - scalar field inside the namespace section.
|
|
72
|
+
* @returns whether the Host accepted the clear, after any recovery read.
|
|
73
|
+
*/
|
|
74
|
+
unset(field: string): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Queue one atomic namespace mutation; see {@link ConfigForm.mutate}.
|
|
77
|
+
* @param ops - ordered field operations copied when queued.
|
|
78
|
+
* @param expectedRevision - optional fixed revision read by the domain editor.
|
|
79
|
+
* @returns whether the Host accepted the mutation, after any recovery read.
|
|
80
|
+
*/
|
|
81
|
+
mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<boolean>;
|
|
82
|
+
/** Reload Host state for the latest failed write; superseded failures leave recovery to it. */
|
|
83
|
+
private recover;
|
|
84
|
+
/**
|
|
85
|
+
* Stop queued operations, stop deriving, and wait for the current wire call
|
|
86
|
+
* to settle.
|
|
87
|
+
* @returns settlement after the controller reaches quiescence.
|
|
88
|
+
*/
|
|
89
|
+
dispose(): Promise<void>;
|
|
90
|
+
private enqueue;
|
|
91
|
+
private derive;
|
|
92
|
+
private decode;
|
|
93
|
+
}
|
|
94
|
+
declare module '@qilin-agent/kylin' {
|
|
95
|
+
interface Context {
|
|
96
|
+
configForms: ConfigForms;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The settings domain's base service. Features that own a preference reach the
|
|
101
|
+
* settings transport through this service rather than a shared function: the
|
|
102
|
+
* client bundle purity gate forbids cross-plugin value imports and directs
|
|
103
|
+
* cross-plugin collaboration through cordis services
|
|
104
|
+
* (`packages/client/tsdown.client.ts`).
|
|
105
|
+
*/
|
|
106
|
+
export declare class ConfigForms extends Service {
|
|
107
|
+
private readonly forms;
|
|
108
|
+
/** Shared developer-tool preference owned by this settings provider. */
|
|
109
|
+
readonly developerTools: DeveloperToolsPreference;
|
|
110
|
+
private readonly mirror;
|
|
111
|
+
private readonly schema;
|
|
112
|
+
private readonly persistence;
|
|
113
|
+
/**
|
|
114
|
+
* The PROVIDING fiber, kept because a Service reads `ctx` as its *consumer's*
|
|
115
|
+
* fiber: letting a shared form write through the caller's context would make
|
|
116
|
+
* every caller declare `remote.settings` in its own `inject`.
|
|
117
|
+
*/
|
|
118
|
+
private readonly owner;
|
|
119
|
+
/**
|
|
120
|
+
* @param ctx - the providing plugin's context.
|
|
121
|
+
* @param config - the shared describe mirror every shared form derives from,
|
|
122
|
+
* the settings-owned schema operations, and the Host persistence the provider
|
|
123
|
+
* resolved from `remote.$host`.
|
|
124
|
+
*/
|
|
125
|
+
constructor(ctx: Context, config: {
|
|
126
|
+
mirror: SettingsDescribeMirror;
|
|
127
|
+
schema: SettingsSchemaService;
|
|
128
|
+
persistence: 'host' | 'memory';
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* The shared mirror's read/fold face for cross-namespace surfaces (schema
|
|
132
|
+
* introspection, the served-namespace directory). Per-namespace consumers
|
|
133
|
+
* use {@link get}; both derive from the same snapshot, so they can never
|
|
134
|
+
* disagree about the document.
|
|
135
|
+
* @returns the describe face over the shared mirror.
|
|
136
|
+
*/
|
|
137
|
+
describe(): SettingsDescribeFace;
|
|
138
|
+
/** Get the shared form values and write queue for one Host plugin entry.
|
|
139
|
+
* @param entryId Unique Host plugin entry id.
|
|
140
|
+
* @returns The entry's form, owned by this provider.
|
|
141
|
+
*/
|
|
142
|
+
get<T>(entryId: string): ConfigForm<T>;
|
|
143
|
+
/**
|
|
144
|
+
* Keep a registration alive while the Host serves any of some namespaces:
|
|
145
|
+
* `register` runs once one of them is in the describe mirror, and its
|
|
146
|
+
* disposer runs when none is or when the returned disposer runs. A plugin
|
|
147
|
+
* whose page edits a namespace another plugin owns registers the page
|
|
148
|
+
* through this, so a deployment that never composed the owner shows no
|
|
149
|
+
* trace of the page. The caller owns the returned disposer and wraps it in
|
|
150
|
+
* `ctx.effect`; unlike {@link bind}, nothing is registered on the caller's
|
|
151
|
+
* context here.
|
|
152
|
+
* @param namespaces - the settings namespaces the registration follows.
|
|
153
|
+
* @param register - registers the contribution, given every namespace the Host serves; returns its disposer.
|
|
154
|
+
* @returns the disposer ending the watch and any live registration.
|
|
155
|
+
*/
|
|
156
|
+
whileServed(namespaces: readonly string[], register: (served: ReadonlySet<string>) => () => void): () => void;
|
|
157
|
+
}
|
|
158
|
+
export {};
|
|
159
|
+
//# sourceMappingURL=config-form.d.ts.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings slot contract — the canonical home of every settings slot type,
|
|
3
|
+
* owned by the settings domain base rather than by the shell that renders
|
|
4
|
+
* them (ui-settings-general, which occupies `sidebar.settings`). The shell has
|
|
5
|
+
* zero copy of its own: ALL text (panel title, header actions, close aria,
|
|
6
|
+
* section content) arrives from registrants. A feature owns its own settings
|
|
7
|
+
* pages — adding a setting never means editing the shell; copy that belongs to
|
|
8
|
+
* no single feature (chrome, the General section) is owned by ui-settings-general too.
|
|
9
|
+
*/
|
|
10
|
+
declare module '@qilin-agent/client-ui-slots' {
|
|
11
|
+
interface SlotMap {
|
|
12
|
+
/**
|
|
13
|
+
* The panel title text seat. Content renders inside the nav heading row;
|
|
14
|
+
* the dialog's accessible name points at that node via aria-labelledby.
|
|
15
|
+
* Absent contribution leaves the heading empty.
|
|
16
|
+
*/
|
|
17
|
+
'settings.header': {
|
|
18
|
+
kind: 'single';
|
|
19
|
+
scope: 'root';
|
|
20
|
+
owner: SettingsHeaderOwnerProps;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Optional actions rendered in the content-column header before Close.
|
|
24
|
+
* Registrants own visibility, behavior, copy, and failure presentation;
|
|
25
|
+
* the shell supplies only the ordered render site.
|
|
26
|
+
*/
|
|
27
|
+
'settings.action': {
|
|
28
|
+
kind: 'list';
|
|
29
|
+
scope: 'root';
|
|
30
|
+
owner: SettingsHeaderOwnerProps;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The close button's visually-hidden label text (the button itself —
|
|
34
|
+
* icon, geometry, focus — is shell chrome). Absent contribution leaves
|
|
35
|
+
* the button without an accessible name (broken-composition state).
|
|
36
|
+
*/
|
|
37
|
+
'settings.close': {
|
|
38
|
+
kind: 'single';
|
|
39
|
+
scope: 'root';
|
|
40
|
+
owner: SettingsHeaderOwnerProps;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* One settings page per list entry. Registrant options carry the nav
|
|
44
|
+
* identity: `id` (section key, drives `only` filtering), `order` (nav
|
|
45
|
+
* position), `label` (registrant-localized display text — the registrant
|
|
46
|
+
* re-registers with fresh text on locale change, so the shell never
|
|
47
|
+
* subscribes locale state; the ledger bump doubles as the shell's
|
|
48
|
+
* re-render trigger). Sections render inside the panel content column.
|
|
49
|
+
* (`settings.general.item`, declared by ui-settings-general's General
|
|
50
|
+
* entry, is typed in the locale package — the common dependency of every
|
|
51
|
+
* item registrant; the shell neither declares nor renders it.)
|
|
52
|
+
*/
|
|
53
|
+
'settings.section': {
|
|
54
|
+
kind: 'list';
|
|
55
|
+
scope: 'root';
|
|
56
|
+
owner: SettingsSectionOwnerProps;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* One page inside the Plugins settings section. The section owner renders
|
|
60
|
+
* localized entry labels as tabs and mounts each contribution inside its
|
|
61
|
+
* corresponding tab panel. Options: `id` (tab key), `order` (tab order),
|
|
62
|
+
* and `label` (registrant-localized tab text). Declared at runtime by the
|
|
63
|
+
* feature that owns the Plugins section; the type lives here so inventory
|
|
64
|
+
* and configuration plugins collaborate without depending on one another.
|
|
65
|
+
*/
|
|
66
|
+
'settings.plugins.tab': {
|
|
67
|
+
kind: 'list';
|
|
68
|
+
scope: 'root';
|
|
69
|
+
owner: SettingsPluginsTabOwnerProps;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Root-scoped onboarding steps contributed by settings features. The
|
|
73
|
+
* shell mounts one ordered step at a time; the active registrant either
|
|
74
|
+
* completes itself or keeps ownership until the user completes its sole
|
|
75
|
+
* path. Registrants own readiness, copy, dialog behavior, AND visible
|
|
76
|
+
* chrome: a step wraps its visible content in its modal surface (including
|
|
77
|
+
* `#root` inert ownership) and renders null while private facts are still
|
|
78
|
+
* loading. The shell paints no chrome of its own, so a mounted-but-deciding
|
|
79
|
+
* step shows and blocks nothing.
|
|
80
|
+
*/
|
|
81
|
+
'settings.onboarding': {
|
|
82
|
+
kind: 'list';
|
|
83
|
+
scope: 'root';
|
|
84
|
+
owner: SettingsOnboardingOwnerProps;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* One preference row inside the General section — the additive seat for a
|
|
88
|
+
* single setting that needs no page of its own (a whole page is
|
|
89
|
+
* `settings.section`), contributed by the feature plugin that owns the
|
|
90
|
+
* preference (locale → Language, ui-theme → Appearance, ui-conversation →
|
|
91
|
+
* Composer Enter). Options: `id` (row key), `order` (row position). The
|
|
92
|
+
* section column only stacks rows, so a row draws its own internals,
|
|
93
|
+
* including its label: nothing projects a `label` here and the owner passes
|
|
94
|
+
* no props at all — copy, current value, and the write path are all yours,
|
|
95
|
+
* through your own inject face and `host.call`. Declared at runtime by
|
|
96
|
+
* ui-settings-general's General entry; the type lives here with every other
|
|
97
|
+
* settings slot type, because this package is the settings domain's base
|
|
98
|
+
* layer and every registrant already depends on it for `ctx.configForms`.
|
|
99
|
+
*/
|
|
100
|
+
'settings.general.item': {
|
|
101
|
+
kind: 'list';
|
|
102
|
+
scope: 'root';
|
|
103
|
+
owner: SettingsGeneralItemOwnerProps;
|
|
104
|
+
};
|
|
105
|
+
/** Mark rendered by the shell-owned About page. */
|
|
106
|
+
'settings.about.mark': {
|
|
107
|
+
kind: 'single';
|
|
108
|
+
scope: 'root';
|
|
109
|
+
owner: SettingsMarkOwnerProps;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Owner share of a settings project mark. */
|
|
114
|
+
export interface SettingsMarkOwnerProps {
|
|
115
|
+
/** Requested square edge in pixels. */
|
|
116
|
+
size: number;
|
|
117
|
+
/** Optional class name supplied by the host surface. */
|
|
118
|
+
className?: string | undefined;
|
|
119
|
+
}
|
|
120
|
+
/** Owner share of a General preference row (the section supplies nothing). */
|
|
121
|
+
export interface SettingsGeneralItemOwnerProps {
|
|
122
|
+
/** Marker field: item owner props are intentionally empty. */
|
|
123
|
+
children?: never;
|
|
124
|
+
}
|
|
125
|
+
/** Owner share of a Plugins tab (the section supplies nothing). */
|
|
126
|
+
export interface SettingsPluginsTabOwnerProps {
|
|
127
|
+
/** Marker field: tab owner props are intentionally empty. */
|
|
128
|
+
children?: never;
|
|
129
|
+
}
|
|
130
|
+
/** Owner share of the header title seat (the shell supplies nothing). */
|
|
131
|
+
export interface SettingsHeaderOwnerProps {
|
|
132
|
+
/** Marker field: header owner props are intentionally empty. */
|
|
133
|
+
children?: never;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Owner share of a settings section entry. The shell owns modal visibility
|
|
137
|
+
* and navigation; a section's data arrives through its own inject faces and
|
|
138
|
+
* stores. `close` is the one shell affordance a section receives, for flows
|
|
139
|
+
* that leave settings altogether (starting a session from a section) — the
|
|
140
|
+
* onboarding coordinator's `openSection`/`complete` precedent, inverted.
|
|
141
|
+
*/
|
|
142
|
+
export interface SettingsSectionOwnerProps {
|
|
143
|
+
/** Close the settings panel (the shell owns the open state). */
|
|
144
|
+
close: () => void;
|
|
145
|
+
}
|
|
146
|
+
/** Owner share of the currently active settings-backed onboarding step. */
|
|
147
|
+
export interface SettingsOnboardingOwnerProps {
|
|
148
|
+
/** Stable id of the step currently selected by the coordinator. */
|
|
149
|
+
stepId: string;
|
|
150
|
+
/** Complete or skip this step and transfer ownership to the next entry. */
|
|
151
|
+
complete: () => void;
|
|
152
|
+
/** Open the settings panel directly on one registered section. */
|
|
153
|
+
openSection: (id: string) => void;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** One accepted preference drives every developer-tool consumer. */
|
|
2
|
+
import { type ObservableSnapshot } from '@qilin-agent/client-store';
|
|
3
|
+
import type { DeveloperToolsSettings } from '../developer-tools-settings.ts';
|
|
4
|
+
import type { ConfigForm } from './config-form-types.ts';
|
|
5
|
+
/** Shared preference; Host-backed features stay disabled until an accepted value arrives. */
|
|
6
|
+
export declare class DeveloperToolsPreference {
|
|
7
|
+
private readonly scope;
|
|
8
|
+
/** Accepted enablement, observable through renderer-bound hooks. */
|
|
9
|
+
readonly enabled: ObservableSnapshot<boolean>;
|
|
10
|
+
private readonly local;
|
|
11
|
+
/**
|
|
12
|
+
* @param scope - settings-owned namespace controller.
|
|
13
|
+
*/
|
|
14
|
+
constructor(scope: ConfigForm<DeveloperToolsSettings>);
|
|
15
|
+
/**
|
|
16
|
+
* Persist a Host choice with ordered writes, or update the shared browser-local choice.
|
|
17
|
+
* @param enabled - requested developer-tool mode.
|
|
18
|
+
* @returns settlement after local publication or Host acceptance; rejects after a refused write recovers.
|
|
19
|
+
*/
|
|
20
|
+
setEnabled(enabled: boolean): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=developer-tools.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings domain base plugin, browser half. Provides `ctx.configForms`, the
|
|
3
|
+
* shared configuration-form service every preference row reaches its durable
|
|
4
|
+
* section through, and owns the one `settings.describe` reader in the browser:
|
|
5
|
+
* the describe mirror, whose invalidation subscriptions
|
|
6
|
+
* (`settings/document-updated`, `connection/reset`) live here so every derived
|
|
7
|
+
* surface refreshes from a single wire read. It depends on no `ui-*`
|
|
8
|
+
* presentation package, so any feature that owns a preference can reach it:
|
|
9
|
+
* the settings SHELL — the `sidebar.settings` occupant, its navigation, and
|
|
10
|
+
* the chrome — lives in ui-settings-general, because a shell dependency on
|
|
11
|
+
* ui-sidebar would close a reference cycle through ui-layout and ui-theme.
|
|
12
|
+
* Export discipline: packages/client/AGENTS.md.
|
|
13
|
+
*/
|
|
14
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
15
|
+
export type { SettingsGeneralItemOwnerProps, SettingsHeaderOwnerProps, SettingsMarkOwnerProps, SettingsOnboardingOwnerProps, SettingsPluginsTabOwnerProps, SettingsSectionOwnerProps, } from './contract/slots.ts';
|
|
16
|
+
export type { ConfigForms } from './config-form.ts';
|
|
17
|
+
export type { ConfigForm, ConfigFormSnapshot } from './config-form-types.ts';
|
|
18
|
+
export type { SettingsSchemaService } from './schema.ts';
|
|
19
|
+
export type { SchemaNode } from './schema.ts';
|
|
20
|
+
export type { SettingsDescribeFace, SettingsDescribeView, SettingsMirrorSnapshot, } from './settings-mirror.ts';
|
|
21
|
+
/**
|
|
22
|
+
* Required services: the Remote namespace the mirror reads through and the
|
|
23
|
+
* forwarded settings invalidation it refreshes on.
|
|
24
|
+
*/
|
|
25
|
+
export declare const inject: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Provide the shared configuration forms over one describe mirror, and keep
|
|
28
|
+
* that mirror fresh on the two signals that can move the settings document: a
|
|
29
|
+
* document commit and a (re)connect.
|
|
30
|
+
*
|
|
31
|
+
* Constructing the service in this plugin's fiber keeps its traced methods
|
|
32
|
+
* bound to each consuming plugin's context.
|
|
33
|
+
* @param ctx - client root context.
|
|
34
|
+
*/
|
|
35
|
+
export declare function apply(ctx: Context): void;
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Synchronous schema introspection and immutable settings-draft edits. */
|
|
2
|
+
import { Service } from '@qilin-agent/kylin';
|
|
3
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
4
|
+
import Schema from '@qilin-agent/schemastery';
|
|
5
|
+
/** Live schemastery node used for settings introspection and validation. */
|
|
6
|
+
export type SchemaNode = Schema;
|
|
7
|
+
/**
|
|
8
|
+
* Settings-owned synchronous schema service. Dynamic client plugins receive
|
|
9
|
+
* this Cordis entity instead of importing executable helpers from one another.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SettingsSchemaService extends Service {
|
|
12
|
+
/** @param ctx - providing ui-settings context. */
|
|
13
|
+
constructor(ctx: Context);
|
|
14
|
+
/**
|
|
15
|
+
* Rehydrate one serialized `schema.toJSON()` envelope.
|
|
16
|
+
* @param serialized - serialized Schemastery node.
|
|
17
|
+
* @returns live schema node.
|
|
18
|
+
*/
|
|
19
|
+
rehydrate(serialized: unknown): SchemaNode;
|
|
20
|
+
/**
|
|
21
|
+
* Validate a settings draft.
|
|
22
|
+
* @param schema - live schema node.
|
|
23
|
+
* @param draft - candidate settings value.
|
|
24
|
+
* @returns validation failure text, or `undefined` when valid.
|
|
25
|
+
*/
|
|
26
|
+
validate(schema: SchemaNode, draft: unknown): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve an object, dict, or array schema node at a settings path.
|
|
29
|
+
* @param root - schema node to traverse.
|
|
30
|
+
* @param path - object keys or array indexes.
|
|
31
|
+
* @returns the resolved node, or `undefined` when the path is absent.
|
|
32
|
+
*/
|
|
33
|
+
nodeAtPath(root: SchemaNode, path: readonly string[]): SchemaNode | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Read a nested value by a string-key or array-index path.
|
|
36
|
+
* @param value - value to traverse.
|
|
37
|
+
* @param path - object keys or array indexes.
|
|
38
|
+
* @returns the resolved value, or `undefined` when the path is absent.
|
|
39
|
+
*/
|
|
40
|
+
getPath(value: unknown, path: readonly string[]): unknown;
|
|
41
|
+
/**
|
|
42
|
+
* Report whether the final path key exists independently of its value.
|
|
43
|
+
* @param value - value to traverse.
|
|
44
|
+
* @param path - object keys or array indexes.
|
|
45
|
+
* @returns whether the path exists.
|
|
46
|
+
*/
|
|
47
|
+
hasPath(value: unknown, path: readonly string[]): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Immutably set a nested value, materializing missing containers.
|
|
50
|
+
* @param root - settings object to copy.
|
|
51
|
+
* @param path - non-empty object-key or array-index path.
|
|
52
|
+
* @param value - replacement value.
|
|
53
|
+
* @returns copied root containing the replacement.
|
|
54
|
+
* @throws when `path` is empty.
|
|
55
|
+
*/
|
|
56
|
+
setPath(root: Record<string, unknown>, path: readonly string[], value: unknown): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Immutably remove a nested key, preserving an unchanged missing root.
|
|
59
|
+
* @param root - settings object to copy.
|
|
60
|
+
* @param path - non-empty object-key or array-index path.
|
|
61
|
+
* @returns copied root without the key, or `root` when the path is absent.
|
|
62
|
+
* @throws when `path` is empty.
|
|
63
|
+
*/
|
|
64
|
+
deletePath(root: Record<string, unknown>, path: readonly string[]): Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
declare module '@qilin-agent/kylin' {
|
|
67
|
+
interface Context {
|
|
68
|
+
/** Settings-owned synchronous schema and immutable path operations. */
|
|
69
|
+
settingsSchema: SettingsSchemaService;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client mirror of the Host settings document: the one `settings.describe`
|
|
3
|
+
* reader in the browser. Every settings consumer derives from this store —
|
|
4
|
+
* shared entry forms through `ConfigForms.get`, cross-namespace
|
|
5
|
+
* surfaces through the provider's shared describe face — so startup cost and
|
|
6
|
+
* freshness are properties of this class, not of how many features own a
|
|
7
|
+
* preference. The Host stays the fact source: the mirror re-reads on the
|
|
8
|
+
* invalidations its owning plugin subscribes to and folds write answers in
|
|
9
|
+
* through {@link SettingsDescribeMirror.acceptView}.
|
|
10
|
+
*/
|
|
11
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
12
|
+
import type { SettingsNamespaceView } from '@qilin-agent/api-remotes/client';
|
|
13
|
+
/** The full `settings.describe` answer the mirror serves. */
|
|
14
|
+
export interface SettingsDescribeView {
|
|
15
|
+
/** Every namespace a live Host plugin registered, as the Host reported it. */
|
|
16
|
+
namespaces: readonly SettingsNamespaceView[];
|
|
17
|
+
/** Whether the settings provider accepts writes. */
|
|
18
|
+
writable: boolean;
|
|
19
|
+
/** Whether a native settings document exists for the Host to open. */
|
|
20
|
+
hasDocument: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Mirror state every derived settings surface renders from. */
|
|
23
|
+
export interface SettingsMirrorSnapshot {
|
|
24
|
+
/**
|
|
25
|
+
* `unavailable` is the terminal non-loopback state; `ready` persists across
|
|
26
|
+
* later failed refreshes (the held view keeps serving); `idle` means no
|
|
27
|
+
* answer is held and no read is running, so `ensure` will start one.
|
|
28
|
+
*/
|
|
29
|
+
status: 'idle' | 'loading' | 'ready' | 'unavailable';
|
|
30
|
+
/** The last good answer; undefined until the first success. */
|
|
31
|
+
view: SettingsDescribeView | undefined;
|
|
32
|
+
/** The latest refresh failure message, cleared by the next success. */
|
|
33
|
+
error: string | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The mirror as cross-namespace surfaces consume it: current answer,
|
|
37
|
+
* subscription, first-use read, and the write-answer fold. `load` stays off
|
|
38
|
+
* this face — invalidation refreshes belong to the mirror's owning plugin.
|
|
39
|
+
*/
|
|
40
|
+
export interface SettingsDescribeFace {
|
|
41
|
+
/** @returns the current sync snapshot (stable reference until the next change). */
|
|
42
|
+
getSnapshot(): SettingsMirrorSnapshot;
|
|
43
|
+
/**
|
|
44
|
+
* Observe snapshot replacements.
|
|
45
|
+
* @param listener - invoked after each snapshot change.
|
|
46
|
+
* @returns the disposer removing this listener.
|
|
47
|
+
*/
|
|
48
|
+
subscribe(listener: () => void): () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve once an answer is held (or the mirror is terminally unavailable),
|
|
51
|
+
* reading only from `idle`.
|
|
52
|
+
* @returns settlement of the current or newly started read, if any.
|
|
53
|
+
*/
|
|
54
|
+
ensure(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Fold one write answer's namespace view into the held view without a wire
|
|
57
|
+
* read, invalidating any older read still in flight.
|
|
58
|
+
* @param view - the namespace view a settings write answered with.
|
|
59
|
+
*/
|
|
60
|
+
acceptView(view: SettingsNamespaceView): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Serializes every Host `settings.describe` read behind one snapshot store.
|
|
64
|
+
* Concurrent {@link load} calls fold into the in-flight read plus one rerun,
|
|
65
|
+
* so an invalidation arriving mid-read is never lost and never duplicated.
|
|
66
|
+
*/
|
|
67
|
+
export declare class SettingsDescribeMirror implements SettingsDescribeFace {
|
|
68
|
+
private readonly ctx;
|
|
69
|
+
private readonly persistence;
|
|
70
|
+
private readonly store;
|
|
71
|
+
private inFlight;
|
|
72
|
+
private rerun;
|
|
73
|
+
private generation;
|
|
74
|
+
/**
|
|
75
|
+
* @param ctx - the providing plugin's context, whose `remote.settings`
|
|
76
|
+
* namespace answers the describe read.
|
|
77
|
+
* @param persistence - client-selected Host persistence; non-loopback pages may remain process-local.
|
|
78
|
+
*/
|
|
79
|
+
constructor(ctx: ClientContext, persistence?: 'host' | 'memory');
|
|
80
|
+
/** @returns the current sync snapshot (stable reference until the next change). */
|
|
81
|
+
getSnapshot(): SettingsMirrorSnapshot;
|
|
82
|
+
/**
|
|
83
|
+
* Observe snapshot replacements.
|
|
84
|
+
* @param listener - invoked after each snapshot change.
|
|
85
|
+
* @returns the disposer removing this listener.
|
|
86
|
+
*/
|
|
87
|
+
subscribe(listener: () => void): () => void;
|
|
88
|
+
/**
|
|
89
|
+
* Refresh from the Host. A call during an in-flight read marks one rerun
|
|
90
|
+
* after it settles instead of racing a second wire read.
|
|
91
|
+
* @returns settlement after this call's freshness is reflected.
|
|
92
|
+
*/
|
|
93
|
+
load(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Resolve once an answer is held (or the mirror is terminally unavailable),
|
|
96
|
+
* reading only from `idle`. The cheap idempotent entry for surfaces that
|
|
97
|
+
* render on first use.
|
|
98
|
+
* @returns settlement of the current or newly started read, if any.
|
|
99
|
+
*/
|
|
100
|
+
ensure(): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Fold one write answer's namespace view into the held view without a wire
|
|
103
|
+
* read, and invalidate any read still in flight. With no held document, the
|
|
104
|
+
* answer is not published as a partial document; an in-flight read reruns so
|
|
105
|
+
* it cannot publish a document fetched before the write committed.
|
|
106
|
+
* @param view - the namespace view a settings write answered with.
|
|
107
|
+
*/
|
|
108
|
+
acceptView(view: SettingsNamespaceView): void;
|
|
109
|
+
/**
|
|
110
|
+
* Convenience row lookup on the held view.
|
|
111
|
+
* @param ns - namespace identity.
|
|
112
|
+
* @returns the namespace view, or undefined while unanswered or unregistered.
|
|
113
|
+
*/
|
|
114
|
+
namespace(ns: string): SettingsNamespaceView | undefined;
|
|
115
|
+
private run;
|
|
116
|
+
private shouldRerun;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=settings-mirror.d.ts.map
|