@graview/primitives 0.1.65 β 0.1.66
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/app-bar.d.ts +88 -15
- package/dist/app-bar.d.ts.map +1 -1
- package/dist/app-bar.js +176 -130
- package/dist/app-bar.js.map +1 -1
- package/dist/app-menu.d.ts +9 -0
- package/dist/app-menu.d.ts.map +1 -0
- package/dist/app-menu.js +60 -0
- package/dist/app-menu.js.map +1 -0
- package/dist/bar-activity.d.ts +5 -9
- package/dist/bar-activity.d.ts.map +1 -1
- package/dist/bar-activity.js +24 -7
- package/dist/bar-activity.js.map +1 -1
- package/dist/bar-panes.d.ts +5 -5
- package/dist/bar-panes.d.ts.map +1 -1
- package/dist/bar-panes.js +9 -5
- package/dist/bar-panes.js.map +1 -1
- package/dist/bar-suggested.d.ts +9 -1
- package/dist/bar-suggested.d.ts.map +1 -1
- package/dist/bar-suggested.js +11 -17
- package/dist/bar-suggested.js.map +1 -1
- package/dist/better-bar.d.ts +65 -0
- package/dist/better-bar.d.ts.map +1 -0
- package/dist/better-bar.js +252 -0
- package/dist/better-bar.js.map +1 -0
- package/dist/better-panel.d.ts +50 -0
- package/dist/better-panel.d.ts.map +1 -0
- package/dist/better-panel.js +151 -0
- package/dist/better-panel.js.map +1 -0
- package/dist/editable.d.ts +8 -1
- package/dist/editable.d.ts.map +1 -1
- package/dist/editable.js +140 -16
- package/dist/editable.js.map +1 -1
- package/dist/first-look-door.d.ts +2 -2
- package/dist/first-look-door.js +2 -2
- package/dist/first-look.d.ts.map +1 -1
- package/dist/first-look.js +9 -7
- package/dist/first-look.js.map +1 -1
- package/dist/frame.d.ts +2 -2
- package/dist/frame.d.ts.map +1 -1
- package/dist/frame.js +1 -1
- package/dist/frame.js.map +1 -1
- package/dist/inbox-pane.d.ts +31 -0
- package/dist/inbox-pane.d.ts.map +1 -0
- package/dist/inbox-pane.js +89 -0
- package/dist/inbox-pane.js.map +1 -0
- package/dist/inbox.d.ts +17 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +84 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/menu-suggested.d.ts +54 -0
- package/dist/menu-suggested.d.ts.map +1 -0
- package/dist/menu-suggested.js +140 -0
- package/dist/menu-suggested.js.map +1 -0
- package/dist/menu-told-door.js +1 -1
- package/dist/menu-told-door.js.map +1 -1
- package/dist/menu-told.d.ts +7 -0
- package/dist/menu-told.d.ts.map +1 -1
- package/dist/menu-told.js +24 -0
- package/dist/menu-told.js.map +1 -1
- package/dist/notify-sheet.js +2 -2
- package/dist/notify-sheet.js.map +1 -1
- package/dist/page-better.d.ts +13 -0
- package/dist/page-better.d.ts.map +1 -0
- package/dist/page-better.js +714 -0
- package/dist/page-better.js.map +1 -0
- package/dist/page-parts.d.ts +41 -0
- package/dist/page-parts.d.ts.map +1 -0
- package/dist/page-parts.js +100 -0
- package/dist/page-parts.js.map +1 -0
- package/dist/pages.d.ts +2 -3
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +2 -3
- package/dist/pages.js.map +1 -1
- package/dist/profile.d.ts +1 -10
- package/dist/profile.d.ts.map +1 -1
- package/dist/profile.js +2 -3
- package/dist/profile.js.map +1 -1
- package/dist/scene-bar.d.ts +1 -1
- package/dist/scene-bar.d.ts.map +1 -1
- package/dist/scene-bar.js +4 -7
- package/dist/scene-bar.js.map +1 -1
- package/dist/section-better.d.ts +47 -9
- package/dist/section-better.d.ts.map +1 -1
- package/dist/section-better.js +239 -121
- package/dist/section-better.js.map +1 -1
- package/dist/section-door.d.ts +7 -0
- package/dist/section-door.d.ts.map +1 -1
- package/dist/section-door.js +39 -5
- package/dist/section-door.js.map +1 -1
- package/dist/section-menu.d.ts +23 -9
- package/dist/section-menu.d.ts.map +1 -1
- package/dist/section-menu.js +176 -23
- package/dist/section-menu.js.map +1 -1
- package/dist/section-words.d.ts +8 -0
- package/dist/section-words.d.ts.map +1 -1
- package/dist/section-words.js +25 -0
- package/dist/section-words.js.map +1 -1
- package/dist/shell.d.ts +5 -1
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +4 -2
- package/dist/shell.js.map +1 -1
- package/dist/suggested.d.ts +21 -10
- package/dist/suggested.d.ts.map +1 -1
- package/dist/suggested.js +43 -64
- package/dist/suggested.js.map +1 -1
- package/dist/told-ui.d.ts +1 -1
- package/dist/told-ui.d.ts.map +1 -1
- package/dist/told-ui.js +1 -1
- package/dist/told-ui.js.map +1 -1
- package/dist/when-words.d.ts +21 -0
- package/dist/when-words.d.ts.map +1 -0
- package/dist/when-words.js +43 -0
- package/dist/when-words.js.map +1 -0
- package/dist/workbench/activity.d.ts +44 -0
- package/dist/workbench/activity.d.ts.map +1 -1
- package/dist/workbench/activity.js +74 -59
- package/dist/workbench/activity.js.map +1 -1
- package/dist/workbench/inspector.d.ts.map +1 -1
- package/dist/workbench/inspector.js +170 -31
- package/dist/workbench/inspector.js.map +1 -1
- package/dist/workbench/standing.d.ts.map +1 -1
- package/dist/workbench/standing.js +1 -1
- package/dist/workbench/standing.js.map +1 -1
- package/package.json +6 -6
package/dist/section-better.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { layer } from "@graview/core";
|
|
3
3
|
import { compareViewRevision } from "@graview/core/revisions";
|
|
4
4
|
import { useGraview } from "@graview/react/provider";
|
|
5
5
|
import { sectionUnderNow, viewPlaceOf } from "@graview/react/drawing";
|
|
6
|
-
import {
|
|
6
|
+
import { CONTROL_WORDS, describeChange, drawnFindings, lookOf, runDesignAgent } from "@graview/tools/section";
|
|
7
7
|
import { interactionId } from "@graview/tools/inline-ai";
|
|
8
8
|
import { retryingImport } from "@graview/core/retry";
|
|
9
9
|
import { lazyModule } from "@graview/react/provider";
|
|
10
10
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
11
11
|
import { boxOf, SectionOutline } from "./section-outline.js";
|
|
12
|
+
import { readDrawn, Redesigning } from "./better-bar.js";
|
|
13
|
+
import { BetterPanel } from "./better-panel.js";
|
|
12
14
|
export { SectionOutline };
|
|
13
|
-
import { nameParts, partLines, placeOfSection } from "./section-words.js";
|
|
14
|
-
import { VISUALLY_HIDDEN } from "./primitives/measure.js";
|
|
15
|
+
import { aboutOf, nameParts, partLines, placeOfSection } from "./section-words.js";
|
|
15
16
|
/*
|
|
16
17
|
* WHAT WAS TOLD (Roadmap f-menu-learns): each revision offered is kept as a
|
|
17
18
|
* telling β the words, the view's section it was said on, the slot that
|
|
@@ -23,10 +24,15 @@ const toldCard = /* @__PURE__ */ lazyModule(retryingImport(() => import("./told-
|
|
|
23
24
|
const Rate = /* @__PURE__ */ toldCard.part((module, props) => _jsx(module.RateTelling, { ...props }), { quiet: true });
|
|
24
25
|
import "./frame.js"; // A door: tied to what the page drew first (TIED DOORS, packages/primitives/src/frame.ts).
|
|
25
26
|
export function SectionDrawn({ ask, open, close }) {
|
|
26
|
-
if (ask)
|
|
27
|
+
if (ask?.part)
|
|
28
|
+
return _jsx(PageCard, { ask: ask, close: close }, ask.at);
|
|
29
|
+
if (ask?.section)
|
|
27
30
|
return _jsx(SectionCard, { ask: ask, close: close }, ask.at);
|
|
28
31
|
return open ? _jsx(SectionOutline, { elements: open }) : null;
|
|
29
32
|
}
|
|
33
|
+
/* A built-in page's card (Roadmap f-make-any-page-better), fetched the first time it is asked for. */
|
|
34
|
+
const pageCard = /* @__PURE__ */ lazyModule(retryingImport(() => import("./page-better.js")));
|
|
35
|
+
const PageCard = /* @__PURE__ */ pageCard.part((module, props) => _jsx(module.PageCard, { ...props }), { what: "Make this better" });
|
|
30
36
|
const WIDTH = 360;
|
|
31
37
|
const GAP = 12;
|
|
32
38
|
/** The card's place: beside the box where the window has room, else under it, else over it. */
|
|
@@ -116,17 +122,34 @@ function judged(handle, before, live, liveRefused, draft, source) {
|
|
|
116
122
|
void handle;
|
|
117
123
|
return { findings, notes };
|
|
118
124
|
}
|
|
125
|
+
/** The labels a view's drawing shows for its records: each keyed row's words. */
|
|
126
|
+
function keyedLabels(root) {
|
|
127
|
+
if (!root)
|
|
128
|
+
return [];
|
|
129
|
+
return [...new Set([...root.querySelectorAll("[data-key]")].map((one) => (one.textContent ?? "").replace(/\s+/g, " ").trim()).filter((label) => label.length > 1 && label.length < 80))].slice(0, 40);
|
|
130
|
+
}
|
|
119
131
|
export function SectionCard({ ask, close }) {
|
|
120
|
-
const { ai, aiDesk, principal, keptViews } = useGraview();
|
|
132
|
+
const { ai, aiDesk, aiHere, session, principal, keptViews } = useGraview();
|
|
121
133
|
const [section, setSection] = useState(ask.section);
|
|
122
134
|
const place = placeOfSection(ask.section);
|
|
123
135
|
const handle = useRef(viewPlaceOf(place));
|
|
124
136
|
const draft = useRef(null);
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
const [phase, setPhase] = useState(ask.mode === "highlight" ? { at: "highlighting" } : ask.mode === "describe" ? { at: "asking" } : { at: "working" });
|
|
128
|
-
const [words, setWords] = useState("");
|
|
137
|
+
const [phase, setPhase] = useState(ask.mode === "highlight" ? { at: "highlighting" } : { at: "open" });
|
|
138
|
+
/* The view's code before any step, the page as it was read, the drafts that passed, the conversation and this turn's steps. */
|
|
129
139
|
const original = useRef(undefined);
|
|
140
|
+
const before = useRef(null);
|
|
141
|
+
const steps = useRef([]);
|
|
142
|
+
const talk = useRef([]);
|
|
143
|
+
const events = useRef([]);
|
|
144
|
+
const since = useRef(Date.now());
|
|
145
|
+
const stopping = useRef(null);
|
|
146
|
+
const pointedAt = useRef(null);
|
|
147
|
+
/** What the conversation is about, where the person pointed at a part of the section. */
|
|
148
|
+
const aboutNow = useRef(null);
|
|
149
|
+
const [covering, setCovering] = useState(false);
|
|
150
|
+
const [comparing, setComparing] = useState(false);
|
|
151
|
+
const [, setTick] = useState(0);
|
|
152
|
+
const redraw = useCallback(() => setTick((one) => one + 1), []);
|
|
130
153
|
/* The telling of the revision on offer, kept with the host's door (`ai.told`), and how it ended. */
|
|
131
154
|
const [telling, setTelling] = useState(null);
|
|
132
155
|
const tellingNow = useRef(null);
|
|
@@ -135,23 +158,32 @@ export function SectionCard({ ask, close }) {
|
|
|
135
158
|
if (now && ai.told)
|
|
136
159
|
void toldCard.load().then((module) => module.changeTelling(ai.told, now.id, { outcome }), () => undefined);
|
|
137
160
|
}, [ai.told]);
|
|
138
|
-
/** Keeps what was said and decided as a telling: offered as a draft ("previewed"), or
|
|
161
|
+
/** Keeps what was said and decided as a telling: offered as a draft ("previewed"), or not. */
|
|
139
162
|
const tell = useCallback((words, on, refused, stages, ms, interaction) => {
|
|
140
163
|
const keeps = ai.told;
|
|
141
164
|
if (!keeps)
|
|
142
165
|
return;
|
|
143
|
-
const slot = stages.at(-1)?.stage === "thinking" ? "full" : stages.length > 0 ? "light" : undefined;
|
|
144
166
|
void toldCard.load().then((module) => {
|
|
145
|
-
const kept = module.keepTelling(keeps, { words, target: { on: "place", place: on.view }, decided: refused ? { type: "refusal" } : { type: "view", section: on.name },
|
|
167
|
+
const kept = module.keepTelling(keeps, { words, target: { on: "place", place: on.view }, decided: refused ? { type: "refusal" } : { type: "view", section: on.name }, slot: "full", stages: stages.map((one) => ({ stage: one.stage === "thinking" ? "full" : "light", ms: one.ms, model: one.model })), ms, outcome: refused ? "abandoned" : "previewed", interaction }, principal);
|
|
146
168
|
tellingNow.current = kept ?? null;
|
|
147
169
|
setTelling(kept ?? null);
|
|
148
170
|
}, () => undefined);
|
|
149
171
|
}, [ai.told, principal]);
|
|
150
172
|
/* Put away: a draft not kept goes with the card, and the view stands as it was. */
|
|
151
173
|
useEffect(() => () => {
|
|
174
|
+
stopping.current?.abort();
|
|
152
175
|
draft.current?.dispose();
|
|
153
176
|
draft.current = null;
|
|
154
177
|
}, []);
|
|
178
|
+
/* GRAVIEW IS HERE WHILE IT WORKS, as it is beside the quick ask. */
|
|
179
|
+
const working = phase.at === "working";
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
if (!working)
|
|
182
|
+
return;
|
|
183
|
+
aiHere({ participant: `agent:graview-for-${principal.id ?? "anyone"}:${session}`, kind: "agent", name: "Graview", ...(principal.id ? { onBehalfOf: principal.id } : {}), over: null, robot: { at: null, mode: "thinking" } });
|
|
184
|
+
return () => aiHere(null);
|
|
185
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
186
|
+
}, [working]);
|
|
155
187
|
const entryOf = useCallback((said, outcome, stages, extra = {}) => ({
|
|
156
188
|
id: interactionId(),
|
|
157
189
|
at: new Date().toISOString(),
|
|
@@ -165,95 +197,196 @@ export function SectionCard({ ask, close }) {
|
|
|
165
197
|
usage: stages.flatMap((one) => (one.usage ? [{ tier: one.stage === "light" ? "fast" : "thinking", model: one.model, inputTokens: one.usage.inputTokens, outputTokens: one.usage.outputTokens }] : [])),
|
|
166
198
|
...extra,
|
|
167
199
|
}), [principal, section.view]);
|
|
168
|
-
/**
|
|
169
|
-
const
|
|
200
|
+
/** The draft drawn in the view's place: the code given, drawn again. */
|
|
201
|
+
const drawDraft = useCallback(async (source) => {
|
|
202
|
+
draft.current?.dispose();
|
|
170
203
|
const view = handle.current;
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
204
|
+
const drawn = view.draft(source);
|
|
205
|
+
draft.current = drawn;
|
|
206
|
+
await drawn.drawn;
|
|
207
|
+
return drawn;
|
|
208
|
+
}, []);
|
|
209
|
+
/** The view as the person last saw it: the newest draft that passed, or the view as it was. */
|
|
210
|
+
const showLatest = useCallback(async () => {
|
|
211
|
+
const latest = steps.current.at(-1);
|
|
212
|
+
setComparing(false);
|
|
213
|
+
if (latest)
|
|
214
|
+
await drawDraft(latest.source);
|
|
215
|
+
else {
|
|
216
|
+
draft.current?.dispose();
|
|
217
|
+
draft.current = null;
|
|
218
|
+
}
|
|
219
|
+
}, [drawDraft]);
|
|
220
|
+
const say = useCallback((who, words) => {
|
|
221
|
+
talk.current.push({ who, said: words });
|
|
222
|
+
redraw();
|
|
223
|
+
}, [redraw]);
|
|
224
|
+
const askedWords = () => talk.current.filter((one) => one.who === "person").map((one) => one.said).join(" ");
|
|
225
|
+
/** One turn of the agent on the section: it looks, drafts the whole script with the section changed, is told what the checks found, and drafts again. */
|
|
226
|
+
const askFor = useCallback(async (words) => {
|
|
227
|
+
const view = handle.current;
|
|
228
|
+
if (!view || phase.at === "working")
|
|
229
|
+
return;
|
|
230
|
+
const pointed = pointedAt.current;
|
|
231
|
+
pointedAt.current = null;
|
|
232
|
+
// The person's words as they typed them: what they are about is said beside them, never glued on.
|
|
233
|
+
if (pointed)
|
|
234
|
+
aboutNow.current = pointed;
|
|
235
|
+
say("person", words);
|
|
236
|
+
events.current = [];
|
|
237
|
+
since.current = Date.now();
|
|
238
|
+
setPhase({ at: "working" });
|
|
239
|
+
const live = await view.source();
|
|
240
|
+
if (live === undefined) {
|
|
241
|
+
say("graview", "This view can't be changed here.");
|
|
242
|
+
return setPhase({ at: "open" });
|
|
243
|
+
}
|
|
244
|
+
original.current ??= live;
|
|
245
|
+
const liveDrawing = view.drawing();
|
|
181
246
|
const liveRefused = view.refused();
|
|
182
|
-
|
|
183
|
-
const
|
|
247
|
+
before.current ??= readDrawn(liveDrawing.root?.parentElement ?? liveDrawing.root ?? place, keyedLabels(liveDrawing.root));
|
|
248
|
+
const was = before.current;
|
|
249
|
+
const stop = new AbortController();
|
|
250
|
+
stopping.current = stop;
|
|
251
|
+
const started = Date.now();
|
|
252
|
+
const turnBegan = steps.current.length;
|
|
253
|
+
const result = await runDesignAgent({
|
|
184
254
|
ai,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
...(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
255
|
+
part: { name: section.name, slot: "place" },
|
|
256
|
+
manifest: { name: view.name, attach: "view", cardinality: "many" },
|
|
257
|
+
talk: talk.current,
|
|
258
|
+
keep: { title: "", headings: was.headings, records: was.records.map((one) => one.label), says: Object.fromEntries(was.records.flatMap((one) => (one.facts?.length || one.under ? [[one.label, { ...(one.facts?.length ? { shows: one.facts } : {}), ...(one.under ? { under: one.under } : {}) }]] : []))) },
|
|
259
|
+
current: steps.current.at(-1)?.source ?? live,
|
|
260
|
+
section: section.name,
|
|
261
|
+
noun: "records",
|
|
262
|
+
...(aboutNow.current ? { about: aboutNow.current } : {}),
|
|
263
|
+
host: {
|
|
264
|
+
look: () => lookOf(readDrawn(view.drawing().root?.parentElement ?? place, was.records.map((one) => one.label))),
|
|
265
|
+
read: () => ({ shows: shownBy(section.elements) }),
|
|
266
|
+
draft: async (source) => {
|
|
267
|
+
setCovering(true);
|
|
268
|
+
try {
|
|
269
|
+
const refused = await view.check(source);
|
|
270
|
+
if (refused.length > 0) {
|
|
271
|
+
await showLatest();
|
|
272
|
+
return { findings: refused, drew: undefined };
|
|
273
|
+
}
|
|
274
|
+
const drawn = await drawDraft(source);
|
|
275
|
+
const { failed } = await drawn.drawn;
|
|
276
|
+
if (failed) {
|
|
277
|
+
await showLatest();
|
|
278
|
+
return { findings: [`The view did not draw: ${failed}`], drew: undefined };
|
|
279
|
+
}
|
|
280
|
+
await new Promise((settled) => requestAnimationFrame(() => requestAnimationFrame(settled)));
|
|
281
|
+
const root = drawn.drawing().root;
|
|
282
|
+
const now = readDrawn(root?.parentElement ?? root, was.records.map((one) => one.label));
|
|
283
|
+
// And every control it draws pressed, headless and nothing applied: one that does nothing, throws or is refused goes back to the agent.
|
|
284
|
+
const pressed = view.presses ? await view.presses(source).catch(() => undefined) : undefined;
|
|
285
|
+
const findings = [...judged(view, original.current, liveDrawing, liveRefused, drawn, source).findings, ...drawnFindings(was, now, { asked: askedWords() }), ...(pressed?.problems ?? []).slice(0, 5).map((one) => `${one.control}: ${one.says}`)];
|
|
286
|
+
if (findings.length > 0)
|
|
287
|
+
await showLatest();
|
|
288
|
+
else {
|
|
289
|
+
steps.current.push({ source, said: describeChange(section.name, was, now) ?? `β${section.name}β has changed. Compare Before and After.`, entry: entryOf(`Make β${section.name}β better: ${askedWords()}`, "offered", []) });
|
|
290
|
+
setComparing(false);
|
|
291
|
+
}
|
|
292
|
+
return { findings, drew: lookOf(now) };
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
setCovering(false);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
onEvent: (event) => {
|
|
300
|
+
// The words as the model writes them stand in for the step it is on, and give way to the step once it is said.
|
|
301
|
+
if (events.current.at(-1)?.kind === "streaming")
|
|
302
|
+
events.current.pop();
|
|
303
|
+
events.current.push(event);
|
|
304
|
+
redraw();
|
|
205
305
|
},
|
|
306
|
+
signal: stop.signal,
|
|
206
307
|
});
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
setPhase({ at: "draft"
|
|
219
|
-
}, [ai, aiDesk, entryOf, section, tell]);
|
|
220
|
-
|
|
308
|
+
if (stopping.current !== stop)
|
|
309
|
+
return;
|
|
310
|
+
stopping.current = null;
|
|
311
|
+
if (events.current.at(-1)?.kind === "streaming")
|
|
312
|
+
events.current.pop();
|
|
313
|
+
const offered = steps.current.length > turnBegan;
|
|
314
|
+
const entry = entryOf(`Make β${section.name}β better: ${askedWords()}`, offered ? "offered" : "failed", result.stages, offered ? {} : { failed: result.last?.findings.join(" ") ?? result.said });
|
|
315
|
+
aiDesk.record(entry);
|
|
316
|
+
tell(askedWords(), section, !offered, result.stages, Date.now() - started, entry.id);
|
|
317
|
+
// What a control does is the press check's to say, never the model's.
|
|
318
|
+
say("graview", result.passed && CONTROL_WORDS.test(result.said) ? "Here it is on the page." : result.said);
|
|
319
|
+
setPhase(steps.current.length > 0 ? { at: "draft" } : { at: "open" });
|
|
320
|
+
}, [ai, aiDesk, drawDraft, entryOf, phase.at, place, redraw, say, section, showLatest, tell]);
|
|
321
|
+
/** Stop: at once β the turn's drafts gone, the view as it was before it began. */
|
|
322
|
+
const stopTurn = useCallback(() => {
|
|
323
|
+
if (phase.at !== "working")
|
|
324
|
+
return;
|
|
325
|
+
const stop = stopping.current;
|
|
326
|
+
stopping.current = null;
|
|
327
|
+
stop?.abort();
|
|
328
|
+
setCovering(false);
|
|
329
|
+
const began = new Date(since.current).toISOString();
|
|
330
|
+
steps.current = steps.current.filter((one) => one.entry.at < began);
|
|
331
|
+
void showLatest();
|
|
332
|
+
say("graview", "Stopped. The view is as it was.");
|
|
333
|
+
setPhase(steps.current.length > 0 ? { at: "draft" } : { at: "open" });
|
|
334
|
+
}, [phase.at, say, showLatest]);
|
|
335
|
+
/* Words that came with the ask β typed in the menu, or a suggestion taken β are asked at once. */
|
|
221
336
|
const began = useRef(false);
|
|
222
337
|
useEffect(() => {
|
|
223
|
-
if (ask.mode
|
|
338
|
+
if (began.current || !(ask.mode === "just" || (ask.mode === "describe" && ask.words)))
|
|
224
339
|
return;
|
|
225
340
|
began.current = true;
|
|
226
|
-
void
|
|
227
|
-
}, [ask.mode,
|
|
341
|
+
void askFor(ask.mode === "just" ? "Make it easier to read at a glance" : ask.words);
|
|
342
|
+
}, [ask.mode, ask.words, askFor]);
|
|
343
|
+
const current = steps.current.at(-1);
|
|
344
|
+
const compare = useCallback(async (originalShown) => {
|
|
345
|
+
if (!current || originalShown === comparing)
|
|
346
|
+
return;
|
|
347
|
+
setComparing(originalShown);
|
|
348
|
+
if (originalShown) {
|
|
349
|
+
draft.current?.dispose();
|
|
350
|
+
draft.current = null;
|
|
351
|
+
}
|
|
352
|
+
else
|
|
353
|
+
await drawDraft(current.source);
|
|
354
|
+
}, [current, comparing, drawDraft]);
|
|
228
355
|
const keep = useCallback(async () => {
|
|
229
|
-
|
|
356
|
+
const latest = steps.current.at(-1);
|
|
357
|
+
if (phase.at !== "draft" || !latest || !keptViews?.revise || !handle.current)
|
|
230
358
|
return;
|
|
231
359
|
const view = handle.current;
|
|
232
|
-
const
|
|
233
|
-
setPhase({ at: "keeping"
|
|
360
|
+
const was = (await view.source()) ?? "";
|
|
361
|
+
setPhase({ at: "keeping" });
|
|
234
362
|
try {
|
|
235
|
-
const answer = await keptViews.revise({ name: view.name, title: view.title, source:
|
|
363
|
+
const answer = await keptViews.revise({ name: view.name, title: view.title, source: latest.source, before: was, section: section.name, said: latest.said });
|
|
236
364
|
draft.current?.dispose();
|
|
237
365
|
draft.current = null;
|
|
238
|
-
view.draw(
|
|
239
|
-
|
|
240
|
-
aiDesk.record(entry);
|
|
366
|
+
view.draw(latest.source);
|
|
367
|
+
aiDesk.record({ ...latest.entry, outcome: "accepted" });
|
|
241
368
|
ended("accepted");
|
|
242
|
-
|
|
369
|
+
steps.current = [];
|
|
370
|
+
setPhase({ at: "kept", source: was, ...(answer && answer.note ? { note: answer.note } : {}), answer, entry: latest.entry });
|
|
243
371
|
}
|
|
244
|
-
catch
|
|
245
|
-
|
|
372
|
+
catch {
|
|
373
|
+
say("graview", "It couldn't be kept.");
|
|
374
|
+
setPhase({ at: "draft" });
|
|
246
375
|
}
|
|
247
|
-
}, [phase, keptViews, aiDesk, section.name, ended]);
|
|
376
|
+
}, [phase, keptViews, aiDesk, section.name, ended, say]);
|
|
248
377
|
const discard = useCallback(() => {
|
|
249
378
|
draft.current?.dispose();
|
|
250
379
|
draft.current = null;
|
|
251
|
-
|
|
252
|
-
|
|
380
|
+
const latest = steps.current.at(-1);
|
|
381
|
+
if (latest) {
|
|
382
|
+
aiDesk.record({ ...latest.entry, outcome: "dismissed" });
|
|
253
383
|
ended("declined");
|
|
254
384
|
}
|
|
255
|
-
|
|
256
|
-
|
|
385
|
+
steps.current = [];
|
|
386
|
+
setComparing(false);
|
|
387
|
+
say("graview", "Put back as it was.");
|
|
388
|
+
setPhase({ at: "open" });
|
|
389
|
+
}, [aiDesk, ended, say]);
|
|
257
390
|
const undo = useCallback(async () => {
|
|
258
391
|
if (phase.at !== "kept" || !handle.current)
|
|
259
392
|
return;
|
|
@@ -268,61 +401,44 @@ export function SectionCard({ ask, close }) {
|
|
|
268
401
|
aiDesk.record({ ...phase.entry, outcome: "dismissed" });
|
|
269
402
|
// A quiet sign, never a π.
|
|
270
403
|
ended("undone");
|
|
404
|
+
say("graview", "Undone. The view is as it was.");
|
|
271
405
|
setPhase({ at: "undone" });
|
|
272
406
|
}
|
|
273
|
-
catch
|
|
274
|
-
|
|
407
|
+
catch {
|
|
408
|
+
say("graview", "It couldn't be put back.");
|
|
275
409
|
}
|
|
276
|
-
}, [phase, keptViews, aiDesk, section.name, ended]);
|
|
277
|
-
/*
|
|
278
|
-
useLayoutEffect(() => {
|
|
279
|
-
let frame = 0;
|
|
280
|
-
const follow = () => {
|
|
281
|
-
if (card.current) {
|
|
282
|
-
const drafted = place?.querySelector("[data-graview-section-draft]");
|
|
283
|
-
placeBeside(card.current, drafted ? boxOf([drafted]) : boxOf(section.elements) ?? (place ? boxOf([place]) : null));
|
|
284
|
-
}
|
|
285
|
-
frame = requestAnimationFrame(follow);
|
|
286
|
-
};
|
|
287
|
-
follow();
|
|
288
|
-
return () => cancelAnimationFrame(frame);
|
|
289
|
-
}, [section, place]);
|
|
290
|
-
/* The keyboard: in the field when it asks, else on the card's first button. */
|
|
291
|
-
useEffect(() => {
|
|
292
|
-
if (phase.at === "asking")
|
|
293
|
-
field.current?.focus();
|
|
294
|
-
else if (phase.at !== "highlighting")
|
|
295
|
-
card.current?.querySelector("button[data-first]")?.focus();
|
|
296
|
-
}, [phase.at]);
|
|
297
|
-
/* Escape puts it away: a draft is discarded, the keyboard goes back where it was. */
|
|
410
|
+
}, [phase, keptViews, aiDesk, section.name, ended, say]);
|
|
411
|
+
/* Escape: Stop while it works, else put it away. */
|
|
298
412
|
useEffect(() => {
|
|
299
413
|
const key = (event) => {
|
|
300
|
-
if (event.key !== "Escape")
|
|
414
|
+
if (event.key !== "Escape" || phase.at === "highlighting")
|
|
301
415
|
return;
|
|
302
416
|
event.preventDefault();
|
|
303
|
-
|
|
417
|
+
if (phase.at === "working")
|
|
418
|
+
return stopTurn();
|
|
419
|
+
if (steps.current.length > 0)
|
|
420
|
+
ended("declined");
|
|
421
|
+
close();
|
|
304
422
|
ask.returnTo?.focus?.();
|
|
305
423
|
};
|
|
306
424
|
document.addEventListener("keydown", key);
|
|
307
425
|
return () => document.removeEventListener("keydown", key);
|
|
308
|
-
}, [
|
|
309
|
-
const pointed = phase.at === "asking" || phase.at === "working" ? phase.pointed : undefined;
|
|
426
|
+
}, [phase.at, stopTurn, close, ended, ask.returnTo]);
|
|
310
427
|
if (phase.at === "highlighting") {
|
|
311
|
-
return (_jsx(Highlighter, { place: place,
|
|
428
|
+
return (_jsx(Highlighter, { box: place, root: place?.querySelector("[data-worker-view]")?.shadowRoot?.querySelector("[data-graview-view-root]") ?? undefined, whole: section.elements, cancel: () => setPhase({ at: "open" }), chose: (elements) => {
|
|
312
429
|
const first = elements[0];
|
|
313
430
|
const under = first ? sectionUnderNow(first) : undefined;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
setPhase({ at: "
|
|
431
|
+
setSection(under ?? section);
|
|
432
|
+
pointedAt.current = { name: aboutOf(elements) ?? nameParts(elements.map(readPart)), lines: partLines(elements.map(readPart)) };
|
|
433
|
+
setPhase({ at: "open" });
|
|
317
434
|
} }));
|
|
318
435
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}, style: { display: "grid", gap: 8 }, children: [_jsx("label", { style: VISUALLY_HIDDEN, htmlFor: "graview-section-words", children: phase.pointed ? "What should change here?" : "What should change?" }), _jsx("input", { id: "graview-section-words", ref: field, "data-testid": "section-words", value: words, onChange: (event) => setWords(event.target.value), placeholder: phase.pointed ? "What should change here?" : "What should change?", style: FIELD }), _jsxs("div", { style: ROW, children: [_jsx("button", { type: "submit", "data-first": "", disabled: !words.trim(), style: PRIMARY, children: "Revise" }), _jsx("button", { type: "button", onClick: close, style: QUIET, children: "Cancel" })] })] })) : null, _jsx("p", { role: "status", "aria-live": "polite", "data-testid": "section-status", style: phase.at === "working" || phase.at === "keeping" ? THINKING : MUTED, children: phase.at === "working" ? `Revising β${section.name}ββ¦` : phase.at === "keeping" ? "Keeping itβ¦" : phase.at === "draft" ? phase.said : phase.at === "kept" ? `Kept.${phase.note ? ` ${phase.note}` : ""}` : phase.at === "undone" ? "Put back as it was." : phase.at === "refused" ? phase.said : "" }), phase.at === "draft" ? (_jsxs(_Fragment, { children: [_jsx("p", { "data-testid": "section-draft-line", style: MUTED, children: "Draft \u2014 shown in place, not kept." }), phase.notes.map((note) => (_jsx("p", { "data-testid": "section-note", style: MUTED, children: note }, note))), _jsxs("div", { style: ROW, children: [_jsx("button", { type: "button", "data-first": "", "data-testid": "section-keep", onClick: () => void keep(), style: PRIMARY, children: "Keep" }), _jsx("button", { type: "button", "data-testid": "section-discard", onClick: discard, style: QUIET, children: "Discard" })] })] })) : null, phase.at === "kept" ? (_jsxs("div", { style: ROW, children: [_jsx("button", { type: "button", "data-first": "", "data-testid": "section-undo", onClick: () => void undo(), style: QUIET, children: "Undo" }), _jsx("button", { type: "button", onClick: close, style: QUIET, children: "Close" })] })) : null, phase.at === "refused" || phase.at === "undone" ? (_jsx("div", { style: ROW, children: _jsx("button", { type: "button", "data-first": "", onClick: close, style: QUIET, children: "Close" }) })) : null, telling && ai.told && phase.at !== "working" && phase.at !== "asking" ? _jsx(Rate, { told: ai.told, telling: telling }) : null] })] }));
|
|
436
|
+
return (_jsxs(_Fragment, { children: [section.elements.length > 0 && phase.at === "open" && talk.current.length === 0 ? _jsx(SectionOutline, { elements: section.elements }) : null, covering ? _jsx(Redesigning, { over: place, name: section.name, said: `Graview is drafting β${section.name}ββ¦` }) : null, _jsx(BetterPanel, { name: section.name, scope: `This changes how β${section.name}β looks for everyone in this app.`, phase: phase.at, talk: talk.current, steps: events.current, since: since.current, working: phase.at === "working" || phase.at === "keeping", ...(current && phase.at === "draft" ? { draft: { said: current.said, before: comparing } } : {}), ...(phase.at === "kept" ? { kept: { ...(phase.note ? { note: phase.note } : {}), undo: () => void undo() } } : {}), about: (pointedAt.current ?? aboutNow.current)?.name ?? section.name, ask: (words) => void askFor(words), just: () => void askFor("Make it easier to read at a glance"), point: () => setPhase({ at: "highlighting" }), stop: stopTurn, compare: (originalShown) => void compare(originalShown), keep: () => void keep(), discard: discard, close: () => {
|
|
437
|
+
stopping.current?.abort();
|
|
438
|
+
if (steps.current.length > 0)
|
|
439
|
+
ended("declined");
|
|
440
|
+
close();
|
|
441
|
+
}, children: telling && ai.told && phase.at !== "working" ? _jsx(Rate, { told: ai.told, telling: telling }) : null })] }));
|
|
326
442
|
}
|
|
327
443
|
/**
|
|
328
444
|
* HIGHLIGHT AREA: an overlay over the view. A drag draws a rectangle and
|
|
@@ -330,10 +446,9 @@ export function SectionCard({ ask, close }) {
|
|
|
330
446
|
* or "Cancel", puts it away; "The whole section" takes the section itself
|
|
331
447
|
* for a keyboard.
|
|
332
448
|
*/
|
|
333
|
-
function Highlighter({ place, section, cancel, chose }) {
|
|
449
|
+
export function Highlighter({ box: place, root, whole, wholeSaid = "The whole section", cancel, chose }) {
|
|
334
450
|
const cover = useRef(null);
|
|
335
451
|
const [drag, setDrag] = useState(null);
|
|
336
|
-
const root = place?.querySelector("[data-worker-view]")?.shadowRoot?.querySelector("[data-graview-view-root]") ?? undefined;
|
|
337
452
|
useLayoutEffect(() => {
|
|
338
453
|
let frame = 0;
|
|
339
454
|
const follow = () => {
|
|
@@ -377,8 +492,9 @@ function Highlighter({ place, section, cancel, chose }) {
|
|
|
377
492
|
event.preventDefault();
|
|
378
493
|
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
379
494
|
setDrag({ x0: event.clientX, y0: event.clientY, x1: event.clientX, y1: event.clientY });
|
|
380
|
-
}, onPointerMove: (event) => setDrag((was) => (was ? { ...was, x1: event.clientX, y1: event.clientY } : was)), onPointerUp: end, onPointerCancel: () => setDrag(null), style: COVER, children: [_jsxs("p", { style: COVER_HINT, children: ["Drag over the part to change, or click one \u00B7", " ", _jsx("button", { type: "button", onPointerDown: (event) => event.stopPropagation(), onClick: () => chose(
|
|
495
|
+
}, onPointerMove: (event) => setDrag((was) => (was ? { ...was, x1: event.clientX, y1: event.clientY } : was)), onPointerUp: end, onPointerCancel: () => setDrag(null), style: COVER, children: [_jsxs("p", { style: COVER_HINT, children: ["Drag over the part to change, or click one \u00B7", " ", _jsx("button", { type: "button", onPointerDown: (event) => event.stopPropagation(), onClick: () => chose(whole), style: LINK, children: wholeSaid }), " ", "\u00B7", " ", _jsx("button", { type: "button", onPointerDown: (event) => event.stopPropagation(), onClick: cancel, style: LINK, children: "Cancel" })] }), box ? _jsx("div", { "data-testid": "section-drag", style: { ...DRAG, left: box.left - (cover.current?.getBoundingClientRect().left ?? 0), top: box.top - (cover.current?.getBoundingClientRect().top ?? 0), width: box.width, height: box.height } }) : null] }));
|
|
381
496
|
}
|
|
497
|
+
export { readPart, placeBeside };
|
|
382
498
|
const CARD = {
|
|
383
499
|
position: "fixed",
|
|
384
500
|
zIndex: layer("popover"),
|
|
@@ -408,4 +524,6 @@ const PAD = 12;
|
|
|
408
524
|
const COVER = { userSelect: "none", WebkitUserSelect: "none", position: "fixed", zIndex: layer("popover"), boxSizing: "border-box", cursor: "crosshair", touchAction: "none", outline: "2px dashed var(--graview-accent)", background: "color-mix(in srgb, var(--graview-accent) 6%, transparent)" };
|
|
409
525
|
const COVER_HINT = { position: "absolute", left: 8, top: 8, margin: 0, padding: "4px 10px", borderRadius: 999, fontSize: "0.8125rem", color: "var(--graview-ink)", background: "var(--graview-float, var(--graview-panel))", border: "1px solid var(--graview-edge)", boxShadow: "var(--graview-lift-low)", cursor: "default" };
|
|
410
526
|
const DRAG = { position: "absolute", border: "2px solid var(--graview-accent)", background: "color-mix(in srgb, var(--graview-accent) 14%, transparent)", borderRadius: 4, pointerEvents: "none" };
|
|
527
|
+
/** The card's look, shared with a built-in page's card. */
|
|
528
|
+
export const LOOK = { CARD, HEAD, MUTED, THINKING, ROW, FIELD, PRIMARY, QUIET };
|
|
411
529
|
//# sourceMappingURL=section-better.js.map
|