@ikenga/contract 0.10.0 → 0.12.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.
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAC;AAIlD;;;;GAIG;AACH,eAAO,MAAM,YAAY;IACvB,4DAA4D;;IAE5D,0DAA0D;;;;;;;;EAE1D,CAAC;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;EAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,gBAAgB;IAC3B,4BAA4B;;IAE5B,iDAAiD;;IAEjD,yEAAyE;;IAEzE,2DAA2D;;IAE3D,mFAAmF;;IAEnF,4DAA4D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE5D,gFAAgF;;QA/BhF,4DAA4D;;QAE5D,0DAA0D;;;;;;;;;IA+B1D;;;;;;OAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe;;IAE1B,4DAA4D;;IAE5D,sDAAsD;;IAEtD,mDAAmD;;QA/BnD,4BAA4B;;QAE5B,iDAAiD;;QAEjD,yEAAyE;;QAEzE,2DAA2D;;QAE3D,mFAAmF;;QAEnF,4DAA4D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAE5D,gFAAgF;;YA/BhF,4DAA4D;;YAE5D,0DAA0D;;;;;;;;;QA+B1D;;;;;;WAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaH,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAIxD,eAAO,MAAM,mBAAmB;IAC9B,qBAAqB;;IAErB,qBAAqB;;IAErB;;;;;OAKG;;IAEH,4EAA4E;;IAE5E,2EAA2E;;IAE3E;;;;OAIG;;IAEH;;;;;;;OAOG;;;;;;;;;;;;;;;;;;EAEH,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB;;IAE9B,wDAAwD;;;QAnCxD,qBAAqB;;QAErB,qBAAqB;;QAErB;;;;;WAKG;;QAEH,4EAA4E;;QAE5E,2EAA2E;;QAE3E;;;;WAIG;;QAEH;;;;;;;WAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUH,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAIhE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAE5D,gFAAgF;;YA/BhF,4DAA4D;;YAE5D,0DAA0D;;;;;;;;;QA+B1D;;;;;;WAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaH,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAIxD,eAAO,MAAM,mBAAmB;IAC9B,qBAAqB;;IAErB,qBAAqB;;IAErB;;;;;OAKG;;IAEH,4EAA4E;;IAE5E,2EAA2E;;IAE3E;;;;OAIG;;IAEH;;;;;;;OAOG;;;;;;;;;;;;;;;;;;EAEH,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB;;IAE9B,wDAAwD;;;QAnCxD,qBAAqB;;QAErB,qBAAqB;;QAErB;;;;;WAKG;;QAEH,4EAA4E;;QAE5E,2EAA2E;;QAE3E;;;;WAIG;;QAEH;;;;;;;WAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUH,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAIhE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,128 @@
1
+ import { z } from 'zod';
2
+ /** Envelope + descriptor wire version. Bumped on non-additive change. */
3
+ export declare const WINDOW_CONTRACT_VERSION: 1;
4
+ /**
5
+ * What a window is.
6
+ * - `primary` — the original "main" window (full chrome).
7
+ * - `single-surface` — a thin detached window hosting one surface (Flavor C).
8
+ * - `pane-set` — a torn-off pane group (Flavor A, Phase 2).
9
+ * - `workspace` — a second full workspace window (Flavor B, Phase 3).
10
+ */
11
+ export declare const WindowKindSchema: z.ZodEnum<["primary", "single-surface", "pane-set", "workspace"]>;
12
+ export type WindowKind = z.infer<typeof WindowKindSchema>;
13
+ export declare const WindowDescriptorSchema: z.ZodObject<{
14
+ /** OS window label. "main" for the primary; "detached-<n>" otherwise. */
15
+ label: z.ZodString;
16
+ kind: z.ZodEnum<["primary", "single-surface", "pane-set", "workspace"]>;
17
+ /** Surface/route ids the window's FE entry mounts. */
18
+ surface_set: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
19
+ /** Project this window is bound to (Flavor B); null = follows primary. */
20
+ project_id: z.ZodDefault<z.ZodNullable<z.ZodString>>;
21
+ /**
22
+ * SQLite `layout_state` partition. A KEY-STRING suffix folded into
23
+ * `scopedKey()` (G-03) — NOT a new column. Usually equals `label`.
24
+ */
25
+ layout_key: z.ZodString;
26
+ }, "strict", z.ZodTypeAny, {
27
+ label: string;
28
+ kind: "primary" | "single-surface" | "pane-set" | "workspace";
29
+ surface_set: string[];
30
+ project_id: string | null;
31
+ layout_key: string;
32
+ }, {
33
+ label: string;
34
+ kind: "primary" | "single-surface" | "pane-set" | "workspace";
35
+ layout_key: string;
36
+ surface_set?: string[] | undefined;
37
+ project_id?: string | null | undefined;
38
+ }>;
39
+ export type WindowDescriptor = z.infer<typeof WindowDescriptorSchema>;
40
+ /**
41
+ * Where a window event goes. The DEFAULT for streaming/data channels is
42
+ * `broadcast` (every window filters its own subscription); only the channels
43
+ * that race (see WINDOW_TARGETED_CHANNELS) carry `window` targeting.
44
+ */
45
+ export declare const WindowEventTargetSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
46
+ kind: z.ZodLiteral<"broadcast">;
47
+ }, "strict", z.ZodTypeAny, {
48
+ kind: "broadcast";
49
+ }, {
50
+ kind: "broadcast";
51
+ }>, z.ZodObject<{
52
+ kind: z.ZodLiteral<"window">;
53
+ label: z.ZodString;
54
+ }, "strict", z.ZodTypeAny, {
55
+ label: string;
56
+ kind: "window";
57
+ }, {
58
+ label: string;
59
+ kind: "window";
60
+ }>]>;
61
+ export type WindowEventTarget = z.infer<typeof WindowEventTargetSchema>;
62
+ /**
63
+ * The base shape every cross-window event rides. `payload` is opaque at the
64
+ * contract layer (each topic refines it); the envelope itself is what the
65
+ * registry + windows agree on.
66
+ */
67
+ export declare const WindowEventEnvelopeSchema: z.ZodObject<{
68
+ v: z.ZodLiteral<1>;
69
+ /** Channel name, e.g. "window://opened". */
70
+ topic: z.ZodString;
71
+ /** Emitting window label, or "core" for the Rust core. */
72
+ source_label: z.ZodString;
73
+ target: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
74
+ kind: z.ZodLiteral<"broadcast">;
75
+ }, "strict", z.ZodTypeAny, {
76
+ kind: "broadcast";
77
+ }, {
78
+ kind: "broadcast";
79
+ }>, z.ZodObject<{
80
+ kind: z.ZodLiteral<"window">;
81
+ label: z.ZodString;
82
+ }, "strict", z.ZodTypeAny, {
83
+ label: string;
84
+ kind: "window";
85
+ }, {
86
+ label: string;
87
+ kind: "window";
88
+ }>]>;
89
+ payload: z.ZodUnknown;
90
+ }, "strict", z.ZodTypeAny, {
91
+ v: 1;
92
+ topic: string;
93
+ source_label: string;
94
+ target: {
95
+ kind: "broadcast";
96
+ } | {
97
+ label: string;
98
+ kind: "window";
99
+ };
100
+ payload?: unknown;
101
+ }, {
102
+ v: 1;
103
+ topic: string;
104
+ source_label: string;
105
+ target: {
106
+ kind: "broadcast";
107
+ } | {
108
+ label: string;
109
+ kind: "window";
110
+ };
111
+ payload?: unknown;
112
+ }>;
113
+ export type WindowEventEnvelope = z.infer<typeof WindowEventEnvelopeSchema>;
114
+ /** Canonical window-lifecycle channels emitted by the Rust core. */
115
+ export declare const WINDOW_TOPICS: {
116
+ readonly opened: "window://opened";
117
+ readonly closed: "window://closed";
118
+ readonly focusChanged: "window://focus-changed";
119
+ };
120
+ export type WindowTopic = (typeof WINDOW_TOPICS)[keyof typeof WINDOW_TOPICS];
121
+ /**
122
+ * Channels that MUST be window-targeted (`emit_to`) rather than broadcast,
123
+ * because a broadcast races across windows (first-reply-wins / wrong-window).
124
+ * Everything not listed here stays broadcast + per-window topic filtering.
125
+ * (plans/multi-window 03-research-internal: the ~25-channel routing table.)
126
+ */
127
+ export declare const WINDOW_TARGETED_CHANNELS: readonly ["screenshot://request", "screenshot://shortcut", "projects:active-changed"];
128
+ //# sourceMappingURL=window.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAC;AAIlD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,mEAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;IAEjC,yEAAyE;;;IAGzE,sDAAsD;;IAEtD,0EAA0E;;IAE1E;;;OAGG;;;;;;;;;;;;;;EAGK,CAAC;AACX,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;IAGlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;;IAGpC,4CAA4C;;IAE5C,0DAA0D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKlD,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,oEAAoE;AACpE,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE7E;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,uFAI3B,CAAC"}
package/dist/window.js ADDED
@@ -0,0 +1,86 @@
1
+ // Multi-window contract — mirrors the Rust source-of-truth in
2
+ // `royalti-io/ikenga` at `src-tauri/src/window/{descriptor,events}.rs`.
3
+ //
4
+ // This is the `G-WINDOW-MODEL` freeze-gate output (plans/multi-window WP-02).
5
+ // It has TWO co-equal halves: the `WindowDescriptor` (what a window IS) and the
6
+ // cross-window event envelope (how windows talk over the shared Rust core).
7
+ // Both are imported by every downstream consumer (the window registry, the thin
8
+ // detached FE entry, Flavor C). Field changes MUST be made in lockstep with the
9
+ // Rust structs; bump WINDOW_CONTRACT_VERSION on any non-additive change.
10
+ import { z } from 'zod';
11
+ /** Envelope + descriptor wire version. Bumped on non-additive change. */
12
+ export const WINDOW_CONTRACT_VERSION = 1;
13
+ // ---------- WindowDescriptor ----------
14
+ /**
15
+ * What a window is.
16
+ * - `primary` — the original "main" window (full chrome).
17
+ * - `single-surface` — a thin detached window hosting one surface (Flavor C).
18
+ * - `pane-set` — a torn-off pane group (Flavor A, Phase 2).
19
+ * - `workspace` — a second full workspace window (Flavor B, Phase 3).
20
+ */
21
+ export const WindowKindSchema = z.enum([
22
+ 'primary',
23
+ 'single-surface',
24
+ 'pane-set',
25
+ 'workspace',
26
+ ]);
27
+ export const WindowDescriptorSchema = z
28
+ .object({
29
+ /** OS window label. "main" for the primary; "detached-<n>" otherwise. */
30
+ label: z.string().min(1),
31
+ kind: WindowKindSchema,
32
+ /** Surface/route ids the window's FE entry mounts. */
33
+ surface_set: z.array(z.string()).default([]),
34
+ /** Project this window is bound to (Flavor B); null = follows primary. */
35
+ project_id: z.string().nullable().default(null),
36
+ /**
37
+ * SQLite `layout_state` partition. A KEY-STRING suffix folded into
38
+ * `scopedKey()` (G-03) — NOT a new column. Usually equals `label`.
39
+ */
40
+ layout_key: z.string().min(1),
41
+ })
42
+ .strict();
43
+ // ---------- Cross-window event envelope ----------
44
+ /**
45
+ * Where a window event goes. The DEFAULT for streaming/data channels is
46
+ * `broadcast` (every window filters its own subscription); only the channels
47
+ * that race (see WINDOW_TARGETED_CHANNELS) carry `window` targeting.
48
+ */
49
+ export const WindowEventTargetSchema = z.discriminatedUnion('kind', [
50
+ z.object({ kind: z.literal('broadcast') }).strict(),
51
+ z.object({ kind: z.literal('window'), label: z.string().min(1) }).strict(),
52
+ ]);
53
+ /**
54
+ * The base shape every cross-window event rides. `payload` is opaque at the
55
+ * contract layer (each topic refines it); the envelope itself is what the
56
+ * registry + windows agree on.
57
+ */
58
+ export const WindowEventEnvelopeSchema = z
59
+ .object({
60
+ v: z.literal(WINDOW_CONTRACT_VERSION),
61
+ /** Channel name, e.g. "window://opened". */
62
+ topic: z.string().min(1),
63
+ /** Emitting window label, or "core" for the Rust core. */
64
+ source_label: z.string().min(1),
65
+ target: WindowEventTargetSchema,
66
+ payload: z.unknown(),
67
+ })
68
+ .strict();
69
+ /** Canonical window-lifecycle channels emitted by the Rust core. */
70
+ export const WINDOW_TOPICS = {
71
+ opened: 'window://opened',
72
+ closed: 'window://closed',
73
+ focusChanged: 'window://focus-changed',
74
+ };
75
+ /**
76
+ * Channels that MUST be window-targeted (`emit_to`) rather than broadcast,
77
+ * because a broadcast races across windows (first-reply-wins / wrong-window).
78
+ * Everything not listed here stays broadcast + per-window topic filtering.
79
+ * (plans/multi-window 03-research-internal: the ~25-channel routing table.)
80
+ */
81
+ export const WINDOW_TARGETED_CHANNELS = [
82
+ 'screenshot://request',
83
+ 'screenshot://shortcut',
84
+ 'projects:active-changed',
85
+ ];
86
+ //# sourceMappingURL=window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"window.js","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,wEAAwE;AACxE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,yEAAyE;AAEzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yEAAyE;AACzE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAU,CAAC;AAElD,yCAAyC;AAEzC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACtC,SAAS;IACT,gBAAgB;IAChB,UAAU;IACV,WAAW;CACX,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACrC,MAAM,CAAC;IACP,yEAAyE;IACzE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,gBAAgB;IACtB,sDAAsD;IACtD,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5C,0EAA0E;IAC1E,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/C;;;OAGG;IACH,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC7B,CAAC;KACD,MAAM,EAAE,CAAC;AAGX,oDAAoD;AAEpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACnE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;IACnD,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;CAC1E,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACxC,MAAM,CAAC;IACP,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC;IACrC,4CAA4C;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,0DAA0D;IAC1D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,MAAM,EAAE,uBAAuB;IAC/B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACpB,CAAC;KACD,MAAM,EAAE,CAAC;AAGX,oEAAoE;AACpE,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,YAAY,EAAE,wBAAwB;CAC7B,CAAC;AAGX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACvC,sBAAsB;IACtB,uBAAuB;IACvB,yBAAyB;CAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikenga/contract",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Shared contract for the Ikenga pkg system: manifest schema, RPC types, Engine interface, capability scopes.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -55,6 +55,10 @@
55
55
  "./pa-actions": {
56
56
  "types": "./dist/pa-actions.d.ts",
57
57
  "import": "./dist/pa-actions.js"
58
+ },
59
+ "./window": {
60
+ "types": "./dist/window.d.ts",
61
+ "import": "./dist/window.js"
58
62
  }
59
63
  },
60
64
  "files": [
package/src/browser.ts CHANGED
@@ -43,6 +43,21 @@ export const BrowserRefSchema = z
43
43
 
44
44
  export type BrowserRef = z.infer<typeof BrowserRefSchema>;
45
45
 
46
+ // ---------- Engine ----------
47
+ //
48
+ // Which underlying browser engine backs a pane. `webkit` (default) is the
49
+ // in-shell child-webview (WebKitGTK on Linux / WKWebView / WebView2). `chrome`
50
+ // is "Managed mode": the shell launches the user's installed Google Chrome with
51
+ // a dedicated `--user-data-dir` + `--remote-debugging-port` and drives it over
52
+ // CDP. The engine is chosen at `open()`; subsequent actions address the pane by
53
+ // id and inherit its engine. See plans/chrome-pkg/01-plan.md §Architecture.
54
+
55
+ export const BROWSER_ENGINES = ['webkit', 'chrome'] as const;
56
+
57
+ export const BrowserEngineSchema = z.enum(BROWSER_ENGINES);
58
+
59
+ export type BrowserEngine = z.infer<typeof BrowserEngineSchema>;
60
+
46
61
  // ---------- Snapshot ----------
47
62
  //
48
63
  // Mirrors the shape of Iyke's `DomResult` so models can move between the
@@ -119,8 +134,17 @@ export const BrowserOpenInputSchema = z
119
134
  * Optional initial rect in main-window-client coordinates. Omitted when
120
135
  * the FE owns layout (the kernel will refuse without a rect; the MCP
121
136
  * layer is expected to supply a sane default if the caller didn't).
137
+ *
138
+ * Ignored for `engine: "chrome"` — Managed Chrome renders in its own OS
139
+ * window, not a shell pane.
122
140
  */
123
141
  rect: RectSchema.optional(),
142
+ /**
143
+ * Which engine backs this pane. Defaults to `"webkit"` (the in-shell
144
+ * child-webview). `"chrome"` is Managed mode (installed Chrome over CDP).
145
+ * The pkg must declare the engine in `capabilities.webview.engines`.
146
+ */
147
+ engine: BrowserEngineSchema.default('webkit'),
124
148
  })
125
149
  .refine((v) => !(v.partition && v.session), {
126
150
  message: 'pass at most one of `partition` / `session`',
@@ -132,6 +156,8 @@ export const BrowserOpenResultSchema = z.object({
132
156
  id: BrowserPaneIdSchema,
133
157
  url: z.string(),
134
158
  partition: z.string(),
159
+ /** The resolved engine backing this pane (`"webkit"` if absent, for back-compat). */
160
+ engine: BrowserEngineSchema.default('webkit'),
135
161
  });
136
162
 
137
163
  export type BrowserOpenResult = z.infer<typeof BrowserOpenResultSchema>;
@@ -147,6 +173,8 @@ export const BrowserListEntrySchema = z.object({
147
173
  url: z.string(),
148
174
  partition: z.string(),
149
175
  focused: z.boolean(),
176
+ /** Which engine backs this pane (`"webkit"` if absent, for back-compat). */
177
+ engine: BrowserEngineSchema.default('webkit'),
150
178
  });
151
179
 
152
180
  export type BrowserListEntry = z.infer<typeof BrowserListEntrySchema>;
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from './artifact.js';
8
8
  export * from './registry.js';
9
9
  export * from './browser.js';
10
10
  export * from './pa-actions.js';
11
+ export * from './window.js';
11
12
 
12
13
  /** This package's own version. */
13
14
  export const CONTRACT_PACKAGE_VERSION = '0.5.0' as const;
package/src/manifest.ts CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { z } from 'zod';
12
12
  import { EngineProvidesSchema } from './engine/index.js';
13
+ import { BrowserEngineSchema } from './browser.js';
13
14
 
14
15
  // v2 (WP-05): added capabilities.sqlite + permissions["sqlite.tables"];
15
16
  // permissions["supabase.tables"] kept as a compat alias for api=1 manifests.
@@ -173,6 +174,11 @@ export const WebviewCapabilitySchema = z.object({
173
174
  child_webviews: z.boolean().default(false),
174
175
  /** Named cookie/data partitions; empty = the implicit "default" partition. */
175
176
  partitions: z.array(z.string()).default([]),
177
+ /** Browser engines this pkg may open panes with. `"webkit"` is the in-shell
178
+ * child-webview; `"chrome"` is Managed mode (installed Chrome over CDP, its
179
+ * own OS window). Defaults to `["webkit"]` so existing manifests are
180
+ * unchanged. Mirrors `engines` in `WebviewCapability` (manifest.rs). */
181
+ engines: z.array(BrowserEngineSchema).default(['webkit']),
176
182
  });
177
183
  export type WebviewCapability = z.infer<typeof WebviewCapabilitySchema>;
178
184
 
package/src/pa-actions.ts CHANGED
@@ -36,6 +36,27 @@ export interface DraftSequence {
36
36
  recipients: number;
37
37
  }
38
38
 
39
+ /** One sub-post in an X/Twitter thread (X allows ≤4 images per tweet). */
40
+ export interface ThreadPost {
41
+ text: string;
42
+ /** Per-sub-post media URLs (X allows ≤4 images per tweet). */
43
+ imageUrls?: string[];
44
+ }
45
+
46
+ /**
47
+ * Media block carried on a Buffer DraftItem. `kind` selects the adapter path:
48
+ * - `'single'` — 0–1 image attached to the post body.
49
+ * - `'carousel'` — 2–10 images (IG ≤10, LI ≤9); adapter uses multi-asset upload.
50
+ * - `'thread'` — ordered sub-posts; adapter posts as N linked Buffer posts (≥2, ≤25).
51
+ */
52
+ export interface SocialMedia {
53
+ kind: 'single' | 'carousel' | 'thread';
54
+ /** single = 0–1 image URL; carousel = 2–10 (platform-dependent). */
55
+ imageUrls?: string[];
56
+ /** thread-kind only: ordered sub-posts (≥2, ≤25). */
57
+ thread?: ThreadPost[];
58
+ }
59
+
39
60
  /**
40
61
  * What an approve-aware action emits per draft — the lean, authoritative
41
62
  * producer shape. The shell derives the panel's `PausedDraft` from this via
@@ -88,6 +109,23 @@ export interface DraftItem {
88
109
  threadCount?: string;
89
110
  /** Explicit section bucket; otherwise derived from `scheduledIso`. */
90
111
  section?: string;
112
+ // ── Buffer / social fields (mirror of payload_json media contract) ──────────
113
+ /**
114
+ * Resolved Buffer channel id — company vs personal LinkedIn disambiguated at
115
+ * enqueue time (not in the adapter). Only set for `channel: 'buffer'` drafts.
116
+ */
117
+ channelId?: string;
118
+ /**
119
+ * First comment text for hashtags / blog URL. Avoids Buffer link-preview
120
+ * attachment errors on LinkedIn. Mapped to `metadata.<platform>.firstComment`.
121
+ */
122
+ firstComment?: string;
123
+ /**
124
+ * Social media block; selects single / carousel / thread adapter path.
125
+ * Kept in lockstep with `payload.schema.ts` `SocialMedia` Zod schema and
126
+ * the worker mirror in `scripts/cron/lib/channels/types.ts`.
127
+ */
128
+ media?: SocialMedia;
91
129
  }
92
130
 
93
131
  /**
@@ -0,0 +1,70 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+ import {
4
+ WINDOW_CONTRACT_VERSION,
5
+ WindowDescriptorSchema,
6
+ WindowEventEnvelopeSchema,
7
+ } from './window.js';
8
+
9
+ // Canonical wire fixtures — the Rust round-trip test
10
+ // (src-tauri/src/window/descriptor.rs + events.rs) asserts the SAME JSON.
11
+ // If these drift, the lockstep contract is broken.
12
+
13
+ const DESCRIPTOR_FIXTURE = {
14
+ label: 'detached-1',
15
+ kind: 'single-surface',
16
+ surface_set: ['chat'],
17
+ project_id: null,
18
+ layout_key: 'detached-1',
19
+ };
20
+
21
+ const ENVELOPE_FIXTURE = {
22
+ v: 1,
23
+ topic: 'window://opened',
24
+ source_label: 'core',
25
+ target: { kind: 'window', label: 'detached-1' },
26
+ payload: { label: 'detached-1' },
27
+ };
28
+
29
+ test('WindowDescriptor parses the canonical fixture and round-trips', () => {
30
+ const parsed = WindowDescriptorSchema.parse(DESCRIPTOR_FIXTURE);
31
+ assert.deepEqual(parsed, DESCRIPTOR_FIXTURE);
32
+ // re-parse the serialized form (round-trip)
33
+ assert.deepEqual(
34
+ WindowDescriptorSchema.parse(JSON.parse(JSON.stringify(parsed))),
35
+ DESCRIPTOR_FIXTURE,
36
+ );
37
+ });
38
+
39
+ test('WindowDescriptor rejects unknown keys (deny_unknown_fields parity)', () => {
40
+ assert.throws(() =>
41
+ WindowDescriptorSchema.parse({ ...DESCRIPTOR_FIXTURE, rogue: true }),
42
+ );
43
+ });
44
+
45
+ test('WindowDescriptor applies defaults for surface_set/project_id', () => {
46
+ const parsed = WindowDescriptorSchema.parse({
47
+ label: 'main',
48
+ kind: 'primary',
49
+ layout_key: 'main',
50
+ });
51
+ assert.deepEqual(parsed.surface_set, []);
52
+ assert.equal(parsed.project_id, null);
53
+ });
54
+
55
+ test('WindowEventEnvelope parses the canonical fixture and round-trips', () => {
56
+ const parsed = WindowEventEnvelopeSchema.parse(ENVELOPE_FIXTURE);
57
+ assert.equal(parsed.v, WINDOW_CONTRACT_VERSION);
58
+ assert.deepEqual(
59
+ WindowEventEnvelopeSchema.parse(JSON.parse(JSON.stringify(parsed))),
60
+ ENVELOPE_FIXTURE,
61
+ );
62
+ });
63
+
64
+ test('WindowEventTarget broadcast variant parses', () => {
65
+ const parsed = WindowEventEnvelopeSchema.parse({
66
+ ...ENVELOPE_FIXTURE,
67
+ target: { kind: 'broadcast' },
68
+ });
69
+ assert.equal(parsed.target.kind, 'broadcast');
70
+ });
package/src/window.ts ADDED
@@ -0,0 +1,100 @@
1
+ // Multi-window contract — mirrors the Rust source-of-truth in
2
+ // `royalti-io/ikenga` at `src-tauri/src/window/{descriptor,events}.rs`.
3
+ //
4
+ // This is the `G-WINDOW-MODEL` freeze-gate output (plans/multi-window WP-02).
5
+ // It has TWO co-equal halves: the `WindowDescriptor` (what a window IS) and the
6
+ // cross-window event envelope (how windows talk over the shared Rust core).
7
+ // Both are imported by every downstream consumer (the window registry, the thin
8
+ // detached FE entry, Flavor C). Field changes MUST be made in lockstep with the
9
+ // Rust structs; bump WINDOW_CONTRACT_VERSION on any non-additive change.
10
+
11
+ import { z } from 'zod';
12
+
13
+ /** Envelope + descriptor wire version. Bumped on non-additive change. */
14
+ export const WINDOW_CONTRACT_VERSION = 1 as const;
15
+
16
+ // ---------- WindowDescriptor ----------
17
+
18
+ /**
19
+ * What a window is.
20
+ * - `primary` — the original "main" window (full chrome).
21
+ * - `single-surface` — a thin detached window hosting one surface (Flavor C).
22
+ * - `pane-set` — a torn-off pane group (Flavor A, Phase 2).
23
+ * - `workspace` — a second full workspace window (Flavor B, Phase 3).
24
+ */
25
+ export const WindowKindSchema = z.enum([
26
+ 'primary',
27
+ 'single-surface',
28
+ 'pane-set',
29
+ 'workspace',
30
+ ]);
31
+ export type WindowKind = z.infer<typeof WindowKindSchema>;
32
+
33
+ export const WindowDescriptorSchema = z
34
+ .object({
35
+ /** OS window label. "main" for the primary; "detached-<n>" otherwise. */
36
+ label: z.string().min(1),
37
+ kind: WindowKindSchema,
38
+ /** Surface/route ids the window's FE entry mounts. */
39
+ surface_set: z.array(z.string()).default([]),
40
+ /** Project this window is bound to (Flavor B); null = follows primary. */
41
+ project_id: z.string().nullable().default(null),
42
+ /**
43
+ * SQLite `layout_state` partition. A KEY-STRING suffix folded into
44
+ * `scopedKey()` (G-03) — NOT a new column. Usually equals `label`.
45
+ */
46
+ layout_key: z.string().min(1),
47
+ })
48
+ .strict();
49
+ export type WindowDescriptor = z.infer<typeof WindowDescriptorSchema>;
50
+
51
+ // ---------- Cross-window event envelope ----------
52
+
53
+ /**
54
+ * Where a window event goes. The DEFAULT for streaming/data channels is
55
+ * `broadcast` (every window filters its own subscription); only the channels
56
+ * that race (see WINDOW_TARGETED_CHANNELS) carry `window` targeting.
57
+ */
58
+ export const WindowEventTargetSchema = z.discriminatedUnion('kind', [
59
+ z.object({ kind: z.literal('broadcast') }).strict(),
60
+ z.object({ kind: z.literal('window'), label: z.string().min(1) }).strict(),
61
+ ]);
62
+ export type WindowEventTarget = z.infer<typeof WindowEventTargetSchema>;
63
+
64
+ /**
65
+ * The base shape every cross-window event rides. `payload` is opaque at the
66
+ * contract layer (each topic refines it); the envelope itself is what the
67
+ * registry + windows agree on.
68
+ */
69
+ export const WindowEventEnvelopeSchema = z
70
+ .object({
71
+ v: z.literal(WINDOW_CONTRACT_VERSION),
72
+ /** Channel name, e.g. "window://opened". */
73
+ topic: z.string().min(1),
74
+ /** Emitting window label, or "core" for the Rust core. */
75
+ source_label: z.string().min(1),
76
+ target: WindowEventTargetSchema,
77
+ payload: z.unknown(),
78
+ })
79
+ .strict();
80
+ export type WindowEventEnvelope = z.infer<typeof WindowEventEnvelopeSchema>;
81
+
82
+ /** Canonical window-lifecycle channels emitted by the Rust core. */
83
+ export const WINDOW_TOPICS = {
84
+ opened: 'window://opened',
85
+ closed: 'window://closed',
86
+ focusChanged: 'window://focus-changed',
87
+ } as const;
88
+ export type WindowTopic = (typeof WINDOW_TOPICS)[keyof typeof WINDOW_TOPICS];
89
+
90
+ /**
91
+ * Channels that MUST be window-targeted (`emit_to`) rather than broadcast,
92
+ * because a broadcast races across windows (first-reply-wins / wrong-window).
93
+ * Everything not listed here stays broadcast + per-window topic filtering.
94
+ * (plans/multi-window 03-research-internal: the ~25-channel routing table.)
95
+ */
96
+ export const WINDOW_TARGETED_CHANNELS = [
97
+ 'screenshot://request',
98
+ 'screenshot://shortcut',
99
+ 'projects:active-changed',
100
+ ] as const;