dsh-quick-actions 0.1.0-rc.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +199 -0
  3. package/README.md +199 -0
  4. package/cordis.patch.yml +29 -0
  5. package/lib/client.js +3901 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/index.js +737 -0
  8. package/lib/types/client/controller.d.ts +207 -0
  9. package/lib/types/client/dsh.d.ts +171 -0
  10. package/lib/types/client/index.d.ts +43 -0
  11. package/lib/types/client/manager/ActionForm.d.ts +20 -0
  12. package/lib/types/client/manager/ActionPanel.d.ts +15 -0
  13. package/lib/types/client/manager/ManagedRow.d.ts +40 -0
  14. package/lib/types/client/manager/ManagerPanel.d.ts +9 -0
  15. package/lib/types/client/manager/press.d.ts +38 -0
  16. package/lib/types/client/manager/search.d.ts +56 -0
  17. package/lib/types/client/manager/status.d.ts +23 -0
  18. package/lib/types/client/modal.d.ts +55 -0
  19. package/lib/types/client/session/ConfirmPanel.d.ts +23 -0
  20. package/lib/types/client/session/availability.d.ts +19 -0
  21. package/lib/types/client/session/execution.d.ts +86 -0
  22. package/lib/types/client/session/guards.d.ts +59 -0
  23. package/lib/types/client/surfaces/ActionFace.d.ts +21 -0
  24. package/lib/types/client/surfaces/ErrorBoundary.d.ts +31 -0
  25. package/lib/types/client/surfaces/QuickActionsSurface.d.ts +17 -0
  26. package/lib/types/client/surfaces/entries.d.ts +27 -0
  27. package/lib/types/client/surfaces/layout.d.ts +49 -0
  28. package/lib/types/client/surfaces/residency.d.ts +65 -0
  29. package/lib/types/host/config.d.ts +18 -0
  30. package/lib/types/host/index.d.ts +38 -0
  31. package/lib/types/host/presets.d.ts +26 -0
  32. package/lib/types/host/settings.d.ts +113 -0
  33. package/lib/types/index.d.ts +35 -0
  34. package/lib/types/locales/index.d.ts +34 -0
  35. package/lib/types/model/catalog.d.ts +63 -0
  36. package/lib/types/model/index.d.ts +13 -0
  37. package/lib/types/model/json.d.ts +11 -0
  38. package/lib/types/model/mutations.d.ts +98 -0
  39. package/lib/types/model/normalize.d.ts +15 -0
  40. package/lib/types/model/projection.d.ts +49 -0
  41. package/lib/types/model/settings.d.ts +48 -0
  42. package/lib/types/model/text.d.ts +28 -0
  43. package/lib/types/model/types.d.ts +89 -0
  44. package/lib/types/model/validation.d.ts +40 -0
  45. package/lib/types/styles/index.d.ts +39 -0
  46. package/lib/types/types.d.ts +11 -0
  47. package/lib/types.js +1 -0
  48. package/package.json +83 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The single persisted Settings namespace and the Host's canonical rewrite
3
+ * (spec 4.2, 6.1, 6.3).
4
+ *
5
+ * The Host is the sole validation and migration authority, so the registered
6
+ * schema is deliberately permissive: it fixes the shape of the section and its
7
+ * defaults, and nothing else. A strict schema would refuse registration for a
8
+ * section a higher version wrote — which is exactly the data spec 5.3 requires
9
+ * to survive a downgrade untouched. The shared model decodes and normalizes what
10
+ * the schema lets through, and it never rewrites content it cannot render.
11
+ */
12
+ import Schema from '@deepseek-ai/schemastery';
13
+ import type { PresetCatalog, QuickActionSettingsV1 } from '../model/index.js';
14
+ /**
15
+ * Shape and defaults of the persisted section (spec 4.2). The three collections
16
+ * stay unconstrained on purpose: a stricter schema would refuse registration for
17
+ * a section this release cannot render, and refusing registration is how stored
18
+ * data gets lost. The shared model is the validation gate — see the module note.
19
+ */
20
+ export declare const quickActionSettingsSchema: Schema<Schemastery.ObjectS<{
21
+ schemaVersion: Schema<number, number>;
22
+ layout: Schema<string, string>;
23
+ userActionsById: Schema<any, any>;
24
+ actionOrder: Schema<any, any>;
25
+ presetStateById: Schema<any, any>;
26
+ }>, Schemastery.ObjectT<{
27
+ schemaVersion: Schema<number, number>;
28
+ layout: Schema<string, string>;
29
+ userActionsById: Schema<any, any>;
30
+ actionOrder: Schema<any, any>;
31
+ presetStateById: Schema<any, any>;
32
+ }>>;
33
+ /**
34
+ * Shape of the catalog namespace. Its authoritative content is the composition
35
+ * `base` layer the Host declares; a user layer is never written and, if one were
36
+ * hand-written into the document, Clients would still read `base` (spec 17.2).
37
+ */
38
+ export declare const quickActionCatalogSchema: Schema<Schemastery.ObjectS<{
39
+ schemaVersion: Schema<number, number>;
40
+ revision: Schema<string, string>;
41
+ presets: Schema<any, any>;
42
+ }>, Schemastery.ObjectT<{
43
+ schemaVersion: Schema<number, number>;
44
+ revision: Schema<string, string>;
45
+ presets: Schema<any, any>;
46
+ }>>;
47
+ /** One namespace as the provider describes it. */
48
+ export interface SettingsDescriptorLike {
49
+ readonly ns: string;
50
+ /** Monotonic revision of the raw user section; send it back to fence a write. */
51
+ readonly revision: number;
52
+ /** The raw stored user section, absent while nothing was ever written. */
53
+ readonly user?: unknown;
54
+ }
55
+ /**
56
+ * The part of the Host settings provider the canonical rewrite reads and writes
57
+ * through. Declared structurally so the rewrite is testable against a controlled
58
+ * fake — the real `SettingsProvider` satisfies it as-is.
59
+ */
60
+ export interface SettingsRewriteProvider {
61
+ readonly writable: boolean;
62
+ describe(): readonly SettingsDescriptorLike[];
63
+ replace(ns: string, section: object, expectedRevision?: number): Promise<void>;
64
+ }
65
+ export type CanonicalRewriteOutcome =
66
+ /** The stored section already was canonical; nothing was written. */
67
+ {
68
+ readonly status: 'unchanged';
69
+ }
70
+ /** Nothing is stored yet, so there is no stored state to canonicalize. */
71
+ | {
72
+ readonly status: 'nothing-stored';
73
+ }
74
+ /**
75
+ * The stored section belongs to a higher `schemaVersion`. This release reads it
76
+ * losslessly but must not write it back, or a downgrade would stamp its own
77
+ * version over data it does not own (spec 5.3, 16.1).
78
+ */
79
+ | {
80
+ readonly status: 'newer-version';
81
+ readonly schemaVersion: number;
82
+ } | {
83
+ readonly status: 'rewritten';
84
+ readonly revision: number;
85
+ }
86
+ /** The namespace kept moving; the rewrite refused to overwrite the writer that won. */
87
+ | {
88
+ readonly status: 'conflict';
89
+ readonly attempts: number;
90
+ } | {
91
+ readonly status: 'read-only';
92
+ }
93
+ /** The namespace is not registered yet, so there is nothing to fence a write to. */
94
+ | {
95
+ readonly status: 'unregistered';
96
+ };
97
+ /**
98
+ * Read the stored section, canonicalize it against the current catalog, and
99
+ * persist it behind the revision it was read at (spec 6.3).
100
+ *
101
+ * Nothing is written when nothing is stored: materializing the defaults into the
102
+ * user layer would shadow the composition `base` and destroy what `replace({})`
103
+ * resets to. Nothing is written for a higher `schemaVersion` either — that data
104
+ * belongs to the version that wrote it.
105
+ *
106
+ * A conflict means another writer won the race, so the rewrite refreshes the
107
+ * authoritative snapshot and recomputes rather than replaying its own stale
108
+ * section; after a bounded number of attempts it reports the conflict instead of
109
+ * overwriting the concurrent write.
110
+ */
111
+ export declare function rewriteCanonicalSettings(settings: SettingsRewriteProvider, catalog: PresetCatalog): Promise<CanonicalRewriteOutcome>;
112
+ /** Decode one raw stored section and canonicalize it against the catalog. */
113
+ export declare function canonicalSettings(stored: unknown, catalog: PresetCatalog): QuickActionSettingsV1;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Host composition entry for Composer Quick Actions.
3
+ *
4
+ * `settings` is a hard dependency: the concrete provider (`dsh-settings-file`)
5
+ * *is* the service, so injecting it is how this plugin waits for the backend
6
+ * instead of inventing an in-memory, browser or private-file store (spec 6.1).
7
+ * A provider that is present but not writable is a supported state, not a
8
+ * missing one — the stored section is left untouched and the surfaces go
9
+ * read-only (spec 10).
10
+ *
11
+ * Everything this entry registers — the Settings namespace and the startup
12
+ * rewrite's reporting — is owned by this fiber and withdrawn when it unloads.
13
+ * The user's stored section deliberately survives unload, so reinstalling
14
+ * restores the user's actions.
15
+ *
16
+ * The public surface is the plugin contract only. The shared model, the
17
+ * Settings rewrite and the catalog assembly stay internal to the package
18
+ * (spec 11.2); whether any of them becomes a published runtime export belongs
19
+ * to the release-surface decision.
20
+ */
21
+ import type { Context } from '@deepseek-ai/cordis';
22
+ import type { QuickActionsHost } from './host/index.js';
23
+ export declare const name = "composer-quick-actions";
24
+ export declare const inject: readonly string[];
25
+ export type { ComposerQuickActionsConfig } from './host/config.js';
26
+ export type { CatalogSnapshot, QuickActionsHost } from './host/index.js';
27
+ /**
28
+ * Load the Preset Catalog, register the Settings namespace and canonicalize the
29
+ * stored section. An invalid preset configuration throws here, failing plugin
30
+ * loading loudly rather than shipping a partial catalog (spec 5.1).
31
+ *
32
+ * @returns the running Host, so a composition that embeds this plugin directly
33
+ * can reach the catalog projection; the cordis loader ignores the value.
34
+ */
35
+ export declare function apply(ctx: Context, config?: unknown): QuickActionsHost;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Composer Quick Actions dictionaries (spec 8.4).
3
+ *
4
+ * Both shipped languages carry the same key set, so a missing translation is a
5
+ * compile error rather than a key leaking onto a surface. Keys are registered
6
+ * under one namespace and reached through the `t` prop the Slot registration's
7
+ * `locale` option binds; nothing here formats a date, a number or a plural.
8
+ *
9
+ * Two families of key are addressed by composition rather than by name — a
10
+ * field issue (`issue.<field>.<reason>`) and a write failure (`write.<kind>`) —
11
+ * because the model and the controller name those cases, not this file. Both go
12
+ * through {@link quickActionsLocaleKey}, which checks the composed key against
13
+ * the dictionary itself, so a case this release cannot phrase falls back to a
14
+ * general sentence instead of printing a raw key at the user.
15
+ */
16
+ import type { QuickActionFieldIssue } from '../model/index.js';
17
+ /** The locale namespace this package registers and every Slot entry binds. */
18
+ export declare const QUICK_ACTIONS_LOCALE_NAMESPACE = "composer-quick-actions";
19
+ /** Every key the Composer surfaces and the management panel render. */
20
+ export type QuickActionsLocaleKey = 'title' | 'manage' | 'manage.tooltip' | 'more' | 'launcher' | 'empty' | 'command.badge' | 'unavailable.occupied-draft' | 'unavailable.composer-busy' | 'unavailable.composer-blocked' | 'unavailable.session-removed' | 'unavailable.parent-offline' | 'unavailable.sending' | 'feedback.state-changed' | 'feedback.retained' | 'feedback.failed' | 'feedback.dismiss' | 'catalog.loading' | 'catalog.unreadable' | 'catalog.unavailable' | 'catalog.undecodable' | 'catalog.retry' | 'crash.title' | 'crash.retry' | 'confirm.title' | 'confirm.send' | 'confirm.cancel' | 'confirm.command' | 'panel.title' | 'panel.close' | 'panel.search' | 'panel.search.empty' | 'manager.title' | 'manager.close' | 'manager.layout' | 'manager.layout.ribbon' | 'manager.layout.bar' | 'manager.layout.launcher' | 'manager.actions' | 'manager.count' | 'manager.new' | 'manager.empty' | 'manager.overflow' | 'manager.limit' | 'manager.command.notice' | 'manager.preset' | 'manager.custom' | 'manager.clonedFrom' | 'manager.hidden' | 'manager.disabled' | 'manager.hide' | 'manager.restore' | 'manager.clone' | 'manager.edit' | 'manager.enable' | 'manager.disable' | 'manager.delete' | 'manager.delete.confirm' | 'manager.delete.cancel' | 'manager.moveUp' | 'manager.moveDown' | 'manager.readonly.storage' | 'manager.readonly.offline' | 'form.title.new' | 'form.title.edit' | 'form.label' | 'form.label.hint' | 'form.text' | 'form.text.hint' | 'form.icon' | 'form.icon.hint' | 'form.confirm' | 'form.save' | 'form.cancel' | 'form.command.warning' | 'issue.invalid' | 'issue.label.blank' | 'issue.label.too-long' | 'issue.text.blank' | 'issue.text.too-long' | 'issue.text.reserved-placeholder' | 'issue.icon.not-emoji' | 'issue.icon.too-long' | 'write.dismiss' | 'write.retry' | 'write.not-ready' | 'write.read-only' | 'write.refused' | 'write.conflict' | 'write.failed' | 'write.invalid-fields' | 'write.limit-reached' | 'write.unknown-action' | 'write.id-in-use' | 'write.invalid-order';
21
+ export type QuickActionsDictionary = Record<QuickActionsLocaleKey, string>;
22
+ export declare const zh: QuickActionsDictionary;
23
+ export declare const en: QuickActionsDictionary;
24
+ /** The dictionaries as `ctx.locale.register` takes them. */
25
+ export declare const quickActionsDictionaries: Record<string, QuickActionsDictionary>;
26
+ /**
27
+ * Narrow a composed key to one this package ships, falling back when it does
28
+ * not. Composed keys come from names the model and the controller own — a field
29
+ * issue, a mutation refusal, a write failure — so a case added upstream shows a
30
+ * general sentence instead of leaking `write.something-new` onto a surface.
31
+ */
32
+ export declare function quickActionsLocaleKey(candidate: string, fallback: QuickActionsLocaleKey): QuickActionsLocaleKey;
33
+ /** The dictionary entry naming one field issue (spec 4.3). */
34
+ export declare function quickActionIssueKey(issue: QuickActionFieldIssue): QuickActionsLocaleKey;
@@ -0,0 +1,63 @@
1
+ import type { PresetQuickAction, QuickActionField, QuickActionIssueReason } from './types.js';
2
+ /** Absolute threshold on the merged catalog itself (spec 5.4). */
3
+ export declare const QUICK_ACTION_CATALOG_LIMIT = 50;
4
+ /** Where a catalog entry was declared. */
5
+ export type PresetSource = 'builtin' | 'config';
6
+ /** What the Host feeds the model: raw JSON, validated here rather than upstream. */
7
+ export interface PresetCatalogInput {
8
+ /** The package's built-in manifest, in declaration order. */
9
+ readonly builtins: readonly unknown[];
10
+ /** `Config.presets` from the Host composition, in declaration order. */
11
+ readonly configured: readonly unknown[];
12
+ }
13
+ /** One reason the plugin config must fail to load. */
14
+ export type PresetCatalogIssue = {
15
+ readonly scope: 'preset';
16
+ readonly source: PresetSource;
17
+ readonly index: number;
18
+ /** The declared id when it is a usable string, otherwise `undefined`. */
19
+ readonly id: string | undefined;
20
+ readonly field: QuickActionField;
21
+ readonly reason: QuickActionIssueReason;
22
+ } | {
23
+ readonly scope: 'catalog';
24
+ readonly reason: 'too-many';
25
+ readonly count: number;
26
+ readonly limit: number;
27
+ };
28
+ /** The authoritative, read-only catalog snapshot the Remote publishes (spec 6.2). */
29
+ export interface PresetCatalog {
30
+ readonly schemaVersion: 1;
31
+ /** Determined by the catalog projection; any change to it changes this value. */
32
+ readonly revision: string;
33
+ readonly presets: readonly PresetQuickAction[];
34
+ }
35
+ /** Either the authoritative catalog, or every reason config loading has to fail. */
36
+ export type PresetCatalogResult = {
37
+ readonly ok: true;
38
+ readonly catalog: PresetCatalog;
39
+ } | {
40
+ readonly ok: false;
41
+ readonly issues: readonly PresetCatalogIssue[];
42
+ };
43
+ /**
44
+ * Merge and validate the built-in manifest with the Host composition's presets.
45
+ * Nothing is repaired or dropped: the first release refuses to load a catalog an
46
+ * author got wrong, so the mistake surfaces at startup rather than at send time.
47
+ */
48
+ export declare function buildPresetCatalog(input: PresetCatalogInput): PresetCatalogResult;
49
+ /**
50
+ * Read one published catalog snapshot back (spec 17.2). The Host is the catalog's
51
+ * single validation authority, so this is a defensive decode of an already
52
+ * confirmed snapshot rather than a second authority — but it is a decode, not a
53
+ * cast: a snapshot this release cannot read in full yields no catalog at all.
54
+ *
55
+ * All-or-nothing on purpose. Dropping the entries it cannot read would show the
56
+ * user a silently truncated action list, which is exactly what spec 5.1 forbids
57
+ * the Host to do; the consumer reports a catalog error instead (spec 10).
58
+ *
59
+ * The Host's `revision` is carried through rather than recomputed. It is the
60
+ * catalog's published identity, and a Client that recomputed it would be
61
+ * asserting an authority it does not have.
62
+ */
63
+ export declare function decodeCatalogSnapshot(raw: unknown): PresetCatalog | undefined;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared Quick Action domain model: pure JSON in, pure JSON out.
3
+ * Host and Client both consume it; it never touches cordis, React, the DOM or storage.
4
+ */
5
+ export * from './types.js';
6
+ export * from './json.js';
7
+ export * from './text.js';
8
+ export * from './validation.js';
9
+ export * from './catalog.js';
10
+ export * from './settings.js';
11
+ export * from './normalize.js';
12
+ export * from './projection.js';
13
+ export * from './mutations.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * JSON structural comparison shared by the mutation planner and the Host's
3
+ * canonical rewrite.
4
+ *
5
+ * Structural, not canonical-JSON: object key order carries no meaning here.
6
+ * A stored section round-tripped through YAML, and a tombstone written by a
7
+ * higher version, both come back with whatever key order their writer chose —
8
+ * comparing serialized text would report a difference that is not one, and the
9
+ * rewrite would then write on every start instead of being idempotent.
10
+ */
11
+ export declare function deepEqualJson(left: unknown, right: unknown): boolean;
@@ -0,0 +1,98 @@
1
+ import type { PresetCatalog } from './catalog.js';
2
+ import type { QuickActionDraft } from './validation.js';
3
+ import type { CustomActionId, PresetActionId, QuickActionFieldIssue, QuickActionLayout, QuickActionRef, QuickActionSettingsV1 } from './types.js';
4
+ /** What every planner reads: the confirmed snapshot, the catalog it was read against, and its revision. */
5
+ export interface QuickActionMutationContext {
6
+ readonly settings: QuickActionSettingsV1;
7
+ readonly catalog: PresetCatalog;
8
+ /** The Settings namespace revision the resulting write must carry (spec 6.3). */
9
+ readonly revision: string;
10
+ }
11
+ /** What to persist, and the fence the write must carry. */
12
+ export interface QuickActionMutationPlan {
13
+ readonly expectedRevision: string;
14
+ readonly next: QuickActionSettingsV1;
15
+ /** False when the plan would persist exactly what is already stored. */
16
+ readonly changed: boolean;
17
+ }
18
+ /** Why a planner refused; each reason maps to one management-panel response (spec 10). */
19
+ export type QuickActionMutationRejectionReason =
20
+ /** One or more fields failed the shared validation rules. */
21
+ 'invalid-fields'
22
+ /** Creating or cloning while the action total is at or above the ceiling. */
23
+ | 'limit-reached'
24
+ /** The target action is absent, or is a tombstone this release must not touch. */
25
+ | 'unknown-action'
26
+ /** The requested Custom Action ID is taken; mint another and retry. */
27
+ | 'id-in-use'
28
+ /** The submitted order is not a permutation of the managed actions. */
29
+ | 'invalid-order';
30
+ /** A refusal, with the field issues to render when the reason is a validation failure. */
31
+ export interface QuickActionMutationRejection {
32
+ readonly reason: QuickActionMutationRejectionReason;
33
+ readonly issues: readonly QuickActionFieldIssue[];
34
+ }
35
+ /** Either a plan to persist behind the fence, or a refusal to show the user. */
36
+ export type QuickActionMutationOutcome = {
37
+ readonly ok: true;
38
+ readonly plan: QuickActionMutationPlan;
39
+ } | {
40
+ readonly ok: false;
41
+ readonly rejection: QuickActionMutationRejection;
42
+ };
43
+ /** The draft the management form opens on; the only place the confirm default is applied. */
44
+ export declare function newQuickActionDraft(): QuickActionDraft;
45
+ /** Create a Custom Quick Action under a caller-minted identity (spec 5.2, 5.4). */
46
+ export declare function planCreateCustomQuickAction(context: QuickActionMutationContext, input: {
47
+ readonly id: CustomActionId;
48
+ readonly draft: QuickActionDraft;
49
+ }): QuickActionMutationOutcome;
50
+ /**
51
+ * Clone a Preset Quick Action into a new Custom Quick Action (spec 5.2).
52
+ * The confirmation policy is copied as it stands — including a Command Send
53
+ * Action the author left unconfirmed — never re-defaulted.
54
+ */
55
+ export declare function planClonePresetQuickAction(context: QuickActionMutationContext, input: {
56
+ readonly presetId: PresetActionId;
57
+ readonly id: CustomActionId;
58
+ }): QuickActionMutationOutcome;
59
+ /**
60
+ * Save edited content onto an existing Custom Quick Action. `enabled` and the
61
+ * Clone Provenance survive the edit; `confirm` comes from the form, which is the
62
+ * only place the user can change it.
63
+ */
64
+ export declare function planUpdateCustomQuickAction(context: QuickActionMutationContext, input: {
65
+ readonly id: CustomActionId;
66
+ readonly draft: QuickActionDraft;
67
+ }): QuickActionMutationOutcome;
68
+ /** Disable or re-enable a Custom Quick Action; disabled actions stay in the management panel (spec 3). */
69
+ export declare function planSetCustomQuickActionEnabled(context: QuickActionMutationContext, input: {
70
+ readonly id: CustomActionId;
71
+ readonly enabled: boolean;
72
+ }): QuickActionMutationOutcome;
73
+ /** Delete a Custom Quick Action; normalization drops its order reference with it. */
74
+ export declare function planDeleteCustomQuickAction(context: QuickActionMutationContext, input: {
75
+ readonly id: CustomActionId;
76
+ }): QuickActionMutationOutcome;
77
+ /** Hide or restore a Preset Quick Action; the author's definition is never touched (spec 5.1). */
78
+ export declare function planSetPresetQuickActionHidden(context: QuickActionMutationContext, input: {
79
+ readonly presetId: PresetActionId;
80
+ readonly hidden: boolean;
81
+ }): QuickActionMutationOutcome;
82
+ /**
83
+ * Apply a new order to the actions the management panel lists. Preserved
84
+ * references — unknown presets and tombstones — keep their exact positions, so a
85
+ * reorder here never disturbs data a higher version owns.
86
+ */
87
+ export declare function planReorderQuickActions(context: QuickActionMutationContext, input: {
88
+ readonly order: readonly QuickActionRef[];
89
+ }): QuickActionMutationOutcome;
90
+ /** Move one managed action to another position in the management list. */
91
+ export declare function planMoveQuickAction(context: QuickActionMutationContext, input: {
92
+ readonly ref: QuickActionRef;
93
+ readonly toIndex: number;
94
+ }): QuickActionMutationOutcome;
95
+ /** Switch the global Quick Action Layout (spec 8.1). */
96
+ export declare function planSetQuickActionLayout(context: QuickActionMutationContext, input: {
97
+ readonly layout: QuickActionLayout;
98
+ }): QuickActionMutationOutcome;
@@ -0,0 +1,15 @@
1
+ import type { PresetCatalog } from './catalog.js';
2
+ import type { QuickActionSettingsV1 } from './types.js';
3
+ /**
4
+ * Canonicalize one decoded snapshot against a catalog.
5
+ *
6
+ * Order: existing references keep their positions, minus repeats and minus
7
+ * references to custom actions that are gone; references to presets this catalog
8
+ * no longer carries stay put, because the same Preset Action ID coming back must
9
+ * restore the user's preference. Known actions with no reference are appended —
10
+ * presets in catalog order, then custom actions in stored order. Tombstones are
11
+ * never given a new reference, so a downgrade round trip returns their exact order.
12
+ */
13
+ export declare function normalizeQuickActionSettings(settings: QuickActionSettingsV1, catalog: PresetCatalog): QuickActionSettingsV1;
14
+ /** Decode a raw persisted value and canonicalize it in one step — the Host's read path. */
15
+ export declare function readQuickActionSettings(raw: unknown, catalog: PresetCatalog): QuickActionSettingsV1;
@@ -0,0 +1,49 @@
1
+ import type { PresetCatalog } from './catalog.js';
2
+ import type { PresetActionId, QuickActionLayout, QuickActionRef, QuickActionSettingsV1 } from './types.js';
3
+ /** Normal ceiling on known presets plus every custom action (spec 5.4). */
4
+ export declare const QUICK_ACTION_TOTAL_LIMIT = 50;
5
+ /** One action as the surfaces consume it, with its source-specific affordances resolved. */
6
+ export interface ProjectedQuickAction {
7
+ readonly ref: QuickActionRef;
8
+ readonly label: string;
9
+ readonly text: string;
10
+ readonly icon: string | undefined;
11
+ readonly confirm: boolean;
12
+ /** Command Send Action: the text's first non-whitespace character is `/` (spec 4.3). */
13
+ readonly command: boolean;
14
+ /** Presets are author-owned: the user may reorder, hide and clone them, never edit them. */
15
+ readonly editable: boolean;
16
+ /** Hidden preset or disabled custom action: management panel only (spec 3). */
17
+ readonly hidden: boolean;
18
+ readonly clonedFromPresetId: PresetActionId | undefined;
19
+ }
20
+ /** The numbers the management panel gates its entry points and warnings on (spec 5.4). */
21
+ export interface QuickActionCounts {
22
+ /** Known presets plus every custom action; hidden and disabled ones included. */
23
+ readonly total: number;
24
+ readonly limit: number;
25
+ /** `total` already above `limit` — a passive overflow that never drops data. */
26
+ readonly overflow: boolean;
27
+ /** Whether creating or cloning is allowed right now. */
28
+ readonly canAdd: boolean;
29
+ readonly visible: number;
30
+ readonly hidden: number;
31
+ /** Entries preserved but kept out of every projection: unknown presets and tombstones. */
32
+ readonly preserved: number;
33
+ }
34
+ /** Everything the surfaces and the management panel derive from one snapshot. */
35
+ export interface QuickActionProjection {
36
+ readonly layout: QuickActionLayout;
37
+ /** Everything the management panel lists, in the shared order. */
38
+ readonly managed: readonly ProjectedQuickAction[];
39
+ /** The subset the Composer renders, in the same order. */
40
+ readonly composer: readonly ProjectedQuickAction[];
41
+ readonly counts: QuickActionCounts;
42
+ }
43
+ /**
44
+ * Project one snapshot against a catalog. The snapshot is canonicalized first, so
45
+ * the counts are the real totals even when the caller hands over state the catalog
46
+ * has moved on from — a known preset or a live action missing from the order must
47
+ * never slip past the action ceiling.
48
+ */
49
+ export declare function projectQuickActions(raw: QuickActionSettingsV1, catalog: PresetCatalog): QuickActionProjection;
@@ -0,0 +1,48 @@
1
+ import type { CustomQuickActionValue, PresetQuickActionState, QuickActionSettingsV1, QuickActionTombstone, StoredQuickActionValue } from './types.js';
2
+ /**
3
+ * The one namespace holding user data (spec 4.2); renaming it orphans every
4
+ * stored section. It lives in the shared model because both faces address it:
5
+ * the Host registers it, the Client binds the same name.
6
+ */
7
+ export declare const QUICK_ACTIONS_SETTINGS_NAMESPACE = "composer-quick-actions";
8
+ /**
9
+ * The read-only namespace carrying the Preset Catalog to Clients (spec 17.2).
10
+ * The plugin never writes its user layer, so it holds no persisted section —
11
+ * `composer-quick-actions` remains the only persisted namespace (spec 4.2).
12
+ */
13
+ export declare const QUICK_ACTIONS_CATALOG_NAMESPACE = "composer-quick-actions-catalog";
14
+ /** The state a fresh install starts from (spec 4.2). */
15
+ export declare const DEFAULT_QUICK_ACTION_SETTINGS: QuickActionSettingsV1;
16
+ /**
17
+ * Whether a stored entry is a live Send Action this release can render.
18
+ * Everything else is a tombstone: a higher version's `kind`, or a value whose
19
+ * label/text are not readable strings.
20
+ */
21
+ export declare function isLiveQuickAction(value: StoredQuickActionValue): value is CustomQuickActionValue;
22
+ /** Whether a stored entry must be preserved untouched and kept out of every projection (spec 5.3). */
23
+ export declare function isQuickActionTombstone(value: StoredQuickActionValue): value is QuickActionTombstone;
24
+ /**
25
+ * Read one stored custom action into canonical form. A missing `kind` reads as
26
+ * `'send'` because V1 always writes the tag; anything else marks a tombstone and
27
+ * is handed straight back by identity, so a value written by a higher version
28
+ * survives the round trip intact.
29
+ *
30
+ * A live result always carries `kind`, `confirm` and `enabled` explicitly, which
31
+ * is what spec 4.3 requires of every normalized action. `confirm` is only
32
+ * defaulted when it is absent — never derived from the text.
33
+ */
34
+ export declare function decodeStoredQuickAction(value: unknown): StoredQuickActionValue | undefined;
35
+ /**
36
+ * Canonicalize one preset state: `hidden` becomes an explicit boolean or
37
+ * disappears, and every other field is carried through so a higher version's own
38
+ * preference survives a downgrade (spec 5.3).
39
+ */
40
+ export declare function canonicalPresetState(state: Readonly<Record<string, unknown>>): PresetQuickActionState;
41
+ /** Whether a canonical preset state carries no user preference at all. */
42
+ export declare function isEmptyPresetState(state: PresetQuickActionState): boolean;
43
+ /**
44
+ * Decode any published snapshot into V1. The stored `schemaVersion` is not a
45
+ * gate: a snapshot written by a higher version still yields every field this
46
+ * release understands, which is what makes a downgrade round trip lossless.
47
+ */
48
+ export declare function decodeQuickActionSettings(raw: unknown): QuickActionSettingsV1;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Unicode primitives shared by every Quick Action entry point (spec 4.3).
3
+ * Config loading, the management form, migration and Settings mutations must all
4
+ * measure and trim text through these functions so one rule cannot drift from another.
5
+ */
6
+ /** Unicode code point length — the counting unit for every length limit (spec 4.3). */
7
+ export declare function countCodePoints(value: string): number;
8
+ /** Whether a text holds no character outside ECMAScript `trim()` whitespace (spec 4.3). */
9
+ export declare function isBlankQuickActionText(text: string): boolean;
10
+ /** Whether a text carries a DSH-reserved reference placeholder code point (spec 4.3). */
11
+ export declare function containsReservedReferencePlaceholder(text: string): boolean;
12
+ /**
13
+ * Whether a static text is a Command Send Action: its first non-whitespace
14
+ * character is `/` (spec 4.3). Whitespace is ECMAScript `trim()` whitespace.
15
+ */
16
+ export declare function isCommandSendActionText(text: string): boolean;
17
+ /** Grapheme-cluster reading of an icon candidate (spec 4.3). */
18
+ export interface EmojiClusterScan {
19
+ /** Grapheme clusters in the candidate, emoji or not. */
20
+ readonly clusters: number;
21
+ /** Whether every grapheme cluster is an emoji cluster. */
22
+ readonly emojiOnly: boolean;
23
+ }
24
+ /**
25
+ * Segment an icon candidate into grapheme clusters and report whether each one
26
+ * is an emoji cluster, so validation can tell `too-long` from `not-emoji`.
27
+ */
28
+ export declare function scanEmojiClusters(icon: string): EmojiClusterScan;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Normalized Quick Action domain types (spec 4.1, 4.2) plus the field-issue
3
+ * vocabulary every validation entry point reports through (spec 4.3).
4
+ * Every type here is plain JSON: no cordis, React, DOM or storage handles.
5
+ */
6
+ /** Author-assigned, package-scoped, permanent identity of a Preset Quick Action. */
7
+ export type PresetActionId = string;
8
+ /** UUID minted when a Custom Quick Action is created; stable for its whole life. */
9
+ export type CustomActionId = string;
10
+ /**
11
+ * Action-type discriminant. The first release is always `'send'`; it is not a
12
+ * configuration field. Keeping the tag lets a later Insert Action ship without
13
+ * raising `schemaVersion` or rewriting stored user data (spec 4.1, 16.1).
14
+ */
15
+ export type QuickActionKind = 'send';
16
+ /** The three global Quick Action Layouts (spec 8.1). */
17
+ export type QuickActionLayout = 'ribbon' | 'bar' | 'launcher';
18
+ /** Every layout value, in the order the management panel offers them. */
19
+ export declare const QUICK_ACTION_LAYOUTS: readonly QuickActionLayout[];
20
+ /** Author-owned, user-read-only action from the Preset Catalog (spec 4.1). */
21
+ export interface PresetQuickAction {
22
+ readonly id: PresetActionId;
23
+ readonly kind: QuickActionKind;
24
+ readonly label: string;
25
+ readonly text: string;
26
+ readonly icon?: string;
27
+ readonly confirm: boolean;
28
+ }
29
+ /** User-owned action stored in Settings (spec 4.1). */
30
+ export interface CustomQuickActionValue {
31
+ readonly kind: QuickActionKind;
32
+ readonly label: string;
33
+ readonly text: string;
34
+ readonly icon?: string;
35
+ readonly confirm: boolean;
36
+ readonly enabled: boolean;
37
+ readonly clonedFromPresetId?: PresetActionId;
38
+ }
39
+ /**
40
+ * A stored custom entry this release cannot render: its `kind` is not `'send'`
41
+ * (real data from a higher version) or its label/text are not readable strings.
42
+ * Preserved verbatim — never displayed, counted, edited or rewritten (spec 5.3).
43
+ * Every field stays opaque on purpose, `kind` included: this release must hand
44
+ * the record back to the version that wrote it exactly as it found it.
45
+ */
46
+ export interface QuickActionTombstone {
47
+ readonly [field: string]: unknown;
48
+ }
49
+ /** One entry of `userActionsById`: either a live send action or a preserved tombstone. */
50
+ export type StoredQuickActionValue = CustomQuickActionValue | QuickActionTombstone;
51
+ /** Reference into the single mixed order shared by presets and custom actions (spec 4.1). */
52
+ export type QuickActionRef = {
53
+ readonly source: 'preset';
54
+ readonly id: PresetActionId;
55
+ } | {
56
+ readonly source: 'custom';
57
+ readonly id: CustomActionId;
58
+ };
59
+ /**
60
+ * The identity of a reference as one comparable string. Source and id together
61
+ * are the identity: the same id under a different source is a different action.
62
+ */
63
+ export declare function quickActionRefKey(ref: QuickActionRef): string;
64
+ /**
65
+ * User difference stored against one Preset Action ID (spec 4.2). `hidden` is the
66
+ * only field this release writes; any other field belongs to the version that
67
+ * wrote it and is carried through untouched.
68
+ */
69
+ export interface PresetQuickActionState {
70
+ readonly hidden?: boolean;
71
+ readonly [field: string]: unknown;
72
+ }
73
+ /** The only persisted namespace shape (spec 4.2). */
74
+ export interface QuickActionSettingsV1 {
75
+ readonly schemaVersion: 1;
76
+ readonly layout: QuickActionLayout;
77
+ readonly userActionsById: Readonly<Record<CustomActionId, StoredQuickActionValue>>;
78
+ readonly actionOrder: readonly QuickActionRef[];
79
+ readonly presetStateById: Readonly<Record<PresetActionId, PresetQuickActionState>>;
80
+ }
81
+ /** Fields a validation issue can be attached to. */
82
+ export type QuickActionField = 'id' | 'kind' | 'label' | 'text' | 'icon' | 'confirm' | 'enabled' | 'clonedFromPresetId';
83
+ /** Why a field was rejected. Combined with `field` it addresses one dictionary entry. */
84
+ export type QuickActionIssueReason = 'invalid-type' | 'missing' | 'blank' | 'too-long' | 'reserved-placeholder' | 'not-emoji' | 'duplicate' | 'unsupported';
85
+ /** One field-level rejection, reported identically by config loading, forms and mutations. */
86
+ export interface QuickActionFieldIssue {
87
+ readonly field: QuickActionField;
88
+ readonly reason: QuickActionIssueReason;
89
+ }
@@ -0,0 +1,40 @@
1
+ import type { QuickActionFieldIssue } from './types.js';
2
+ /** Label limit, in Unicode code points, measured after trimming (spec 4.3). */
3
+ export declare const QUICK_ACTION_LABEL_MAX_CODE_POINTS = 40;
4
+ /** Static text limit, in Unicode code points, measured without trimming (spec 4.3). */
5
+ export declare const QUICK_ACTION_TEXT_MAX_CODE_POINTS = 4000;
6
+ /** Icon limit, in emoji grapheme clusters (spec 4.3). */
7
+ export declare const QUICK_ACTION_ICON_MAX_CLUSTERS = 4;
8
+ /** What the management form edits; an empty `icon` means the action has no icon. */
9
+ export interface QuickActionDraft {
10
+ readonly label: string;
11
+ readonly text: string;
12
+ readonly icon: string;
13
+ readonly confirm: boolean;
14
+ }
15
+ /** The stored content of a validated draft, shared by preset and custom actions. */
16
+ export interface QuickActionContent {
17
+ readonly label: string;
18
+ readonly text: string;
19
+ readonly icon: string | undefined;
20
+ readonly confirm: boolean;
21
+ }
22
+ /** Either the content to persist, or every field that has to be fixed first. */
23
+ export type QuickActionDraftResult = {
24
+ readonly ok: true;
25
+ readonly value: QuickActionContent;
26
+ } | {
27
+ readonly ok: false;
28
+ readonly issues: readonly QuickActionFieldIssue[];
29
+ };
30
+ /** Trimmed label, or the reason it cannot be stored. */
31
+ export declare function labelIssue(label: string): QuickActionFieldIssue | undefined;
32
+ /** The reason a static text cannot be stored, if any. */
33
+ export declare function textIssue(text: string): QuickActionFieldIssue | undefined;
34
+ /** The reason a non-empty icon cannot be stored, if any. */
35
+ export declare function iconIssue(icon: string): QuickActionFieldIssue | undefined;
36
+ /**
37
+ * Validate one edited or created action. Issues come back in field declaration
38
+ * order — label, text, icon — so the form can render them deterministically.
39
+ */
40
+ export declare function validateQuickActionDraft(draft: QuickActionDraft): QuickActionDraftResult;