@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.
- package/dist/browser.d.ts +30 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +23 -0
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +125 -96
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +6 -0
- package/dist/manifest.js.map +1 -1
- package/dist/pa-actions.d.ts +35 -0
- package/dist/pa-actions.d.ts.map +1 -1
- package/dist/pa-actions.js.map +1 -1
- package/dist/registry.d.ts +260 -240
- package/dist/registry.d.ts.map +1 -1
- package/dist/window.d.ts +128 -0
- package/dist/window.d.ts.map +1 -0
- package/dist/window.js +86 -0
- package/dist/window.js.map +1 -0
- package/package.json +5 -1
- package/src/browser.ts +28 -0
- package/src/index.ts +1 -0
- package/src/manifest.ts +6 -0
- package/src/pa-actions.ts +38 -0
- package/src/window.test.ts +70 -0
- package/src/window.ts +100 -0
package/dist/registry.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/window.d.ts
ADDED
|
@@ -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.
|
|
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
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;
|