@ai-matrx/content-ir-react 0.11.10 → 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/CHANGELOG.md +48 -0
- package/dist/directive-host-Bl-ZgJWp.d.cts +116 -0
- package/dist/directive-host-Bl-ZgJWp.d.ts +116 -0
- package/dist/directives.cjs +219 -25
- package/dist/directives.cjs.map +1 -1
- package/dist/directives.d.cts +64 -3
- package/dist/directives.d.ts +64 -3
- package/dist/directives.js +209 -14
- package/dist/directives.js.map +1 -1
- package/dist/{host-types-C8jR31Wk.d.cts → host-types-BgROpyjE.d.cts} +1 -1
- package/dist/{host-types-IUEDmso7.d.ts → host-types-Cz-1pGHz.d.ts} +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts +3 -3
- package/dist/host.d.ts +3 -3
- package/dist/host.js.map +1 -1
- package/dist/index.cjs +278 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -4
- package/dist/index.d.ts +65 -4
- package/dist/index.js +256 -61
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +271 -85
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +248 -61
- package/dist/react.js.map +1 -1
- package/dist/{route-B9U-9QCw.d.cts → route-DxK1SoPh.d.cts} +54 -1
- package/dist/{route-gNpJ8zzW.d.ts → route-JGZfl2nL.d.ts} +54 -1
- package/dist/route.d.cts +1 -1
- package/dist/route.d.ts +1 -1
- package/package.json +1 -1
- package/dist/directive-host-DAb7Keg8.d.cts +0 -63
- package/dist/directive-host-DAb7Keg8.d.ts +0 -63
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0 — 2026-09-30
|
|
4
|
+
|
|
5
|
+
- **An update/delete card names the record it changes.** Rows for
|
|
6
|
+
`directive_v1_update_*` / `directive_v1_delete_*` items carrying an `id` render
|
|
7
|
+
the EXISTING record through the new host seam `renderRecord` (its live name, a
|
|
8
|
+
way to open it). With no seam the row reads "Task 1a2b3c4d" — never "Item",
|
|
9
|
+
and never an update's NEW title passed off as the record's name.
|
|
10
|
+
- **An update card lists what it changes** — every field the item sets,
|
|
11
|
+
"Due date → 2026-10-15", "Assignee id → cleared" (`itemChanges`, exported with
|
|
12
|
+
`itemRecordId`, `fieldLabel`, `recordFallbackName`, `TARGETING_CLASSES`).
|
|
13
|
+
- **Apply tells the truth about where it is.** New host seam `ask(request)` runs
|
|
14
|
+
BEFORE anything is applied; the control stays idle (disabled) while it is open
|
|
15
|
+
and only a yes moves it to "Applying…". A no leaves the card exactly as it was.
|
|
16
|
+
- **The tally is a door.** `DirectiveApplyResult` gains optional `message` (the
|
|
17
|
+
server's receipt sentence, shown instead of "Applied 1") and `records`
|
|
18
|
+
(`{noun, id}[]`, drawn through `renderRecord` with `context: "applied"` and
|
|
19
|
+
`trashed` for a delete).
|
|
20
|
+
|
|
21
|
+
### Consumer action
|
|
22
|
+
|
|
23
|
+
- Wire `ask` (your consequence dialog) and stop asking inside `confirm`, or your
|
|
24
|
+
card will still read "Applying…" while your dialog is up.
|
|
25
|
+
- Wire `renderRecord` to your live-name + open primitive, and return `message`
|
|
26
|
+
and `records` from `confirm` (the server's receipt `message` and
|
|
27
|
+
`resource_kind`/`resource_ids`).
|
|
28
|
+
|
|
29
|
+
## 0.11.11 — 2026-09-25
|
|
30
|
+
|
|
31
|
+
- **THE PROVISIONAL TIER in `DirectiveRender`.** A directive whose JSON has not
|
|
32
|
+
finished arriving (its head already names a reserved `directive_v…` slug, the
|
|
33
|
+
document does not parse yet) now renders `DirectiveProvisionalCard` — the
|
|
34
|
+
catalog title ("Run Create agent definition") with a "Writing…" state — instead
|
|
35
|
+
of the raw growing JSON in a `<pre>`. Found live on the agent builder's
|
|
36
|
+
`directive_v1_action_create_agent_definition` in the agent run window.
|
|
37
|
+
- New optional `streaming?: boolean` prop on `DirectiveRender`. Pass `false` once
|
|
38
|
+
the host knows the stream has ended: an undecodable head then reads "Cut off
|
|
39
|
+
before it finished — nothing can be applied." with what arrived one click
|
|
40
|
+
away, never "Writing…" forever. Omitted = may still be arriving.
|
|
41
|
+
- A WHOLE document that parses but does not decode keeps the existing behaviour
|
|
42
|
+
(reported through the host, rendered raw) — it is never dressed as streaming.
|
|
43
|
+
- New exports: `DirectiveProvisionalCard`, `provisionalDirectiveSlug`.
|
|
44
|
+
|
|
45
|
+
### Consumer action
|
|
46
|
+
|
|
47
|
+
- Pass your stream state to `DirectiveRender` as `streaming` (e.g. a markdown
|
|
48
|
+
block renderer's "stream active" flag) so a truncated directive reads as cut
|
|
49
|
+
off. Hosts that pass nothing still get the provisional card mid-stream.
|
|
50
|
+
|
|
3
51
|
## 0.11.10
|
|
4
52
|
|
|
5
53
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { DecodedDirective, DirectiveNounCatalog, DirectiveItemKindLookup } from '@ai-matrx/content-ir';
|
|
4
|
+
|
|
5
|
+
interface DirectiveApplyResult {
|
|
6
|
+
applied: number;
|
|
7
|
+
failed: number;
|
|
8
|
+
/**
|
|
9
|
+
* The server's own receipt sentence ("Deleted task “Ship it”."), shown in
|
|
10
|
+
* place of a bare count. Never composed client-side.
|
|
11
|
+
*/
|
|
12
|
+
message?: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* The records the apply created or changed, so the tally is a way IN to them
|
|
15
|
+
* (no dead ends). Rendered through `renderRecord`; absent → the sentence alone.
|
|
16
|
+
*/
|
|
17
|
+
records?: DirectiveRecordRef[];
|
|
18
|
+
}
|
|
19
|
+
/** One record a directive names: the noun it is, and its id. */
|
|
20
|
+
interface DirectiveRecordRef {
|
|
21
|
+
noun: string;
|
|
22
|
+
id: string;
|
|
23
|
+
}
|
|
24
|
+
interface DirectiveRecordProps extends DirectiveRecordRef {
|
|
25
|
+
/**
|
|
26
|
+
* What to show until (or unless) the host resolves the live name —
|
|
27
|
+
* "Task 1a2b3c4d". Never blank, never "Item".
|
|
28
|
+
*/
|
|
29
|
+
fallback: string;
|
|
30
|
+
/** Where it is drawn: an item row, the confirm dialog, or the applied tally. */
|
|
31
|
+
context: "row" | "dialog" | "applied";
|
|
32
|
+
/**
|
|
33
|
+
* True when the record is (about to be) in the trash — the host may still
|
|
34
|
+
* name it but should not promise it opens as a live record.
|
|
35
|
+
*/
|
|
36
|
+
trashed?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** What the person is being asked to approve — everything the card knows. */
|
|
39
|
+
interface DirectiveAskRequest {
|
|
40
|
+
directive: DecodedDirective;
|
|
41
|
+
/** The items, exactly as they will be sent. */
|
|
42
|
+
items: Record<string, unknown>[];
|
|
43
|
+
/** The noun's human label ("Task"), from the host's catalog when it has one. */
|
|
44
|
+
nounLabel: string;
|
|
45
|
+
}
|
|
46
|
+
interface DirectiveOpenItemOptions {
|
|
47
|
+
/** The item, `__kind`-stamped when the shape has an item kind. */
|
|
48
|
+
item: Record<string, unknown>;
|
|
49
|
+
/** The content-IR kind this item IS, or null when the shape has none. */
|
|
50
|
+
itemKind: string | null;
|
|
51
|
+
/** What the item is called — the same title the card shows. */
|
|
52
|
+
title: string;
|
|
53
|
+
/** "Run Create agent definition · item 1 of 1". */
|
|
54
|
+
subtitle: string | null;
|
|
55
|
+
directive: DecodedDirective;
|
|
56
|
+
index: number;
|
|
57
|
+
total: number;
|
|
58
|
+
}
|
|
59
|
+
interface DirectiveCopyProps {
|
|
60
|
+
label: string;
|
|
61
|
+
/** The value to copy — the whole shell or one item. */
|
|
62
|
+
value: unknown;
|
|
63
|
+
/** The item's kind, when copying an item. */
|
|
64
|
+
kind?: string | null;
|
|
65
|
+
size: "icon" | "xs";
|
|
66
|
+
}
|
|
67
|
+
interface DirectiveHost {
|
|
68
|
+
/**
|
|
69
|
+
* Apply a side-effect directive as the user (the server re-validates every
|
|
70
|
+
* item against the registered model and runs the ONE handler; idempotent).
|
|
71
|
+
* Reject with an Error whose message is safe to show.
|
|
72
|
+
*/
|
|
73
|
+
confirm?: (shell: {
|
|
74
|
+
__kind: string;
|
|
75
|
+
items: Record<string, unknown>[];
|
|
76
|
+
}) => Promise<DirectiveApplyResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Ask the person BEFORE anything runs, naming the consequence (the records,
|
|
79
|
+
* what changes). Resolve `true` to go ahead, `false` to leave the card as it
|
|
80
|
+
* was. The Apply control stays idle while this is open — nothing reads as
|
|
81
|
+
* "applying" until the person has said yes. Absent → `confirm` is called
|
|
82
|
+
* directly (a host that asks inside `confirm` keeps working, but its card
|
|
83
|
+
* will read "Applying…" while its dialog is up).
|
|
84
|
+
*/
|
|
85
|
+
ask?: (request: DirectiveAskRequest) => Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Draw ONE existing record by its live name, as a way to open it. Used on
|
|
88
|
+
* update/delete rows (the record that will change), in the host's own ask
|
|
89
|
+
* dialog if it likes, and on the applied tally (what was written). Absent →
|
|
90
|
+
* the fallback text, unlinked.
|
|
91
|
+
*/
|
|
92
|
+
renderRecord?: (props: DirectiveRecordProps) => ReactNode;
|
|
93
|
+
/** Open ONE item to read it properly before approving the write. */
|
|
94
|
+
openItem?: (options: DirectiveOpenItemOptions) => void;
|
|
95
|
+
/** Host copy controls. */
|
|
96
|
+
renderCopy?: (props: DirectiveCopyProps) => ReactNode;
|
|
97
|
+
/** The mirrored noun catalog (label / family / title column). */
|
|
98
|
+
nouns?: DirectiveNounCatalog;
|
|
99
|
+
/** slug → the kind ONE item is (THE DIRECTIVE⇄KIND SEAM), or null. */
|
|
100
|
+
itemKind?: DirectiveItemKindLookup;
|
|
101
|
+
/** Where a decode failure or an unhonourable shell is reported. Default: console.error. */
|
|
102
|
+
reportError?: (message: string) => void;
|
|
103
|
+
}
|
|
104
|
+
interface DirectiveHostProviderProps {
|
|
105
|
+
host: DirectiveHost;
|
|
106
|
+
children: ReactNode;
|
|
107
|
+
}
|
|
108
|
+
declare function DirectiveHostProvider({ host, children }: DirectiveHostProviderProps): react.JSX.Element;
|
|
109
|
+
/**
|
|
110
|
+
* The wired directive host. Unlike the render host this does NOT throw when
|
|
111
|
+
* unwired: a directive still renders (named, itemised, copy-less, apply-less)
|
|
112
|
+
* — degrading the chrome is honest, hiding the content is not.
|
|
113
|
+
*/
|
|
114
|
+
declare function useDirectiveHost(): DirectiveHost;
|
|
115
|
+
|
|
116
|
+
export { type DirectiveApplyResult as D, type DirectiveAskRequest as a, type DirectiveCopyProps as b, type DirectiveHost as c, DirectiveHostProvider as d, type DirectiveHostProviderProps as e, type DirectiveOpenItemOptions as f, type DirectiveRecordProps as g, type DirectiveRecordRef as h, useDirectiveHost as u };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { DecodedDirective, DirectiveNounCatalog, DirectiveItemKindLookup } from '@ai-matrx/content-ir';
|
|
4
|
+
|
|
5
|
+
interface DirectiveApplyResult {
|
|
6
|
+
applied: number;
|
|
7
|
+
failed: number;
|
|
8
|
+
/**
|
|
9
|
+
* The server's own receipt sentence ("Deleted task “Ship it”."), shown in
|
|
10
|
+
* place of a bare count. Never composed client-side.
|
|
11
|
+
*/
|
|
12
|
+
message?: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* The records the apply created or changed, so the tally is a way IN to them
|
|
15
|
+
* (no dead ends). Rendered through `renderRecord`; absent → the sentence alone.
|
|
16
|
+
*/
|
|
17
|
+
records?: DirectiveRecordRef[];
|
|
18
|
+
}
|
|
19
|
+
/** One record a directive names: the noun it is, and its id. */
|
|
20
|
+
interface DirectiveRecordRef {
|
|
21
|
+
noun: string;
|
|
22
|
+
id: string;
|
|
23
|
+
}
|
|
24
|
+
interface DirectiveRecordProps extends DirectiveRecordRef {
|
|
25
|
+
/**
|
|
26
|
+
* What to show until (or unless) the host resolves the live name —
|
|
27
|
+
* "Task 1a2b3c4d". Never blank, never "Item".
|
|
28
|
+
*/
|
|
29
|
+
fallback: string;
|
|
30
|
+
/** Where it is drawn: an item row, the confirm dialog, or the applied tally. */
|
|
31
|
+
context: "row" | "dialog" | "applied";
|
|
32
|
+
/**
|
|
33
|
+
* True when the record is (about to be) in the trash — the host may still
|
|
34
|
+
* name it but should not promise it opens as a live record.
|
|
35
|
+
*/
|
|
36
|
+
trashed?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** What the person is being asked to approve — everything the card knows. */
|
|
39
|
+
interface DirectiveAskRequest {
|
|
40
|
+
directive: DecodedDirective;
|
|
41
|
+
/** The items, exactly as they will be sent. */
|
|
42
|
+
items: Record<string, unknown>[];
|
|
43
|
+
/** The noun's human label ("Task"), from the host's catalog when it has one. */
|
|
44
|
+
nounLabel: string;
|
|
45
|
+
}
|
|
46
|
+
interface DirectiveOpenItemOptions {
|
|
47
|
+
/** The item, `__kind`-stamped when the shape has an item kind. */
|
|
48
|
+
item: Record<string, unknown>;
|
|
49
|
+
/** The content-IR kind this item IS, or null when the shape has none. */
|
|
50
|
+
itemKind: string | null;
|
|
51
|
+
/** What the item is called — the same title the card shows. */
|
|
52
|
+
title: string;
|
|
53
|
+
/** "Run Create agent definition · item 1 of 1". */
|
|
54
|
+
subtitle: string | null;
|
|
55
|
+
directive: DecodedDirective;
|
|
56
|
+
index: number;
|
|
57
|
+
total: number;
|
|
58
|
+
}
|
|
59
|
+
interface DirectiveCopyProps {
|
|
60
|
+
label: string;
|
|
61
|
+
/** The value to copy — the whole shell or one item. */
|
|
62
|
+
value: unknown;
|
|
63
|
+
/** The item's kind, when copying an item. */
|
|
64
|
+
kind?: string | null;
|
|
65
|
+
size: "icon" | "xs";
|
|
66
|
+
}
|
|
67
|
+
interface DirectiveHost {
|
|
68
|
+
/**
|
|
69
|
+
* Apply a side-effect directive as the user (the server re-validates every
|
|
70
|
+
* item against the registered model and runs the ONE handler; idempotent).
|
|
71
|
+
* Reject with an Error whose message is safe to show.
|
|
72
|
+
*/
|
|
73
|
+
confirm?: (shell: {
|
|
74
|
+
__kind: string;
|
|
75
|
+
items: Record<string, unknown>[];
|
|
76
|
+
}) => Promise<DirectiveApplyResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Ask the person BEFORE anything runs, naming the consequence (the records,
|
|
79
|
+
* what changes). Resolve `true` to go ahead, `false` to leave the card as it
|
|
80
|
+
* was. The Apply control stays idle while this is open — nothing reads as
|
|
81
|
+
* "applying" until the person has said yes. Absent → `confirm` is called
|
|
82
|
+
* directly (a host that asks inside `confirm` keeps working, but its card
|
|
83
|
+
* will read "Applying…" while its dialog is up).
|
|
84
|
+
*/
|
|
85
|
+
ask?: (request: DirectiveAskRequest) => Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Draw ONE existing record by its live name, as a way to open it. Used on
|
|
88
|
+
* update/delete rows (the record that will change), in the host's own ask
|
|
89
|
+
* dialog if it likes, and on the applied tally (what was written). Absent →
|
|
90
|
+
* the fallback text, unlinked.
|
|
91
|
+
*/
|
|
92
|
+
renderRecord?: (props: DirectiveRecordProps) => ReactNode;
|
|
93
|
+
/** Open ONE item to read it properly before approving the write. */
|
|
94
|
+
openItem?: (options: DirectiveOpenItemOptions) => void;
|
|
95
|
+
/** Host copy controls. */
|
|
96
|
+
renderCopy?: (props: DirectiveCopyProps) => ReactNode;
|
|
97
|
+
/** The mirrored noun catalog (label / family / title column). */
|
|
98
|
+
nouns?: DirectiveNounCatalog;
|
|
99
|
+
/** slug → the kind ONE item is (THE DIRECTIVE⇄KIND SEAM), or null. */
|
|
100
|
+
itemKind?: DirectiveItemKindLookup;
|
|
101
|
+
/** Where a decode failure or an unhonourable shell is reported. Default: console.error. */
|
|
102
|
+
reportError?: (message: string) => void;
|
|
103
|
+
}
|
|
104
|
+
interface DirectiveHostProviderProps {
|
|
105
|
+
host: DirectiveHost;
|
|
106
|
+
children: ReactNode;
|
|
107
|
+
}
|
|
108
|
+
declare function DirectiveHostProvider({ host, children }: DirectiveHostProviderProps): react.JSX.Element;
|
|
109
|
+
/**
|
|
110
|
+
* The wired directive host. Unlike the render host this does NOT throw when
|
|
111
|
+
* unwired: a directive still renders (named, itemised, copy-less, apply-less)
|
|
112
|
+
* — degrading the chrome is honest, hiding the content is not.
|
|
113
|
+
*/
|
|
114
|
+
declare function useDirectiveHost(): DirectiveHost;
|
|
115
|
+
|
|
116
|
+
export { type DirectiveApplyResult as D, type DirectiveAskRequest as a, type DirectiveCopyProps as b, type DirectiveHost as c, DirectiveHostProvider as d, type DirectiveHostProviderProps as e, type DirectiveOpenItemOptions as f, type DirectiveRecordProps as g, type DirectiveRecordRef as h, useDirectiveHost as u };
|
package/dist/directives.cjs
CHANGED
|
@@ -22,11 +22,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var directives_exports = {};
|
|
23
23
|
__export(directives_exports, {
|
|
24
24
|
ApplyDirectiveButton: () => ApplyDirectiveButton,
|
|
25
|
+
CHANGE_VALUE_MAX: () => CHANGE_VALUE_MAX,
|
|
25
26
|
DirectiveFallbackCard: () => DirectiveFallbackCard,
|
|
26
27
|
DirectiveHostProvider: () => DirectiveHostProvider,
|
|
28
|
+
DirectiveProvisionalCard: () => DirectiveProvisionalCard,
|
|
27
29
|
DirectiveRender: () => DirectiveRender,
|
|
28
30
|
SideEffectDirectiveCard: () => SideEffectDirectiveCard,
|
|
31
|
+
TARGETING_CLASSES: () => TARGETING_CLASSES,
|
|
32
|
+
fieldLabel: () => fieldLabel,
|
|
29
33
|
getDirectiveRenderer: () => getDirectiveRenderer,
|
|
34
|
+
itemChanges: () => itemChanges,
|
|
35
|
+
itemRecordId: () => itemRecordId,
|
|
36
|
+
provisionalDirectiveSlug: () => provisionalDirectiveSlug,
|
|
37
|
+
recordFallbackName: () => recordFallbackName,
|
|
30
38
|
registerDirectiveRenderer: () => registerDirectiveRenderer,
|
|
31
39
|
resetDirectiveRenderers: () => resetDirectiveRenderers,
|
|
32
40
|
useDirectiveHost: () => useDirectiveHost
|
|
@@ -66,6 +74,63 @@ function resetDirectiveRenderers() {
|
|
|
66
74
|
|
|
67
75
|
// directives/ApplyDirectiveButton.tsx
|
|
68
76
|
var import_react2 = require("react");
|
|
77
|
+
var import_content_ir2 = require("@ai-matrx/content-ir");
|
|
78
|
+
|
|
79
|
+
// directives/item-changes.ts
|
|
80
|
+
var TARGETING_CLASSES = /* @__PURE__ */ new Set(["update", "delete"]);
|
|
81
|
+
var NOT_A_CHANGE = /* @__PURE__ */ new Set(["__kind", "id"]);
|
|
82
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
83
|
+
var CHANGE_VALUE_MAX = 60;
|
|
84
|
+
function itemRecordId(item) {
|
|
85
|
+
const id = item.id;
|
|
86
|
+
if (typeof id === "string" && id.trim()) return id.trim();
|
|
87
|
+
if (typeof id === "number") return String(id);
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function fieldLabel(key) {
|
|
91
|
+
const spaced = key.replace(/_/g, " ").trim();
|
|
92
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
93
|
+
}
|
|
94
|
+
function recordFallbackName(nounLabel3, id) {
|
|
95
|
+
const short = UUID_RE.test(id) ? id.slice(0, 8) : id.length > 12 ? `${id.slice(0, 12)}\u2026` : id;
|
|
96
|
+
return `${nounLabel3} ${short}`;
|
|
97
|
+
}
|
|
98
|
+
function describeValue(value) {
|
|
99
|
+
if (value === null || value === void 0) return { value: "cleared", full: "cleared", clears: true };
|
|
100
|
+
if (typeof value === "string") {
|
|
101
|
+
const trimmed = value.trim();
|
|
102
|
+
if (!trimmed) return { value: "cleared", full: "cleared", clears: true };
|
|
103
|
+
const oneLine = trimmed.replace(/\s+/g, " ");
|
|
104
|
+
const shown = oneLine.length > CHANGE_VALUE_MAX ? `${oneLine.slice(0, CHANGE_VALUE_MAX - 1)}\u2026` : oneLine;
|
|
105
|
+
return { value: shown, full: trimmed, clears: false };
|
|
106
|
+
}
|
|
107
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
108
|
+
return { value: String(value), full: String(value), clears: false };
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
const n2 = value.length;
|
|
112
|
+
const text2 = n2 === 0 ? "cleared" : `${n2} ${n2 === 1 ? "entry" : "entries"}`;
|
|
113
|
+
return { value: text2, full: safeJson(value), clears: n2 === 0 };
|
|
114
|
+
}
|
|
115
|
+
const n = Object.keys(value).length;
|
|
116
|
+
const text = n === 0 ? "cleared" : `${n} ${n === 1 ? "field" : "fields"}`;
|
|
117
|
+
return { value: text, full: safeJson(value), clears: n === 0 };
|
|
118
|
+
}
|
|
119
|
+
function safeJson(value) {
|
|
120
|
+
try {
|
|
121
|
+
return JSON.stringify(value, null, 2);
|
|
122
|
+
} catch {
|
|
123
|
+
return String(value);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function itemChanges(item) {
|
|
127
|
+
const out = [];
|
|
128
|
+
for (const [key, raw] of Object.entries(item)) {
|
|
129
|
+
if (NOT_A_CHANGE.has(key)) continue;
|
|
130
|
+
out.push({ key, label: fieldLabel(key), ...describeValue(raw) });
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
69
134
|
|
|
70
135
|
// directives/icons.tsx
|
|
71
136
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -183,10 +248,38 @@ function ApplyDirectiveButton({
|
|
|
183
248
|
const items = directive.items;
|
|
184
249
|
async function handleApply() {
|
|
185
250
|
if (!confirm) return;
|
|
251
|
+
if (host.ask) {
|
|
252
|
+
setState({ status: "asking" });
|
|
253
|
+
let ok = false;
|
|
254
|
+
try {
|
|
255
|
+
ok = await host.ask({
|
|
256
|
+
directive,
|
|
257
|
+
items,
|
|
258
|
+
nounLabel: (0, import_content_ir2.nounLabel)(directive.noun, host.nouns)
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
setState({
|
|
262
|
+
status: "error",
|
|
263
|
+
message: error instanceof Error && error.message ? error.message : "We could not ask you to confirm this, so nothing was done."
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!ok) {
|
|
268
|
+
setState({ status: "idle" });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
setElapsed(0);
|
|
186
273
|
setState({ status: "applying" });
|
|
187
274
|
try {
|
|
188
275
|
const result = await confirm({ __kind: directive.slug, items });
|
|
189
|
-
setState({
|
|
276
|
+
setState({
|
|
277
|
+
status: "applied",
|
|
278
|
+
applied: result.applied,
|
|
279
|
+
failed: result.failed,
|
|
280
|
+
message: result.message?.trim() ? result.message.trim() : null,
|
|
281
|
+
records: result.records ?? []
|
|
282
|
+
});
|
|
190
283
|
onApplied?.();
|
|
191
284
|
} catch (error) {
|
|
192
285
|
setState({
|
|
@@ -197,15 +290,26 @@ function ApplyDirectiveButton({
|
|
|
197
290
|
}
|
|
198
291
|
if (state.status === "applied") {
|
|
199
292
|
const bad = state.failed > 0;
|
|
293
|
+
const trashed = directive.directiveClass === "delete";
|
|
294
|
+
const label2 = (0, import_content_ir2.nounLabel)(directive.noun, host.nouns);
|
|
295
|
+
const records = host.renderRecord ? state.records : [];
|
|
200
296
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
201
297
|
"span",
|
|
202
298
|
{
|
|
203
|
-
className: `inline-flex items-center gap-1 text-xs ${bad ? "text-destructive" : "text-primary"}`,
|
|
299
|
+
className: `inline-flex min-w-0 flex-wrap items-center gap-1 text-xs ${bad ? "text-destructive" : "text-primary"}`,
|
|
300
|
+
"data-apply-state": "applied",
|
|
204
301
|
children: [
|
|
205
|
-
bad ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AlertIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckCircleIcon, { className: "h-3.5 w-3.5" }),
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
302
|
+
bad ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AlertIcon, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckCircleIcon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
303
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
304
|
+
state.message ?? `Applied ${state.applied}`,
|
|
305
|
+
bad && !state.message ? ` \xB7 ${state.failed} failed` : ""
|
|
306
|
+
] }),
|
|
307
|
+
records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "inline-flex min-w-0 items-center", children: host.renderRecord?.({
|
|
308
|
+
...record,
|
|
309
|
+
fallback: recordFallbackName(label2, record.id),
|
|
310
|
+
context: "applied",
|
|
311
|
+
trashed
|
|
312
|
+
}) }, `${record.noun}:${record.id}`))
|
|
209
313
|
]
|
|
210
314
|
}
|
|
211
315
|
);
|
|
@@ -216,7 +320,7 @@ function ApplyDirectiveButton({
|
|
|
216
320
|
{
|
|
217
321
|
type: "button",
|
|
218
322
|
onClick: () => void handleApply(),
|
|
219
|
-
disabled: state.status === "applying" || items.length === 0,
|
|
323
|
+
disabled: state.status === "applying" || state.status === "asking" || items.length === 0,
|
|
220
324
|
className: "inline-flex items-center gap-1 rounded-md border border-border bg-background px-2 py-1 text-xs font-medium hover:bg-accent disabled:opacity-50",
|
|
221
325
|
children: [
|
|
222
326
|
state.status === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlayIcon, { className: "h-3.5 w-3.5" }),
|
|
@@ -233,7 +337,7 @@ function ApplyDirectiveButton({
|
|
|
233
337
|
|
|
234
338
|
// directives/SideEffectDirectiveCard.tsx
|
|
235
339
|
var import_react3 = require("react");
|
|
236
|
-
var
|
|
340
|
+
var import_content_ir3 = require("@ai-matrx/content-ir");
|
|
237
341
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
238
342
|
var ROWS_BEFORE_FOLD = 3;
|
|
239
343
|
function ItemRow({
|
|
@@ -243,12 +347,16 @@ function ItemRow({
|
|
|
243
347
|
total
|
|
244
348
|
}) {
|
|
245
349
|
const host = useDirectiveHost();
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
350
|
+
const recordId = TARGETING_CLASSES.has(directive.directiveClass) ? itemRecordId(item) : null;
|
|
351
|
+
if (recordId) {
|
|
352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TargetRow, { directive, item, recordId });
|
|
353
|
+
}
|
|
354
|
+
const titleColumn = (0, import_content_ir3.nounTitleColumn)(directive.noun, host.nouns);
|
|
355
|
+
const title = (0, import_content_ir3.itemTitle)(item, titleColumn, index, total);
|
|
356
|
+
const subtitle = (0, import_content_ir3.itemSubtitle)(item);
|
|
357
|
+
const facts = (0, import_content_ir3.itemFacts)(item);
|
|
250
358
|
const kind = host.itemKind?.(directive.slug) ?? null;
|
|
251
|
-
const display = (0,
|
|
359
|
+
const display = (0, import_content_ir3.directiveDisplay)(directive.directiveClass, directive.noun, host.nouns);
|
|
252
360
|
const openItem = host.openItem;
|
|
253
361
|
const body = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
254
362
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0 shrink-0 truncate font-medium text-foreground", children: title }),
|
|
@@ -267,7 +375,7 @@ function ItemRow({
|
|
|
267
375
|
type: "button",
|
|
268
376
|
onClick: () => openItem({
|
|
269
377
|
// Stamped so the panel's kind pipeline types it from its own first key.
|
|
270
|
-
item: (0,
|
|
378
|
+
item: (0, import_content_ir3.asKindInstance)(item, kind) ?? item,
|
|
271
379
|
itemKind: kind,
|
|
272
380
|
title,
|
|
273
381
|
subtitle: `${display.title} \xB7 item ${index + 1} of ${total}`,
|
|
@@ -283,12 +391,47 @@ function ItemRow({
|
|
|
283
391
|
host.renderCopy?.({ label: title, value: item, kind, size: "xs" })
|
|
284
392
|
] });
|
|
285
393
|
}
|
|
394
|
+
function TargetRow({
|
|
395
|
+
directive,
|
|
396
|
+
item,
|
|
397
|
+
recordId
|
|
398
|
+
}) {
|
|
399
|
+
const host = useDirectiveHost();
|
|
400
|
+
const fallback = recordFallbackName((0, import_content_ir3.nounLabel)(directive.noun, host.nouns), recordId);
|
|
401
|
+
const changes = directive.directiveClass === "update" ? itemChanges(item) : [];
|
|
402
|
+
const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate font-medium text-foreground", children: fallback });
|
|
403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
|
|
404
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
405
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
|
|
406
|
+
host.renderCopy?.({ label: fallback, value: item, size: "xs" })
|
|
407
|
+
] }),
|
|
408
|
+
directive.directiveClass === "update" ? changes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "px-1 text-[11px] text-muted-foreground", children: "Sets no fields \u2014 nothing would change." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "mt-0.5 space-y-0.5 px-1", children: changes.map((change) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
409
|
+
"li",
|
|
410
|
+
{
|
|
411
|
+
className: "flex min-w-0 items-baseline gap-1.5 text-[11px]",
|
|
412
|
+
title: `${change.label} \u2192 ${change.full}`,
|
|
413
|
+
children: [
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "shrink-0 text-muted-foreground", children: change.label }),
|
|
415
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": true, className: "shrink-0 text-muted-foreground", children: "\u2192" }),
|
|
416
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
417
|
+
"span",
|
|
418
|
+
{
|
|
419
|
+
className: `min-w-0 truncate ${change.clears ? "italic text-muted-foreground" : "text-foreground"}`,
|
|
420
|
+
children: change.value
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
change.key
|
|
426
|
+
)) }) : null
|
|
427
|
+
] });
|
|
428
|
+
}
|
|
286
429
|
function SideEffectDirectiveCard({ directive }) {
|
|
287
430
|
const host = useDirectiveHost();
|
|
288
431
|
const [expanded, setExpanded] = (0, import_react3.useState)(false);
|
|
289
432
|
const items = directive.items;
|
|
290
433
|
const total = items.length;
|
|
291
|
-
const display = (0,
|
|
434
|
+
const display = (0, import_content_ir3.directiveDisplay)(directive.directiveClass, directive.noun, host.nouns);
|
|
292
435
|
const Icon = classIcon(directive.directiveClass, directive.noun);
|
|
293
436
|
const shown = expanded ? items : items.slice(0, ROWS_BEFORE_FOLD);
|
|
294
437
|
const hidden = total - shown.length;
|
|
@@ -327,12 +470,12 @@ function SideEffectDirectiveCard({ directive }) {
|
|
|
327
470
|
}
|
|
328
471
|
|
|
329
472
|
// directives/DirectiveFallbackCard.tsx
|
|
330
|
-
var
|
|
473
|
+
var import_content_ir4 = require("@ai-matrx/content-ir");
|
|
331
474
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
332
475
|
function DirectiveFallbackCard({ directive, reason }) {
|
|
333
476
|
const host = useDirectiveHost();
|
|
334
477
|
const count = directive.items.length;
|
|
335
|
-
const display = (0,
|
|
478
|
+
const display = (0, import_content_ir4.directiveDisplay)(directive.directiveClass, directive.noun, host.nouns);
|
|
336
479
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "my-3 inline-flex flex-wrap items-center gap-2 rounded-md border border-border bg-muted px-2.5 py-1 text-sm", children: [
|
|
337
480
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BoxesIcon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
338
481
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-foreground", children: display.title }),
|
|
@@ -354,19 +497,70 @@ function DirectiveFallbackCard({ directive, reason }) {
|
|
|
354
497
|
] });
|
|
355
498
|
}
|
|
356
499
|
|
|
357
|
-
// directives/
|
|
358
|
-
var
|
|
500
|
+
// directives/DirectiveProvisionalCard.tsx
|
|
501
|
+
var import_content_ir5 = require("@ai-matrx/content-ir");
|
|
359
502
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
360
|
-
|
|
503
|
+
var COMPLETE_SLUG_RE = /^\{\s*"__kind"\s*:\s*"([^"]+)"/;
|
|
504
|
+
function provisionalDirectiveSlug(content) {
|
|
505
|
+
const match = content.trimStart().match(COMPLETE_SLUG_RE);
|
|
506
|
+
return match?.[1] ?? null;
|
|
507
|
+
}
|
|
508
|
+
function DirectiveProvisionalCard({ content, streaming, className }) {
|
|
509
|
+
const host = useDirectiveHost();
|
|
510
|
+
const slug = provisionalDirectiveSlug(content);
|
|
511
|
+
const known = slug ? (0, import_content_ir5.parseDirectiveSlug)(slug) : null;
|
|
512
|
+
const display = known ? (0, import_content_ir5.directiveDisplay)(known.directiveClass, known.noun, host.nouns) : null;
|
|
513
|
+
const Icon = known ? classIcon(known.directiveClass, known.noun) : BoxesIcon;
|
|
514
|
+
const title = display?.title ?? "Structured action";
|
|
515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
516
|
+
"div",
|
|
517
|
+
{
|
|
518
|
+
className: `my-3 rounded-md border border-border bg-card px-2.5 py-1.5${className ? ` ${className}` : ""}`,
|
|
519
|
+
"data-directive": slug ?? void 0,
|
|
520
|
+
"data-directive-state": streaming ? "streaming" : "incomplete",
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-1", children: [
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
524
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-sm font-medium text-foreground", children: title }),
|
|
525
|
+
display?.family ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-xs text-muted-foreground", children: display.family }) : null,
|
|
526
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", role: "status", children: [
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-primary", "aria-hidden": true }),
|
|
528
|
+
"Writing\u2026"
|
|
529
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-xs text-destructive", role: "status", children: "Cut off before it finished \u2014 nothing can be applied." })
|
|
530
|
+
] }),
|
|
531
|
+
streaming ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("details", { className: "mt-1 text-xs text-muted-foreground", children: [
|
|
532
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("summary", { className: "cursor-pointer select-none hover:text-foreground", children: "Show what arrived" }),
|
|
533
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { className: "mt-1 max-h-64 overflow-auto whitespace-pre-wrap break-all rounded bg-muted px-2 py-1 text-[11px]", children: content })
|
|
534
|
+
] })
|
|
535
|
+
]
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// directives/DirectiveRender.tsx
|
|
541
|
+
var import_content_ir6 = require("@ai-matrx/content-ir");
|
|
542
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
543
|
+
function isWholeJson(content) {
|
|
544
|
+
try {
|
|
545
|
+
JSON.parse(content);
|
|
546
|
+
return true;
|
|
547
|
+
} catch {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function DirectiveRender({ content, className, streaming }) {
|
|
361
552
|
const host = useDirectiveHost();
|
|
362
|
-
const directive = (0,
|
|
553
|
+
const directive = (0, import_content_ir6.tryDecodeDirectiveContent)(content, (message) => {
|
|
363
554
|
const line = `[kind-directives] ${message}`;
|
|
364
555
|
if (host.reportError) host.reportError(line);
|
|
365
556
|
else console.error(line);
|
|
366
557
|
});
|
|
367
558
|
if (!directive) {
|
|
559
|
+
if (typeof content === "string" && (0, import_content_ir6.looksLikeDirectiveHead)(content) && !isWholeJson(content)) {
|
|
560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DirectiveProvisionalCard, { content, streaming: streaming !== false, className });
|
|
561
|
+
}
|
|
368
562
|
const raw = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
369
|
-
return /* @__PURE__ */ (0,
|
|
563
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
370
564
|
"pre",
|
|
371
565
|
{
|
|
372
566
|
className: `my-3 overflow-x-auto rounded-md border border-border bg-muted px-3 py-2 text-xs text-muted-foreground${className ? ` ${className}` : ""}`,
|
|
@@ -375,8 +569,8 @@ function DirectiveRender({ content, className }) {
|
|
|
375
569
|
);
|
|
376
570
|
}
|
|
377
571
|
const Renderer = getDirectiveRenderer(directive);
|
|
378
|
-
if (Renderer) return /* @__PURE__ */ (0,
|
|
379
|
-
if (directive.parsed.executes) return /* @__PURE__ */ (0,
|
|
380
|
-
return /* @__PURE__ */ (0,
|
|
572
|
+
if (Renderer) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Renderer, { directive });
|
|
573
|
+
if (directive.parsed.executes) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SideEffectDirectiveCard, { directive });
|
|
574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DirectiveFallbackCard, { directive });
|
|
381
575
|
}
|
|
382
576
|
//# sourceMappingURL=directives.cjs.map
|