@tangle-network/agent-app 0.29.0 → 0.31.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/{DesignCanvasEditor-IB2FMBBD.js → DesignCanvasEditor-QQNAZRJQ.js} +3 -3
- package/dist/IntakeInterview-WHVKMNQW.js +8 -0
- package/dist/IntakeInterview-WHVKMNQW.js.map +1 -0
- package/dist/chunk-4K7BZYO7.js +124 -0
- package/dist/chunk-4K7BZYO7.js.map +1 -0
- package/dist/chunk-5I72FRZY.js +212 -0
- package/dist/chunk-5I72FRZY.js.map +1 -0
- package/dist/{chunk-S5CAJX6O.js → chunk-FJUOBNIY.js} +2 -2
- package/dist/{chunk-Y2SZVW35.js → chunk-LVD37K5V.js} +41 -41
- package/dist/chunk-LVD37K5V.js.map +1 -0
- package/dist/chunk-ND3XEGBE.js +119 -0
- package/dist/chunk-ND3XEGBE.js.map +1 -0
- package/dist/chunk-USYXHDKE.js +30 -0
- package/dist/chunk-USYXHDKE.js.map +1 -0
- package/dist/{chunk-N4ZFKQ5C.js → chunk-YYYXGLLQ.js} +2 -2
- package/dist/design-canvas-react/index.js +3 -3
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/index.js +1 -1
- package/dist/intakes/api.d.ts +59 -0
- package/dist/intakes/api.js +63 -0
- package/dist/intakes/api.js.map +1 -0
- package/dist/intakes/drizzle.d.ts +385 -0
- package/dist/intakes/drizzle.js +61 -0
- package/dist/intakes/drizzle.js.map +1 -0
- package/dist/intakes/index.d.ts +42 -0
- package/dist/intakes/index.js +31 -0
- package/dist/intakes/index.js.map +1 -0
- package/dist/intakes-react/index.d.ts +48 -0
- package/dist/intakes-react/index.js +8 -0
- package/dist/intakes-react/index.js.map +1 -0
- package/dist/intakes-react/lazy.d.ts +8 -0
- package/dist/intakes-react/lazy.js +9 -0
- package/dist/intakes-react/lazy.js.map +1 -0
- package/dist/model-Cuj7d-xT.d.ts +116 -0
- package/dist/theme/index.js +1 -1
- package/dist/theme/tokens.css +50 -48
- package/dist/web-react/index.d.ts +6 -1
- package/dist/web-react/index.js +8 -14
- package/dist/web-react/index.js.map +1 -1
- package/package.json +26 -1
- package/dist/chunk-Y2SZVW35.js.map +0 -1
- /package/dist/{DesignCanvasEditor-IB2FMBBD.js.map → DesignCanvasEditor-QQNAZRJQ.js.map} +0 -0
- /package/dist/{chunk-S5CAJX6O.js.map → chunk-FJUOBNIY.js.map} +0 -0
- /package/dist/{chunk-N4ZFKQ5C.js.map → chunk-YYYXGLLQ.js.map} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DesignCanvasEditor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YYYXGLLQ.js";
|
|
4
4
|
import "./chunk-YEWTVDDT.js";
|
|
5
5
|
import "./chunk-V5XZKHCL.js";
|
|
6
6
|
import "./chunk-XWJXZ6WE.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-LVD37K5V.js";
|
|
8
8
|
import "./chunk-M2EBUDZ7.js";
|
|
9
9
|
import "./chunk-HFC4BTWJ.js";
|
|
10
10
|
export {
|
|
11
11
|
DesignCanvasEditor
|
|
12
12
|
};
|
|
13
|
-
//# sourceMappingURL=DesignCanvasEditor-
|
|
13
|
+
//# sourceMappingURL=DesignCanvasEditor-QQNAZRJQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
emptyPayload,
|
|
3
|
+
markComplete,
|
|
4
|
+
payloadComplete,
|
|
5
|
+
withAnswer
|
|
6
|
+
} from "./chunk-USYXHDKE.js";
|
|
7
|
+
import {
|
|
8
|
+
getQuestion,
|
|
9
|
+
validateAnswer
|
|
10
|
+
} from "./chunk-ND3XEGBE.js";
|
|
11
|
+
|
|
12
|
+
// src/intakes/drizzle/store.ts
|
|
13
|
+
import { eq } from "drizzle-orm";
|
|
14
|
+
var IntakeError = class extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "IntakeError";
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function createScopedStore(opts) {
|
|
23
|
+
const { db, graph, table, scopeColumn, scopeValue, insertScope } = opts;
|
|
24
|
+
async function loadRow() {
|
|
25
|
+
const [row] = await db.select().from(table).where(eq(scopeColumn, scopeValue)).limit(1);
|
|
26
|
+
return row ?? null;
|
|
27
|
+
}
|
|
28
|
+
function toState(row) {
|
|
29
|
+
if (!row) {
|
|
30
|
+
return { payload: emptyPayload(graph), completed: false, completedAt: null };
|
|
31
|
+
}
|
|
32
|
+
const payload = normalizePayload(row.payload, row.graphId);
|
|
33
|
+
const completedAt = toDate(row.completedAt);
|
|
34
|
+
return {
|
|
35
|
+
payload,
|
|
36
|
+
completed: completedAt != null && payloadComplete(graph, payload),
|
|
37
|
+
completedAt
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function get() {
|
|
41
|
+
return toState(await loadRow());
|
|
42
|
+
}
|
|
43
|
+
async function upsertPayload(payload, completedAt) {
|
|
44
|
+
const existing = await loadRow();
|
|
45
|
+
if (existing) {
|
|
46
|
+
await db.update(table).set({ graphId: payload.graphId, payload, completedAt, updatedAt: /* @__PURE__ */ new Date() }).where(eq(scopeColumn, scopeValue));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
await db.insert(table).values({
|
|
50
|
+
...insertScope,
|
|
51
|
+
graphId: payload.graphId,
|
|
52
|
+
payload,
|
|
53
|
+
completedAt
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function save(questionId, value) {
|
|
57
|
+
const question = getQuestion(graph, questionId);
|
|
58
|
+
if (!question) throw new IntakeError("unknown-question", `No question '${questionId}' in intake '${graph.id}'`);
|
|
59
|
+
const validity = validateAnswer(question, value);
|
|
60
|
+
if (!validity.ok) {
|
|
61
|
+
throw new IntakeError("invalid-answer", `Answer to '${questionId}' rejected: ${validity.reason}`);
|
|
62
|
+
}
|
|
63
|
+
const current = await get();
|
|
64
|
+
const next = withAnswer(current.payload, questionId, value);
|
|
65
|
+
await upsertPayload(next, current.completedAt);
|
|
66
|
+
return toState({ graphId: next.graphId, payload: next, completedAt: current.completedAt });
|
|
67
|
+
}
|
|
68
|
+
async function complete() {
|
|
69
|
+
const current = await get();
|
|
70
|
+
if (current.payload.graphId !== graph.id) {
|
|
71
|
+
throw new IntakeError("stale-graph", `Intake payload was collected against '${current.payload.graphId}', not '${graph.id}'`);
|
|
72
|
+
}
|
|
73
|
+
if (!payloadComplete(graph, current.payload)) {
|
|
74
|
+
throw new IntakeError("incomplete", `Intake '${graph.id}' has unanswered required questions`);
|
|
75
|
+
}
|
|
76
|
+
const completedPayload = markComplete(current.payload);
|
|
77
|
+
const completedAt = new Date(completedPayload.completedAt);
|
|
78
|
+
await upsertPayload(completedPayload, completedAt);
|
|
79
|
+
return { payload: completedPayload, completed: true, completedAt };
|
|
80
|
+
}
|
|
81
|
+
return { get, save, complete };
|
|
82
|
+
}
|
|
83
|
+
function createUserIntakeStore(opts) {
|
|
84
|
+
return createScopedStore({
|
|
85
|
+
db: opts.db,
|
|
86
|
+
graph: opts.graph,
|
|
87
|
+
table: opts.table,
|
|
88
|
+
scopeColumn: opts.table.userId,
|
|
89
|
+
scopeValue: opts.userId,
|
|
90
|
+
insertScope: { userId: opts.userId }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function createProjectIntakeStore(opts) {
|
|
94
|
+
return createScopedStore({
|
|
95
|
+
db: opts.db,
|
|
96
|
+
graph: opts.graph,
|
|
97
|
+
table: opts.table,
|
|
98
|
+
scopeColumn: opts.table.workspaceId,
|
|
99
|
+
scopeValue: opts.workspaceId,
|
|
100
|
+
insertScope: { workspaceId: opts.workspaceId }
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function normalizePayload(raw, graphId) {
|
|
104
|
+
if (raw && typeof raw === "object" && "answers" in raw) {
|
|
105
|
+
const candidate = raw;
|
|
106
|
+
return {
|
|
107
|
+
graphId: candidate.graphId ?? graphId,
|
|
108
|
+
answers: candidate.answers ?? {},
|
|
109
|
+
...candidate.completedAt ? { completedAt: candidate.completedAt } : {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return { graphId, answers: {} };
|
|
113
|
+
}
|
|
114
|
+
function toDate(value) {
|
|
115
|
+
if (value == null) return null;
|
|
116
|
+
return value instanceof Date ? value : new Date(Number(value) * (Number(value) < 1e12 ? 1e3 : 1));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
IntakeError,
|
|
121
|
+
createUserIntakeStore,
|
|
122
|
+
createProjectIntakeStore
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-4K7BZYO7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/intakes/drizzle/store.ts"],"sourcesContent":["/**\n * Drizzle-backed store over the tables from `createIntakeTables`. One store\n * builder per scope: `createUserIntakeStore` (keyed on userId) and\n * `createProjectIntakeStore` (keyed on workspaceId). Each returns the same\n * three-method surface — `get` / `save` / `complete` — so the api handlers and\n * the UI talk to one shape regardless of scope.\n *\n * Works against any SQLite drizzle driver (better-sqlite3, D1, libsql): the\n * builders are awaited, never `.run()`/`.all()`, so sync and async drivers\n * behave identically.\n *\n * Validation is fail-loud and pure (from the `./intakes` leaf). `save` runs\n * `validateAnswer` before writing, so an invalid answer can never enter the\n * payload; `complete` runs `payloadComplete` and refuses to stamp an\n * incomplete (or stale-graph) intake — it throws a typed `IntakeError` rather\n * than silently writing a half-finished onboarding state.\n *\n * `getProjectIntakeStore` pins `workspaceId` in every WHERE clause; RBAC runs\n * in the route before the store is built, but the scope key is enforced here\n * too, so a leaked id can never read or write across the boundary.\n *\n * Imports `drizzle-orm`, so this is a subpath, never re-exported from root.\n */\n\nimport { eq } from 'drizzle-orm'\nimport type { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core'\nimport {\n type IntakeAnswerValue,\n type IntakeGraph,\n getQuestion,\n validateAnswer,\n} from '../model'\nimport {\n type IntakePayload,\n emptyPayload,\n markComplete,\n payloadComplete,\n withAnswer,\n} from '../completion'\nimport type { ProjectIntakeTable, UserIntakeTable } from './schema'\n\n/** Any SQLite drizzle database — `any` erases driver-specific generics so\n * better-sqlite3, D1, and libsql handles all fit. */\nexport type IntakeDatabase = BaseSQLiteDatabase<'sync' | 'async', any, any>\n\n/** A loaded intake: the payload plus whether it is complete against the graph. */\nexport interface IntakeState {\n payload: IntakePayload\n completed: boolean\n completedAt: Date | null\n}\n\nexport type IntakeErrorCode = 'invalid-answer' | 'unknown-question' | 'incomplete' | 'stale-graph'\n\n/** Thrown by store mutations on a refused write — callers map it to a 4xx. */\nexport class IntakeError extends Error {\n readonly code: IntakeErrorCode\n constructor(code: IntakeErrorCode, message: string) {\n super(message)\n this.name = 'IntakeError'\n this.code = code\n }\n}\n\n/** The three-method store surface, identical for both scopes. */\nexport interface IntakeStore {\n /** Load the current intake, or seed an empty payload when none exists yet. */\n get(): Promise<IntakeState>\n /** Validate and persist one answer; returns the updated state. */\n save(questionId: string, value: IntakeAnswerValue): Promise<IntakeState>\n /** Stamp the intake complete; throws IntakeError when not yet completable. */\n complete(): Promise<IntakeState>\n}\n\ninterface IntakeTableShape {\n id: any\n graphId: any\n payload: any\n completedAt: any\n updatedAt: any\n}\n\ninterface ScopedStoreOptions {\n db: IntakeDatabase\n graph: IntakeGraph\n table: IntakeTableShape\n /** The scope column (userIntake.userId or projectIntake.workspaceId). */\n scopeColumn: any\n scopeValue: string\n /** Extra columns to set on insert (the scope key). */\n insertScope: Record<string, unknown>\n}\n\nfunction createScopedStore(opts: ScopedStoreOptions): IntakeStore {\n const { db, graph, table, scopeColumn, scopeValue, insertScope } = opts\n\n async function loadRow() {\n const [row] = await db\n .select()\n .from(table as any)\n .where(eq(scopeColumn, scopeValue))\n .limit(1)\n return (row ?? null) as\n | { id: string; graphId: string; payload: unknown; completedAt: Date | number | null }\n | null\n }\n\n function toState(\n row: { graphId: string; payload: unknown; completedAt: Date | number | null } | null,\n ): IntakeState {\n if (!row) {\n return { payload: emptyPayload(graph), completed: false, completedAt: null }\n }\n const payload = normalizePayload(row.payload, row.graphId)\n const completedAt = toDate(row.completedAt)\n return {\n payload,\n completed: completedAt != null && payloadComplete(graph, payload),\n completedAt,\n }\n }\n\n async function get(): Promise<IntakeState> {\n return toState(await loadRow())\n }\n\n async function upsertPayload(payload: IntakePayload, completedAt: Date | null) {\n const existing = await loadRow()\n if (existing) {\n await db\n .update(table as any)\n .set({ graphId: payload.graphId, payload, completedAt, updatedAt: new Date() })\n .where(eq(scopeColumn, scopeValue))\n return\n }\n await db.insert(table as any).values({\n ...insertScope,\n graphId: payload.graphId,\n payload,\n completedAt,\n })\n }\n\n async function save(questionId: string, value: IntakeAnswerValue): Promise<IntakeState> {\n const question = getQuestion(graph, questionId)\n if (!question) throw new IntakeError('unknown-question', `No question '${questionId}' in intake '${graph.id}'`)\n const validity = validateAnswer(question, value)\n if (!validity.ok) {\n throw new IntakeError('invalid-answer', `Answer to '${questionId}' rejected: ${validity.reason}`)\n }\n\n const current = await get()\n const next = withAnswer(current.payload, questionId, value)\n await upsertPayload(next, current.completedAt)\n return toState({ graphId: next.graphId, payload: next, completedAt: current.completedAt })\n }\n\n async function complete(): Promise<IntakeState> {\n const current = await get()\n if (current.payload.graphId !== graph.id) {\n throw new IntakeError('stale-graph', `Intake payload was collected against '${current.payload.graphId}', not '${graph.id}'`)\n }\n if (!payloadComplete(graph, current.payload)) {\n throw new IntakeError('incomplete', `Intake '${graph.id}' has unanswered required questions`)\n }\n const completedPayload = markComplete(current.payload)\n const completedAt = new Date(completedPayload.completedAt!)\n await upsertPayload(completedPayload, completedAt)\n return { payload: completedPayload, completed: true, completedAt }\n }\n\n return { get, save, complete }\n}\n\nexport interface CreateUserIntakeStoreOptions {\n db: IntakeDatabase\n /** The user-intake table from createIntakeTables. */\n table: UserIntakeTable\n /** The intake definition this store collects answers against. */\n graph: IntakeGraph\n /** The user whose onboarding this is. */\n userId: string\n}\n\n/** Build the per-user onboarding store, scoped to one userId. */\nexport function createUserIntakeStore(opts: CreateUserIntakeStoreOptions): IntakeStore {\n return createScopedStore({\n db: opts.db,\n graph: opts.graph,\n table: opts.table as unknown as IntakeTableShape,\n scopeColumn: opts.table.userId,\n scopeValue: opts.userId,\n insertScope: { userId: opts.userId },\n })\n}\n\nexport interface CreateProjectIntakeStoreOptions {\n db: IntakeDatabase\n /** The project-intake table from createIntakeTables (workspace scope). */\n table: ProjectIntakeTable\n graph: IntakeGraph\n /** The workspace this intake is attached to. */\n workspaceId: string\n}\n\n/** Build the per-project store, scoped to one workspaceId. */\nexport function createProjectIntakeStore(opts: CreateProjectIntakeStoreOptions): IntakeStore {\n return createScopedStore({\n db: opts.db,\n graph: opts.graph,\n table: opts.table as unknown as IntakeTableShape,\n scopeColumn: opts.table.workspaceId,\n scopeValue: opts.workspaceId,\n insertScope: { workspaceId: opts.workspaceId },\n })\n}\n\nfunction normalizePayload(raw: unknown, graphId: string): IntakePayload {\n if (raw && typeof raw === 'object' && 'answers' in (raw as Record<string, unknown>)) {\n const candidate = raw as Partial<IntakePayload>\n return {\n graphId: candidate.graphId ?? graphId,\n answers: candidate.answers ?? {},\n ...(candidate.completedAt ? { completedAt: candidate.completedAt } : {}),\n }\n }\n return { graphId, answers: {} }\n}\n\nfunction toDate(value: Date | number | null | undefined): Date | null {\n if (value == null) return null\n return value instanceof Date ? value : new Date(Number(value) * (Number(value) < 1e12 ? 1000 : 1))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateAnswer
|
|
3
|
+
} from "./chunk-ND3XEGBE.js";
|
|
4
|
+
|
|
5
|
+
// src/intakes-react/components/IntakeInterview.tsx
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function IntakeInterview({
|
|
9
|
+
view: initialView,
|
|
10
|
+
onAnswer,
|
|
11
|
+
onComplete,
|
|
12
|
+
onDone,
|
|
13
|
+
onNotice
|
|
14
|
+
}) {
|
|
15
|
+
const [view, setView] = useState(initialView);
|
|
16
|
+
const [draft, setDraft] = useState(currentAnswer(initialView));
|
|
17
|
+
const [busy, setBusy] = useState(false);
|
|
18
|
+
const [doneFired, setDoneFired] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setView(initialView);
|
|
21
|
+
setDraft(currentAnswer(initialView));
|
|
22
|
+
}, [initialView]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (view.completed && !doneFired) {
|
|
25
|
+
setDoneFired(true);
|
|
26
|
+
onDone?.();
|
|
27
|
+
}
|
|
28
|
+
}, [view.completed, doneFired, onDone]);
|
|
29
|
+
function notify(kind, message) {
|
|
30
|
+
onNotice?.({ kind, message });
|
|
31
|
+
}
|
|
32
|
+
const question = view.nextQuestion;
|
|
33
|
+
async function submit() {
|
|
34
|
+
if (!question || busy) return;
|
|
35
|
+
const validity = validateAnswer(question, draft);
|
|
36
|
+
if (!validity.ok) {
|
|
37
|
+
notify("error", `Please answer: ${validity.reason}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
setBusy(true);
|
|
41
|
+
try {
|
|
42
|
+
const next = await onAnswer({ questionId: question.id, value: normalize(question, draft) });
|
|
43
|
+
setView(next);
|
|
44
|
+
setDraft(currentAnswer(next));
|
|
45
|
+
} catch (err) {
|
|
46
|
+
notify("error", err instanceof Error ? err.message : "Failed to save answer");
|
|
47
|
+
} finally {
|
|
48
|
+
setBusy(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function finish() {
|
|
52
|
+
if (busy) return;
|
|
53
|
+
setBusy(true);
|
|
54
|
+
try {
|
|
55
|
+
const next = await onComplete();
|
|
56
|
+
setView(next);
|
|
57
|
+
notify("success", "All set.");
|
|
58
|
+
} catch (err) {
|
|
59
|
+
notify("error", err instanceof Error ? err.message : "Failed to finish");
|
|
60
|
+
} finally {
|
|
61
|
+
setBusy(false);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-5", children: [
|
|
65
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
|
|
66
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--text-primary)]", children: view.title }),
|
|
67
|
+
view.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-muted)]", children: view.description }),
|
|
68
|
+
/* @__PURE__ */ jsx(ProgressBar, { answered: view.progress.answered, total: view.progress.total })
|
|
69
|
+
] }),
|
|
70
|
+
view.completed ? /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-secondary)]", children: "Thanks \u2014 your intake is complete." }) : question ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
72
|
+
/* @__PURE__ */ jsx("p", { className: "text-base font-medium text-[var(--text-primary)]", children: question.prompt }),
|
|
73
|
+
question.help && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: question.help })
|
|
74
|
+
] }),
|
|
75
|
+
/* @__PURE__ */ jsx(AnswerField, { question, value: draft, onChange: setDraft, onSubmit: () => void submit() }),
|
|
76
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
77
|
+
"button",
|
|
78
|
+
{
|
|
79
|
+
type: "button",
|
|
80
|
+
onClick: () => void submit(),
|
|
81
|
+
disabled: busy,
|
|
82
|
+
className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50",
|
|
83
|
+
children: busy ? "Saving\u2026" : "Continue"
|
|
84
|
+
}
|
|
85
|
+
) })
|
|
86
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
87
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-secondary)]", children: "That's everything. Ready to finish?" }),
|
|
88
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
onClick: () => void finish(),
|
|
93
|
+
disabled: busy,
|
|
94
|
+
className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50",
|
|
95
|
+
children: busy ? "Finishing\u2026" : "Finish"
|
|
96
|
+
}
|
|
97
|
+
) })
|
|
98
|
+
] })
|
|
99
|
+
] });
|
|
100
|
+
}
|
|
101
|
+
function AnswerField({ question, value, onChange, onSubmit }) {
|
|
102
|
+
const inputClass = "rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]";
|
|
103
|
+
switch (question.type) {
|
|
104
|
+
case "long-text":
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
"textarea",
|
|
107
|
+
{
|
|
108
|
+
rows: 4,
|
|
109
|
+
"aria-label": question.prompt,
|
|
110
|
+
value: typeof value === "string" ? value : "",
|
|
111
|
+
onChange: (event) => onChange(event.target.value),
|
|
112
|
+
className: inputClass
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
case "boolean":
|
|
116
|
+
return /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [{ v: true, l: "Yes" }, { v: false, l: "No" }].map((opt) => /* @__PURE__ */ jsx(
|
|
117
|
+
"button",
|
|
118
|
+
{
|
|
119
|
+
type: "button",
|
|
120
|
+
onClick: () => onChange(opt.v),
|
|
121
|
+
className: `rounded border px-4 py-1.5 text-sm ${value === opt.v ? "border-[var(--brand-primary)] bg-[var(--brand-primary)] text-white" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
|
|
122
|
+
children: opt.l
|
|
123
|
+
},
|
|
124
|
+
opt.l
|
|
125
|
+
)) });
|
|
126
|
+
case "number":
|
|
127
|
+
return /* @__PURE__ */ jsx(
|
|
128
|
+
"input",
|
|
129
|
+
{
|
|
130
|
+
type: "number",
|
|
131
|
+
"aria-label": question.prompt,
|
|
132
|
+
value: typeof value === "number" ? value : "",
|
|
133
|
+
onChange: (event) => onChange(event.target.value === "" ? null : Number(event.target.value)),
|
|
134
|
+
onKeyDown: (event) => {
|
|
135
|
+
if (event.key === "Enter") onSubmit();
|
|
136
|
+
},
|
|
137
|
+
className: inputClass
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
case "single-select":
|
|
141
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => /* @__PURE__ */ jsx(
|
|
142
|
+
"button",
|
|
143
|
+
{
|
|
144
|
+
type: "button",
|
|
145
|
+
onClick: () => onChange(option.value),
|
|
146
|
+
className: `rounded border px-3 py-2 text-left text-sm ${value === option.value ? "border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
|
|
147
|
+
children: option.label
|
|
148
|
+
},
|
|
149
|
+
option.value
|
|
150
|
+
)) });
|
|
151
|
+
case "multi-select": {
|
|
152
|
+
const selected = Array.isArray(value) ? value : [];
|
|
153
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => {
|
|
154
|
+
const on = selected.includes(option.value);
|
|
155
|
+
return /* @__PURE__ */ jsx(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
type: "button",
|
|
159
|
+
onClick: () => onChange(on ? selected.filter((v) => v !== option.value) : [...selected, option.value]),
|
|
160
|
+
className: `rounded border px-3 py-2 text-left text-sm ${on ? "border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
|
|
161
|
+
children: option.label
|
|
162
|
+
},
|
|
163
|
+
option.value
|
|
164
|
+
);
|
|
165
|
+
}) });
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return /* @__PURE__ */ jsx(
|
|
169
|
+
"input",
|
|
170
|
+
{
|
|
171
|
+
type: question.type === "email" ? "email" : question.type === "url" ? "url" : "text",
|
|
172
|
+
"aria-label": question.prompt,
|
|
173
|
+
value: typeof value === "string" ? value : "",
|
|
174
|
+
onChange: (event) => onChange(event.target.value),
|
|
175
|
+
onKeyDown: (event) => {
|
|
176
|
+
if (event.key === "Enter") onSubmit();
|
|
177
|
+
},
|
|
178
|
+
className: inputClass
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function ProgressBar({ answered, total }) {
|
|
184
|
+
const pct = total > 0 ? Math.round(answered / total * 100) : 0;
|
|
185
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
186
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-[var(--brand-primary)] transition-all", style: { width: `${pct}%` } }) }),
|
|
187
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--text-muted)]", children: [
|
|
188
|
+
answered,
|
|
189
|
+
"/",
|
|
190
|
+
total
|
|
191
|
+
] })
|
|
192
|
+
] });
|
|
193
|
+
}
|
|
194
|
+
function currentAnswer(view) {
|
|
195
|
+
const id = view.nextQuestion?.id;
|
|
196
|
+
if (!id) return null;
|
|
197
|
+
return view.answers[id] ?? defaultDraft(view.nextQuestion);
|
|
198
|
+
}
|
|
199
|
+
function defaultDraft(question) {
|
|
200
|
+
return question.type === "multi-select" ? [] : null;
|
|
201
|
+
}
|
|
202
|
+
function normalize(question, value) {
|
|
203
|
+
if (typeof value === "string" && (question.type === "text" || question.type === "long-text" || question.type === "url" || question.type === "email")) {
|
|
204
|
+
return value.trim();
|
|
205
|
+
}
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export {
|
|
210
|
+
IntakeInterview
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=chunk-5I72FRZY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/intakes-react/components/IntakeInterview.tsx"],"sourcesContent":["/**\n * Agent-led intake interview: asks one question at a time, advancing as each\n * answer is saved. Fully callback-driven — the host supplies the loaded `view`\n * and the async `onAnswer` / `onComplete` callbacks (backed by `./intakes/api`),\n * so this imports no app router, fetch client, or toast. Styled with the\n * shipped Tangle Quiet tokens (`var(--*)`).\n *\n * The server owns traversal: each `onAnswer` resolves with the next view (next\n * question + progress), so the component never re-derives the graph — it renders\n * `view.nextQuestion`, and when that is null and the view is completable it\n * shows the finish action. Local validation only gates the submit button; the\n * authoritative validation runs in the store.\n */\n\nimport { useEffect, useState } from 'react'\nimport type { IntakeAnswerValue, IntakeQuestion } from '../../intakes/model'\nimport { validateAnswer } from '../../intakes/model'\nimport type { IntakeInterviewProps, IntakeView } from '../contracts'\n\nexport function IntakeInterview({\n view: initialView,\n onAnswer,\n onComplete,\n onDone,\n onNotice,\n}: IntakeInterviewProps) {\n const [view, setView] = useState<IntakeView>(initialView)\n const [draft, setDraft] = useState<IntakeAnswerValue>(currentAnswer(initialView))\n const [busy, setBusy] = useState(false)\n const [doneFired, setDoneFired] = useState(false)\n\n useEffect(() => {\n setView(initialView)\n setDraft(currentAnswer(initialView))\n }, [initialView])\n\n useEffect(() => {\n if (view.completed && !doneFired) {\n setDoneFired(true)\n onDone?.()\n }\n }, [view.completed, doneFired, onDone])\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n const question = view.nextQuestion\n\n async function submit() {\n if (!question || busy) return\n const validity = validateAnswer(question, draft)\n if (!validity.ok) {\n notify('error', `Please answer: ${validity.reason}`)\n return\n }\n setBusy(true)\n try {\n const next = await onAnswer({ questionId: question.id, value: normalize(question, draft) })\n setView(next)\n setDraft(currentAnswer(next))\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to save answer')\n } finally {\n setBusy(false)\n }\n }\n\n async function finish() {\n if (busy) return\n setBusy(true)\n try {\n const next = await onComplete()\n setView(next)\n notify('success', 'All set.')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to finish')\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <section className=\"flex flex-col gap-5\">\n <header className=\"flex flex-col gap-1\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">{view.title}</h2>\n {view.description && <p className=\"text-sm text-[var(--text-muted)]\">{view.description}</p>}\n <ProgressBar answered={view.progress.answered} total={view.progress.total} />\n </header>\n\n {view.completed ? (\n <p className=\"text-sm text-[var(--text-secondary)]\">Thanks — your intake is complete.</p>\n ) : question ? (\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-base font-medium text-[var(--text-primary)]\">{question.prompt}</p>\n {question.help && <p className=\"text-xs text-[var(--text-muted)]\">{question.help}</p>}\n </div>\n\n <AnswerField question={question} value={draft} onChange={setDraft} onSubmit={() => void submit()} />\n\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void submit()}\n disabled={busy}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50\"\n >\n {busy ? 'Saving…' : 'Continue'}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n <p className=\"text-sm text-[var(--text-secondary)]\">That's everything. Ready to finish?</p>\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void finish()}\n disabled={busy}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50\"\n >\n {busy ? 'Finishing…' : 'Finish'}\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n\ninterface AnswerFieldProps {\n question: IntakeQuestion\n value: IntakeAnswerValue\n onChange(value: IntakeAnswerValue): void\n onSubmit(): void\n}\n\nfunction AnswerField({ question, value, onChange, onSubmit }: AnswerFieldProps) {\n const inputClass =\n 'rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]'\n\n switch (question.type) {\n case 'long-text':\n return (\n <textarea\n rows={4}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n className={inputClass}\n />\n )\n case 'boolean':\n return (\n <div className=\"flex gap-2\">\n {[{ v: true, l: 'Yes' }, { v: false, l: 'No' }].map((opt) => (\n <button\n key={opt.l}\n type=\"button\"\n onClick={() => onChange(opt.v)}\n className={`rounded border px-4 py-1.5 text-sm ${\n value === opt.v\n ? 'border-[var(--brand-primary)] bg-[var(--brand-primary)] text-white'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {opt.l}\n </button>\n ))}\n </div>\n )\n case 'number':\n return (\n <input\n type=\"number\"\n aria-label={question.prompt}\n value={typeof value === 'number' ? value : ''}\n onChange={(event) => onChange(event.target.value === '' ? null : Number(event.target.value))}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n case 'single-select':\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => onChange(option.value)}\n className={`rounded border px-3 py-2 text-left text-sm ${\n value === option.value\n ? 'border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {option.label}\n </button>\n ))}\n </div>\n )\n case 'multi-select': {\n const selected = Array.isArray(value) ? value : []\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => {\n const on = selected.includes(option.value)\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() =>\n onChange(on ? selected.filter((v) => v !== option.value) : [...selected, option.value])\n }\n className={`rounded border px-3 py-2 text-left text-sm ${\n on\n ? 'border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {option.label}\n </button>\n )\n })}\n </div>\n )\n }\n default:\n return (\n <input\n type={question.type === 'email' ? 'email' : question.type === 'url' ? 'url' : 'text'}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n }\n}\n\nfunction ProgressBar({ answered, total }: { answered: number; total: number }) {\n const pct = total > 0 ? Math.round((answered / total) * 100) : 0\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]\">\n <div className=\"h-full rounded-full bg-[var(--brand-primary)] transition-all\" style={{ width: `${pct}%` }} />\n </div>\n <span className=\"text-xs text-[var(--text-muted)]\">{answered}/{total}</span>\n </div>\n )\n}\n\nfunction currentAnswer(view: IntakeView): IntakeAnswerValue {\n const id = view.nextQuestion?.id\n if (!id) return null\n return view.answers[id] ?? defaultDraft(view.nextQuestion!)\n}\n\nfunction defaultDraft(question: IntakeQuestion): IntakeAnswerValue {\n return question.type === 'multi-select' ? [] : null\n}\n\n/** Trim text answers before submit; pass others through unchanged. */\nfunction normalize(question: IntakeQuestion, value: IntakeAnswerValue): IntakeAnswerValue {\n if (typeof value === 'string' && (question.type === 'text' || question.type === 'long-text' || question.type === 'url' || question.type === 'email')) {\n return value.trim()\n }\n return value\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/design-canvas-react/lazy.tsx
|
|
2
2
|
import { lazy } from "react";
|
|
3
3
|
var DesignCanvasLazy = lazy(
|
|
4
|
-
() => import("./DesignCanvasEditor-
|
|
4
|
+
() => import("./DesignCanvasEditor-QQNAZRJQ.js").then((m) => ({ default: m.DesignCanvasEditor }))
|
|
5
5
|
);
|
|
6
6
|
var DesignCanvasChromeLazy = lazy(
|
|
7
7
|
() => import("./DesignCanvas-AOIVN3SJ.js").then((m) => ({ default: m.DesignCanvas }))
|
|
@@ -11,4 +11,4 @@ export {
|
|
|
11
11
|
DesignCanvasLazy,
|
|
12
12
|
DesignCanvasChromeLazy
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=chunk-
|
|
14
|
+
//# sourceMappingURL=chunk-FJUOBNIY.js.map
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
// src/theme/theme.ts
|
|
2
2
|
var lightTheme = {
|
|
3
3
|
background: "0 0% 100%",
|
|
4
|
-
foreground: "
|
|
5
|
-
card: "
|
|
6
|
-
cardForeground: "
|
|
4
|
+
foreground: "0 0% 5%",
|
|
5
|
+
card: "240 7% 97%",
|
|
6
|
+
cardForeground: "0 0% 5%",
|
|
7
7
|
popover: "0 0% 100%",
|
|
8
|
-
popoverForeground: "
|
|
9
|
-
primary: "
|
|
8
|
+
popoverForeground: "0 0% 5%",
|
|
9
|
+
primary: "245 62% 57%",
|
|
10
10
|
primaryForeground: "0 0% 100%",
|
|
11
|
-
secondary: "
|
|
12
|
-
secondaryForeground: "
|
|
13
|
-
muted: "
|
|
14
|
-
mutedForeground: "
|
|
15
|
-
accent: "
|
|
16
|
-
accentForeground: "
|
|
17
|
-
destructive: "0 72%
|
|
11
|
+
secondary: "240 6% 93%",
|
|
12
|
+
secondaryForeground: "0 0% 10%",
|
|
13
|
+
muted: "240 6% 93%",
|
|
14
|
+
mutedForeground: "240 5% 38%",
|
|
15
|
+
accent: "240 6% 93%",
|
|
16
|
+
accentForeground: "0 0% 10%",
|
|
17
|
+
destructive: "0 72% 41%",
|
|
18
18
|
destructiveForeground: "0 0% 100%",
|
|
19
|
-
border: "
|
|
20
|
-
input: "
|
|
21
|
-
ring: "
|
|
22
|
-
success: "
|
|
19
|
+
border: "240 6% 89%",
|
|
20
|
+
input: "240 6% 89%",
|
|
21
|
+
ring: "245 62% 57%",
|
|
22
|
+
success: "160 84% 26%",
|
|
23
23
|
successForeground: "0 0% 100%",
|
|
24
|
-
warning: "
|
|
24
|
+
warning: "41 96% 38%",
|
|
25
25
|
warningForeground: "38 92% 12%",
|
|
26
|
-
canvasBackdrop: "hsl(
|
|
26
|
+
canvasBackdrop: "hsl(240 7% 90%)",
|
|
27
27
|
canvasRender: {
|
|
28
28
|
grid: "#c0c0c0",
|
|
29
29
|
snapGrid: "#a0a0a0",
|
|
@@ -39,30 +39,30 @@ var lightTheme = {
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
var darkTheme = {
|
|
42
|
-
background: "
|
|
43
|
-
foreground: "
|
|
44
|
-
card: "
|
|
45
|
-
cardForeground: "
|
|
46
|
-
popover: "
|
|
47
|
-
popoverForeground: "
|
|
48
|
-
primary: "
|
|
49
|
-
primaryForeground: "
|
|
50
|
-
secondary: "
|
|
51
|
-
secondaryForeground: "
|
|
52
|
-
muted: "
|
|
53
|
-
mutedForeground: "
|
|
54
|
-
accent: "
|
|
55
|
-
accentForeground: "
|
|
56
|
-
destructive: "
|
|
42
|
+
background: "240 8% 5%",
|
|
43
|
+
foreground: "240 6% 93%",
|
|
44
|
+
card: "240 5% 8%",
|
|
45
|
+
cardForeground: "240 6% 93%",
|
|
46
|
+
popover: "240 4% 13%",
|
|
47
|
+
popoverForeground: "240 6% 93%",
|
|
48
|
+
primary: "239 84% 74%",
|
|
49
|
+
primaryForeground: "0 0% 100%",
|
|
50
|
+
secondary: "240 5% 11%",
|
|
51
|
+
secondaryForeground: "240 6% 93%",
|
|
52
|
+
muted: "240 5% 11%",
|
|
53
|
+
mutedForeground: "240 4% 62%",
|
|
54
|
+
accent: "240 5% 11%",
|
|
55
|
+
accentForeground: "240 6% 93%",
|
|
56
|
+
destructive: "348 90% 68%",
|
|
57
57
|
destructiveForeground: "0 0% 12%",
|
|
58
|
-
border: "
|
|
59
|
-
input: "
|
|
60
|
-
ring: "
|
|
61
|
-
success: "
|
|
62
|
-
successForeground: "
|
|
63
|
-
warning: "
|
|
58
|
+
border: "240 3% 13%",
|
|
59
|
+
input: "240 5% 11%",
|
|
60
|
+
ring: "239 84% 74%",
|
|
61
|
+
success: "160 70% 52%",
|
|
62
|
+
successForeground: "160 84% 10%",
|
|
63
|
+
warning: "40 94% 56%",
|
|
64
64
|
warningForeground: "38 92% 12%",
|
|
65
|
-
canvasBackdrop: "hsl(
|
|
65
|
+
canvasBackdrop: "hsl(240 8% 5%)",
|
|
66
66
|
canvasRender: {
|
|
67
67
|
grid: "#3a3a3a",
|
|
68
68
|
snapGrid: "#5a5a5a",
|
|
@@ -122,4 +122,4 @@ export {
|
|
|
122
122
|
themeColor,
|
|
123
123
|
themeToCssVars
|
|
124
124
|
};
|
|
125
|
-
//# sourceMappingURL=chunk-
|
|
125
|
+
//# sourceMappingURL=chunk-LVD37K5V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/theme/theme.ts"],"sourcesContent":["/**\n * Typed mirror of tokens.css for runtime/JS theming. The canonical source is\n * tokens.css (`import '@tangle-network/agent-app/styles'`); this module is for\n * apps that compute theme variables in JS, or read color values where CSS\n * custom properties cannot reach — notably Konva canvas render code, which\n * paints to a bitmap and cannot resolve `var(--…)`.\n *\n * Values are shadcn-style HSL channel triples (\"H S% L%\"); wrap with `color()`.\n */\n\nexport interface AgentAppTheme {\n background: string\n foreground: string\n card: string\n cardForeground: string\n popover: string\n popoverForeground: string\n primary: string\n primaryForeground: string\n secondary: string\n secondaryForeground: string\n muted: string\n mutedForeground: string\n accent: string\n accentForeground: string\n destructive: string\n destructiveForeground: string\n border: string\n input: string\n ring: string\n success: string\n successForeground: string\n warning: string\n warningForeground: string\n /** Full CSS color (not a triple) — the canvas/scene backdrop. */\n canvasBackdrop: string\n /** Konva render palette — full hex colors the bitmap canvas paints with\n * (it cannot resolve `var(--…)`). NOT emitted by themeToCssVars. */\n canvasRender: CanvasRenderPalette\n}\n\n/**\n * Colors the Konva design-canvas paints directly. Konva renders to a bitmap\n * and cannot read CSS custom properties, so these are full hex strings sourced\n * from the active theme and threaded through the canvas components.\n */\nexport interface CanvasRenderPalette {\n /** Grid line color (GridLayer). */\n grid: string\n /** Grid-snap guide line (SnapGuidesOverlay, kind 'grid'). */\n snapGrid: string\n /** Saved ruler-guide snap line (kind 'guide'). */\n snapGuide: string\n /** Page edge/center snap line (kinds 'page-edge'/'page-center'). */\n snapPage: string\n /** Element edge/center snap line (kinds 'element-edge'/'element-center'). */\n snapElement: string\n /** Transformer border + anchor stroke (SelectionLayer). */\n selectionStroke: string\n /** Transformer anchor fill (SelectionLayer). */\n selectionAnchorFill: string\n /** Video placeholder fill (ElementNode VideoNode). */\n placeholderFill: string\n /** Video placeholder stroke (ElementNode VideoNode). */\n placeholderStroke: string\n /** Broken/loading image placeholder fill (ElementNode ImageNode). */\n brokenFill: string\n /** Broken/loading image placeholder stroke (ElementNode ImageNode). */\n brokenStroke: string\n}\n\nexport const lightTheme: AgentAppTheme = {\n background: '0 0% 100%',\n foreground: '0 0% 5%',\n card: '240 7% 97%',\n cardForeground: '0 0% 5%',\n popover: '0 0% 100%',\n popoverForeground: '0 0% 5%',\n primary: '245 62% 57%',\n primaryForeground: '0 0% 100%',\n secondary: '240 6% 93%',\n secondaryForeground: '0 0% 10%',\n muted: '240 6% 93%',\n mutedForeground: '240 5% 38%',\n accent: '240 6% 93%',\n accentForeground: '0 0% 10%',\n destructive: '0 72% 41%',\n destructiveForeground: '0 0% 100%',\n border: '240 6% 89%',\n input: '240 6% 89%',\n ring: '245 62% 57%',\n success: '160 84% 26%',\n successForeground: '0 0% 100%',\n warning: '41 96% 38%',\n warningForeground: '38 92% 12%',\n canvasBackdrop: 'hsl(240 7% 90%)',\n canvasRender: {\n grid: '#c0c0c0',\n snapGrid: '#a0a0a0',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#ffffff',\n placeholderFill: '#1f2937',\n placeholderStroke: '#374151',\n brokenFill: '#e5e7eb',\n brokenStroke: '#9ca3af',\n },\n}\n\nexport const darkTheme: AgentAppTheme = {\n background: '240 8% 5%',\n foreground: '240 6% 93%',\n card: '240 5% 8%',\n cardForeground: '240 6% 93%',\n popover: '240 4% 13%',\n popoverForeground: '240 6% 93%',\n primary: '239 84% 74%',\n primaryForeground: '0 0% 100%',\n secondary: '240 5% 11%',\n secondaryForeground: '240 6% 93%',\n muted: '240 5% 11%',\n mutedForeground: '240 4% 62%',\n accent: '240 5% 11%',\n accentForeground: '240 6% 93%',\n destructive: '348 90% 68%',\n destructiveForeground: '0 0% 12%',\n border: '240 3% 13%',\n input: '240 5% 11%',\n ring: '239 84% 74%',\n success: '160 70% 52%',\n successForeground: '160 84% 10%',\n warning: '40 94% 56%',\n warningForeground: '38 92% 12%',\n canvasBackdrop: 'hsl(240 8% 5%)',\n canvasRender: {\n grid: '#3a3a3a',\n snapGrid: '#5a5a5a',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#e5e7eb',\n placeholderFill: '#2a2f3a',\n placeholderStroke: '#3f4654',\n brokenFill: '#262b33',\n brokenStroke: '#4b5563',\n },\n}\n\n/** Wrap a channel triple in `hsl()`; pass through values already in a color form. */\nexport function themeColor(value: string): string {\n return value.startsWith('hsl') || value.startsWith('#') || value.startsWith('rgb')\n ? value\n : `hsl(${value})`\n}\n\n/**\n * Map a theme to the full CSS-variable set (shadcn triples + canvas/sequences\n * aliases + canvas surface). Apply at runtime to scope a theme without loading\n * tokens.css: `Object.assign(el.style, themeToCssVars(darkTheme))`.\n */\nexport function themeToCssVars(theme: AgentAppTheme): Record<string, string> {\n return {\n '--background': theme.background,\n '--foreground': theme.foreground,\n '--card': theme.card,\n '--card-foreground': theme.cardForeground,\n '--popover': theme.popover,\n '--popover-foreground': theme.popoverForeground,\n '--primary': theme.primary,\n '--primary-foreground': theme.primaryForeground,\n '--secondary': theme.secondary,\n '--secondary-foreground': theme.secondaryForeground,\n '--muted': theme.muted,\n '--muted-foreground': theme.mutedForeground,\n '--accent': theme.accent,\n '--accent-foreground': theme.accentForeground,\n '--destructive': theme.destructive,\n '--destructive-foreground': theme.destructiveForeground,\n '--border': theme.border,\n '--input': theme.input,\n '--ring': theme.ring,\n '--success': theme.success,\n '--success-foreground': theme.successForeground,\n '--warning': theme.warning,\n '--warning-foreground': theme.warningForeground,\n '--bg-input': `hsl(${theme.card})`,\n '--text-primary': `hsl(${theme.foreground})`,\n '--text-secondary': `hsl(${theme.secondaryForeground})`,\n '--text-muted': `hsl(${theme.mutedForeground})`,\n '--text-danger': `hsl(${theme.destructive})`,\n '--border-default': `hsl(${theme.border})`,\n '--brand-primary': `hsl(${theme.primary})`,\n '--canvas-backdrop': theme.canvasBackdrop,\n }\n}\n"],"mappings":"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","names":[]}
|