@contractspec/lib.example-shared-ui 6.0.17 → 6.0.18

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.
Files changed (84) hide show
  1. package/.turbo/turbo-build.log +81 -81
  2. package/CHANGELOG.md +17 -0
  3. package/dist/EvolutionDashboard.js +1 -803
  4. package/dist/EvolutionSidebar.js +1 -531
  5. package/dist/LocalDataIndicator.js +1 -68
  6. package/dist/MarkdownView.js +1 -213
  7. package/dist/OverlayContextProvider.js +1 -202
  8. package/dist/PersonalizationInsights.js +1 -455
  9. package/dist/SaveToStudioButton.js +1 -79
  10. package/dist/SpecDrivenTemplateShell.js +1 -203
  11. package/dist/SpecEditorPanel.js +17 -364
  12. package/dist/TemplateShell.js +1 -195
  13. package/dist/browser/EvolutionDashboard.js +1 -803
  14. package/dist/browser/EvolutionSidebar.js +1 -531
  15. package/dist/browser/LocalDataIndicator.js +1 -68
  16. package/dist/browser/MarkdownView.js +1 -213
  17. package/dist/browser/OverlayContextProvider.js +1 -202
  18. package/dist/browser/PersonalizationInsights.js +1 -455
  19. package/dist/browser/SaveToStudioButton.js +1 -79
  20. package/dist/browser/SpecDrivenTemplateShell.js +1 -203
  21. package/dist/browser/SpecEditorPanel.js +17 -364
  22. package/dist/browser/TemplateShell.js +1 -195
  23. package/dist/browser/bundles/ExampleTemplateBundle.js +1 -85
  24. package/dist/browser/bundles/index.js +1 -85
  25. package/dist/browser/hooks/index.js +40 -1151
  26. package/dist/browser/hooks/useBehaviorTracking.js +1 -157
  27. package/dist/browser/hooks/useEvolution.js +1 -260
  28. package/dist/browser/hooks/useRegistryTemplates.js +1 -31
  29. package/dist/browser/hooks/useSpecContent.js +17 -224
  30. package/dist/browser/hooks/useWorkflowComposer.js +24 -483
  31. package/dist/browser/index.js +40 -3122
  32. package/dist/browser/lib/component-registry.js +1 -48
  33. package/dist/browser/lib/runtime-context.js +1 -21
  34. package/dist/browser/markdown/formatPresentationName.js +1 -9
  35. package/dist/browser/markdown/useMarkdownPresentation.js +1 -65
  36. package/dist/browser/utils/fetchPresentationData.js +1 -15
  37. package/dist/browser/utils/generateSpecFromTemplate.js +16 -62
  38. package/dist/browser/utils/index.js +16 -76
  39. package/dist/bundles/ExampleTemplateBundle.js +1 -85
  40. package/dist/bundles/index.js +1 -85
  41. package/dist/hooks/index.js +40 -1151
  42. package/dist/hooks/useBehaviorTracking.js +1 -157
  43. package/dist/hooks/useEvolution.js +1 -260
  44. package/dist/hooks/useRegistryTemplates.js +1 -31
  45. package/dist/hooks/useSpecContent.js +17 -224
  46. package/dist/hooks/useWorkflowComposer.js +24 -483
  47. package/dist/index.js +40 -3122
  48. package/dist/lib/component-registry.js +1 -48
  49. package/dist/lib/runtime-context.js +1 -21
  50. package/dist/markdown/formatPresentationName.js +1 -9
  51. package/dist/markdown/useMarkdownPresentation.js +1 -65
  52. package/dist/node/EvolutionDashboard.js +1 -803
  53. package/dist/node/EvolutionSidebar.js +1 -531
  54. package/dist/node/LocalDataIndicator.js +1 -68
  55. package/dist/node/MarkdownView.js +1 -213
  56. package/dist/node/OverlayContextProvider.js +1 -202
  57. package/dist/node/PersonalizationInsights.js +1 -455
  58. package/dist/node/SaveToStudioButton.js +1 -79
  59. package/dist/node/SpecDrivenTemplateShell.js +1 -203
  60. package/dist/node/SpecEditorPanel.js +17 -364
  61. package/dist/node/TemplateShell.js +1 -195
  62. package/dist/node/bundles/ExampleTemplateBundle.js +1 -85
  63. package/dist/node/bundles/index.js +1 -85
  64. package/dist/node/hooks/index.js +40 -1151
  65. package/dist/node/hooks/useBehaviorTracking.js +1 -157
  66. package/dist/node/hooks/useEvolution.js +1 -260
  67. package/dist/node/hooks/useRegistryTemplates.js +1 -31
  68. package/dist/node/hooks/useSpecContent.js +17 -224
  69. package/dist/node/hooks/useWorkflowComposer.js +24 -483
  70. package/dist/node/index.js +40 -3122
  71. package/dist/node/lib/component-registry.js +1 -48
  72. package/dist/node/lib/runtime-context.js +1 -21
  73. package/dist/node/markdown/formatPresentationName.js +1 -9
  74. package/dist/node/markdown/useMarkdownPresentation.js +1 -65
  75. package/dist/node/utils/fetchPresentationData.js +1 -15
  76. package/dist/node/utils/generateSpecFromTemplate.js +16 -62
  77. package/dist/node/utils/index.js +16 -76
  78. package/dist/utils/fetchPresentationData.js +1 -15
  79. package/dist/utils/generateSpecFromTemplate.js +16 -62
  80. package/dist/utils/index.js +16 -76
  81. package/package.json +13 -13
  82. package/src/lib/component-registry.tsx +2 -1
  83. package/src/lib/runtime-context.tsx +1 -1
  84. package/src/lib/singletons.test.ts +19 -22
@@ -1,203 +1 @@
1
- // src/lib/runtime-context.tsx
2
- import { createContext, useContext } from "react";
3
- "use client";
4
- var TEMPLATE_RUNTIME_CONTEXT_KEY = Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");
5
- function getTemplateRuntimeContextSingleton() {
6
- const store = globalThis;
7
- store[TEMPLATE_RUNTIME_CONTEXT_KEY] ??= createContext(null);
8
- return store[TEMPLATE_RUNTIME_CONTEXT_KEY];
9
- }
10
- var TemplateRuntimeContext = getTemplateRuntimeContextSingleton();
11
- function useTemplateRuntime() {
12
- const context = useContext(TemplateRuntimeContext);
13
- if (!context) {
14
- throw new Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");
15
- }
16
- return context;
17
- }
18
-
19
- // src/LocalDataIndicator.tsx
20
- import { RefreshCw, Shield } from "lucide-react";
21
- import { useState } from "react";
22
- import { jsxDEV } from "react/jsx-dev-runtime";
23
- "use client";
24
- function LocalDataIndicator() {
25
- const { projectId, templateId, template, installer } = useTemplateRuntime();
26
- const [isResetting, setIsResetting] = useState(false);
27
- const handleReset = async () => {
28
- setIsResetting(true);
29
- try {
30
- await installer.install(templateId, { projectId });
31
- } finally {
32
- setIsResetting(false);
33
- }
34
- };
35
- return /* @__PURE__ */ jsxDEV("div", {
36
- className: "inline-flex items-center gap-2 rounded-full border border-border bg-muted/40 px-3 py-1 text-muted-foreground text-xs",
37
- children: [
38
- /* @__PURE__ */ jsxDEV(Shield, {
39
- className: "h-3.5 w-3.5 text-violet-400"
40
- }, undefined, false, undefined, this),
41
- /* @__PURE__ */ jsxDEV("span", {
42
- children: [
43
- "Local runtime ·",
44
- " ",
45
- /* @__PURE__ */ jsxDEV("span", {
46
- className: "font-semibold text-foreground",
47
- children: template.name
48
- }, undefined, false, undefined, this)
49
- ]
50
- }, undefined, true, undefined, this),
51
- /* @__PURE__ */ jsxDEV("button", {
52
- type: "button",
53
- className: "inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 font-semibold text-[11px] text-muted-foreground hover:text-foreground",
54
- onClick: handleReset,
55
- disabled: isResetting,
56
- children: [
57
- /* @__PURE__ */ jsxDEV(RefreshCw, {
58
- className: "h-3 w-3"
59
- }, undefined, false, undefined, this),
60
- isResetting ? "Resetting…" : "Reset data"
61
- ]
62
- }, undefined, true, undefined, this)
63
- ]
64
- }, undefined, true, undefined, this);
65
- }
66
-
67
- // src/SaveToStudioButton.tsx
68
- import { Sparkles } from "lucide-react";
69
- import { useState as useState2 } from "react";
70
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
71
- "use client";
72
- function SaveToStudioButton({
73
- organizationId = "demo-org",
74
- projectName,
75
- endpoint,
76
- token
77
- }) {
78
- const { installer, templateId, template } = useTemplateRuntime();
79
- const [status, setStatus] = useState2("idle");
80
- const [error, setError] = useState2(null);
81
- const handleSave = async () => {
82
- setStatus("saving");
83
- setError(null);
84
- try {
85
- await installer.saveToStudio({
86
- templateId,
87
- projectName: projectName ?? `${template.name} demo`,
88
- organizationId,
89
- endpoint,
90
- token
91
- });
92
- setStatus("saved");
93
- setTimeout(() => setStatus("idle"), 3000);
94
- } catch (err) {
95
- setStatus("error");
96
- setError(err instanceof Error ? err.message : "Unknown error");
97
- }
98
- };
99
- return /* @__PURE__ */ jsxDEV2("div", {
100
- className: "flex flex-col items-end gap-1",
101
- children: [
102
- /* @__PURE__ */ jsxDEV2("button", {
103
- type: "button",
104
- className: "btn-primary inline-flex items-center gap-2 text-sm",
105
- onClick: handleSave,
106
- disabled: status === "saving",
107
- children: [
108
- /* @__PURE__ */ jsxDEV2(Sparkles, {
109
- className: "h-4 w-4"
110
- }, undefined, false, undefined, this),
111
- status === "saving" ? "Publishing…" : "Save to Studio"
112
- ]
113
- }, undefined, true, undefined, this),
114
- status === "error" && error ? /* @__PURE__ */ jsxDEV2("p", {
115
- className: "text-destructive text-xs",
116
- children: error
117
- }, undefined, false, undefined, this) : null,
118
- status === "saved" ? /* @__PURE__ */ jsxDEV2("p", {
119
- className: "text-emerald-400 text-xs",
120
- children: "Template sent to Studio."
121
- }, undefined, false, undefined, this) : null
122
- ]
123
- }, undefined, true, undefined, this);
124
- }
125
-
126
- // src/SpecDrivenTemplateShell.tsx
127
- import {
128
- BundleProvider,
129
- BundleRenderer
130
- } from "@contractspec/lib.surface-runtime/react";
131
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
132
- function SpecDrivenTemplateShell({
133
- plan,
134
- title,
135
- description,
136
- sidebar,
137
- actions,
138
- showSaveAction = true,
139
- saveProps,
140
- children
141
- }) {
142
- const headerContent = /* @__PURE__ */ jsxDEV3("header", {
143
- className: "rounded-2xl border border-border bg-card p-6 shadow-sm",
144
- children: [
145
- /* @__PURE__ */ jsxDEV3("div", {
146
- className: "flex flex-wrap items-center justify-between gap-4",
147
- children: [
148
- /* @__PURE__ */ jsxDEV3("div", {
149
- children: [
150
- /* @__PURE__ */ jsxDEV3("p", {
151
- className: "font-semibold text-muted-foreground text-sm uppercase tracking-wide",
152
- children: "ContractSpec Templates"
153
- }, undefined, false, undefined, this),
154
- /* @__PURE__ */ jsxDEV3("h1", {
155
- className: "font-bold text-3xl",
156
- children: title
157
- }, undefined, false, undefined, this),
158
- description ? /* @__PURE__ */ jsxDEV3("p", {
159
- className: "mt-2 max-w-2xl text-muted-foreground text-sm",
160
- children: description
161
- }, undefined, false, undefined, this) : null
162
- ]
163
- }, undefined, true, undefined, this),
164
- /* @__PURE__ */ jsxDEV3("div", {
165
- className: "flex flex-col items-end gap-2",
166
- children: [
167
- /* @__PURE__ */ jsxDEV3(LocalDataIndicator, {}, undefined, false, undefined, this),
168
- showSaveAction ? /* @__PURE__ */ jsxDEV3(SaveToStudioButton, {
169
- ...saveProps
170
- }, undefined, false, undefined, this) : null
171
- ]
172
- }, undefined, true, undefined, this)
173
- ]
174
- }, undefined, true, undefined, this),
175
- actions ? /* @__PURE__ */ jsxDEV3("div", {
176
- className: "mt-4",
177
- children: actions
178
- }, undefined, false, undefined, this) : null
179
- ]
180
- }, undefined, true, undefined, this);
181
- const slotContent = {
182
- header: headerContent,
183
- primary: /* @__PURE__ */ jsxDEV3("main", {
184
- className: "space-y-4 p-2",
185
- children
186
- }, undefined, false, undefined, this)
187
- };
188
- if (sidebar != null) {
189
- slotContent.sidebar = /* @__PURE__ */ jsxDEV3("aside", {
190
- className: "rounded-2xl border border-border bg-card p-4",
191
- children: sidebar
192
- }, undefined, false, undefined, this);
193
- }
194
- return /* @__PURE__ */ jsxDEV3(BundleProvider, {
195
- plan,
196
- children: /* @__PURE__ */ jsxDEV3(BundleRenderer, {
197
- slotContent
198
- }, undefined, false, undefined, this)
199
- }, undefined, false, undefined, this);
200
- }
201
- export {
202
- SpecDrivenTemplateShell
203
- };
1
+ import{createContext as w,useContext as R}from"react";var y=Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");function I(){let e=globalThis;return e[y]??=w(null),e[y]}var z=I();function p(){let e=R(z);if(!e)throw Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");return e}import{RefreshCw as $,Shield as q}from"lucide-react";import{useState as F}from"react";import{jsx as f,jsxs as g}from"react/jsx-runtime";function S(){let{projectId:e,templateId:m,template:a,installer:o}=p(),[r,n]=F(!1),i=async()=>{n(!0);try{await o.install(m,{projectId:e})}finally{n(!1)}};return g("div",{className:"inline-flex items-center gap-2 rounded-full border border-border bg-muted/40 px-3 py-1 text-muted-foreground text-xs",children:[f(q,{className:"h-3.5 w-3.5 text-violet-400"}),g("span",{children:["Local runtime ·"," ",f("span",{className:"font-semibold text-foreground",children:a.name})]}),g("button",{type:"button",className:"inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 font-semibold text-[11px] text-muted-foreground hover:text-foreground",onClick:i,disabled:r,children:[f($,{className:"h-3 w-3"}),r?"Resetting…":"Reset data"]})]})}import{Sparkles as G}from"lucide-react";import{useState as N}from"react";import{jsx as b,jsxs as h}from"react/jsx-runtime";function k({organizationId:e="demo-org",projectName:m,endpoint:a,token:o}){let{installer:r,templateId:n,template:i}=p(),[l,d]=N("idle"),[u,c]=N(null);return h("div",{className:"flex flex-col items-end gap-1",children:[h("button",{type:"button",className:"btn-primary inline-flex items-center gap-2 text-sm",onClick:async()=>{d("saving"),c(null);try{await r.saveToStudio({templateId:n,projectName:m??`${i.name} demo`,organizationId:e,endpoint:a,token:o}),d("saved"),setTimeout(()=>d("idle"),3000)}catch(v){d("error"),c(v instanceof Error?v.message:"Unknown error")}},disabled:l==="saving",children:[b(G,{className:"h-4 w-4"}),l==="saving"?"Publishing…":"Save to Studio"]}),l==="error"&&u?b("p",{className:"text-destructive text-xs",children:u}):null,l==="saved"?b("p",{className:"text-emerald-400 text-xs",children:"Template sent to Studio."}):null]})}import{BundleProvider as H,BundleRenderer as J}from"@contractspec/lib.surface-runtime/react";import{jsx as t,jsxs as s}from"react/jsx-runtime";function x({plan:e,title:m,description:a,sidebar:o,actions:r,showSaveAction:n=!0,saveProps:i,children:l}){let u={header:s("header",{className:"rounded-2xl border border-border bg-card p-6 shadow-sm",children:[s("div",{className:"flex flex-wrap items-center justify-between gap-4",children:[s("div",{children:[t("p",{className:"font-semibold text-muted-foreground text-sm uppercase tracking-wide",children:"ContractSpec Templates"}),t("h1",{className:"font-bold text-3xl",children:m}),a?t("p",{className:"mt-2 max-w-2xl text-muted-foreground text-sm",children:a}):null]}),s("div",{className:"flex flex-col items-end gap-2",children:[t(S,{}),n?t(k,{...i}):null]})]}),r?t("div",{className:"mt-4",children:r}):null]}),primary:t("main",{className:"space-y-4 p-2",children:l})};if(o!=null)u.sidebar=t("aside",{className:"rounded-2xl border border-border bg-card p-4",children:o});return t(H,{plan:e,children:t(J,{slotContent:u})})}export{x as SpecDrivenTemplateShell};
@@ -1,47 +1,5 @@
1
- // src/lib/runtime-context.tsx
2
- import { createContext, useContext } from "react";
3
- "use client";
4
- var TEMPLATE_RUNTIME_CONTEXT_KEY = Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");
5
- function getTemplateRuntimeContextSingleton() {
6
- const store = globalThis;
7
- store[TEMPLATE_RUNTIME_CONTEXT_KEY] ??= createContext(null);
8
- return store[TEMPLATE_RUNTIME_CONTEXT_KEY];
9
- }
10
- var TemplateRuntimeContext = getTemplateRuntimeContextSingleton();
11
- function useTemplateRuntime() {
12
- const context = useContext(TemplateRuntimeContext);
13
- if (!context) {
14
- throw new Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");
15
- }
16
- return context;
17
- }
18
-
19
- // src/utils/generateSpecFromTemplate.ts
20
- function generateSpecFromTemplate(template) {
21
- const templateId = template?.id ?? "unknown";
22
- if (!template) {
23
- return generateDefaultSpec(templateId);
24
- }
25
- switch (templateId) {
26
- case "crm-pipeline":
27
- return generateCrmPipelineSpec(template.schema.contracts);
28
- case "saas-boilerplate":
29
- return generateSaasBoilerplateSpec(template.schema.contracts);
30
- case "agent-console":
31
- return generateAgentConsoleSpec(template.schema.contracts);
32
- case "todos-app":
33
- return generateTodosSpec(template.schema.contracts);
34
- case "messaging-app":
35
- return generateMessagingSpec(template.schema.contracts);
36
- case "recipe-app-i18n":
37
- return generateRecipeSpec(template.schema.contracts);
38
- default:
39
- return generateDefaultSpec(templateId);
40
- }
41
- }
42
- function generateCrmPipelineSpec(contracts) {
43
- return `// CRM Pipeline Specs
44
- // Contracts: ${contracts.join(", ")}
1
+ import{createContext as g,useContext as i}from"react";var L=Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");function m(){let q=globalThis;return q[L]??=g(null),q[L]}var c=m();function x(){let q=i(c);if(!q)throw Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");return q}function F(q){let Z=q?.id??"unknown";if(!q)return I(Z);switch(Z){case"crm-pipeline":return p(q.schema.contracts);case"saas-boilerplate":return d(q.schema.contracts);case"agent-console":return l(q.schema.contracts);case"todos-app":return r(q.schema.contracts);case"messaging-app":return s(q.schema.contracts);case"recipe-app-i18n":return o(q.schema.contracts);default:return I(Z)}}function p(q){return`// CRM Pipeline Specs
2
+ // Contracts: ${q.join(", ")}
45
3
 
46
4
  contractSpec("crm.deal.updateStage.v1", {
47
5
  goal: "Move a deal to a different pipeline stage",
@@ -110,11 +68,8 @@ contractSpec("crm.contact.list.v1", {
110
68
  hasMore: "boolean"
111
69
  }
112
70
  }
113
- });`;
114
- }
115
- function generateSaasBoilerplateSpec(contracts) {
116
- return `// SaaS Boilerplate Specs
117
- // Contracts: ${contracts.join(", ")}
71
+ });`}function d(q){return`// SaaS Boilerplate Specs
72
+ // Contracts: ${q.join(", ")}
118
73
 
119
74
  contractSpec("saas.project.create.v1", {
120
75
  goal: "Create a new project in an organization",
@@ -177,11 +132,8 @@ contractSpec("saas.settings.update.v1", {
177
132
  }
178
133
  },
179
134
  events: ["settings.updated"]
180
- });`;
181
- }
182
- function generateAgentConsoleSpec(contracts) {
183
- return `// Agent Console Specs
184
- // Contracts: ${contracts.join(", ")}
135
+ });`}function l(q){return`// Agent Console Specs
136
+ // Contracts: ${q.join(", ")}
185
137
 
186
138
  contractSpec("agent.run.execute.v1", {
187
139
  goal: "Execute an agent run with specified tools",
@@ -247,11 +199,8 @@ contractSpec("agent.agent.create.v1", {
247
199
  }
248
200
  },
249
201
  events: ["agent.created"]
250
- });`;
251
- }
252
- function generateTodosSpec(contracts) {
253
- return `// To-dos App Specs
254
- // Contracts: ${contracts.join(", ")}
202
+ });`}function r(q){return`// To-dos App Specs
203
+ // Contracts: ${q.join(", ")}
255
204
 
256
205
  contractSpec("tasks.board.v1", {
257
206
  goal: "Assign and approve craft work",
@@ -310,11 +259,8 @@ contractSpec("tasks.complete.v1", {
310
259
  }
311
260
  },
312
261
  events: ["task.completed"]
313
- });`;
314
- }
315
- function generateMessagingSpec(contracts) {
316
- return `// Messaging App Specs
317
- // Contracts: ${contracts.join(", ")}
262
+ });`}function s(q){return`// Messaging App Specs
263
+ // Contracts: ${q.join(", ")}
318
264
 
319
265
  contractSpec("messaging.send.v1", {
320
266
  goal: "Deliver intent-rich updates",
@@ -367,11 +313,8 @@ contractSpec("messaging.read.v1", {
367
313
  }
368
314
  },
369
315
  events: ["message.read"]
370
- });`;
371
- }
372
- function generateRecipeSpec(contracts) {
373
- return `// Recipe App (i18n) Specs
374
- // Contracts: ${contracts.join(", ")}
316
+ });`}function o(q){return`// Recipe App (i18n) Specs
317
+ // Contracts: ${q.join(", ")}
375
318
 
376
319
  contractSpec("recipes.lookup.v1", {
377
320
  goal: "Serve bilingual rituals",
@@ -418,13 +361,10 @@ contractSpec("recipes.favorite.toggle.v1", {
418
361
  }
419
362
  },
420
363
  events: ["recipe.favorited", "recipe.unfavorited"]
421
- });`;
422
- }
423
- function generateDefaultSpec(templateId) {
424
- return `// ${templateId} Specs
364
+ });`}function I(q){return`// ${q} Specs
425
365
 
426
- contractSpec("${templateId}.main.v1", {
427
- goal: "Main operation for ${templateId}",
366
+ contractSpec("${q}.main.v1", {
367
+ goal: "Main operation for ${q}",
428
368
  transport: { gql: { query: "main" } },
429
369
  io: {
430
370
  input: {
@@ -434,292 +374,5 @@ contractSpec("${templateId}.main.v1", {
434
374
  result: "unknown"
435
375
  }
436
376
  }
437
- });`;
438
- }
439
-
440
- // src/hooks/useSpecContent.ts
441
- import { useCallback, useEffect, useState } from "react";
442
- "use client";
443
- var SPEC_STORAGE_KEY = "contractspec-spec-content";
444
- function useSpecContent(templateId) {
445
- const { template } = useTemplateRuntime();
446
- const [content, setContentState] = useState("");
447
- const [savedContent, setSavedContent] = useState("");
448
- const [loading, setLoading] = useState(true);
449
- const [validation, setValidation] = useState(null);
450
- const [lastSaved, setLastSaved] = useState(null);
451
- useEffect(() => {
452
- setLoading(true);
453
- try {
454
- const stored = localStorage.getItem(`${SPEC_STORAGE_KEY}-${templateId}`);
455
- if (stored) {
456
- const parsed = JSON.parse(stored);
457
- if (parsed.content) {
458
- setContentState(parsed.content);
459
- setSavedContent(parsed.content);
460
- setLastSaved(parsed.savedAt);
461
- } else {
462
- const generated = generateSpecFromTemplate(template);
463
- setContentState(generated);
464
- setSavedContent(generated);
465
- }
466
- } else {
467
- const generated = generateSpecFromTemplate(template);
468
- setContentState(generated);
469
- setSavedContent(generated);
470
- }
471
- } catch {
472
- const generated = generateSpecFromTemplate(template);
473
- setContentState(generated);
474
- setSavedContent(generated);
475
- }
476
- setLoading(false);
477
- }, [templateId]);
478
- const setContent = useCallback((newContent) => {
479
- setContentState(newContent);
480
- setValidation(null);
481
- }, []);
482
- const save = useCallback(() => {
483
- try {
484
- const savedAt = new Date().toISOString();
485
- localStorage.setItem(`${SPEC_STORAGE_KEY}-${templateId}`, JSON.stringify({
486
- content,
487
- savedAt
488
- }));
489
- setSavedContent(content);
490
- setLastSaved(savedAt);
491
- } catch {}
492
- }, [content, templateId]);
493
- const validate = useCallback(() => {
494
- const errors = [];
495
- const lines = content.split(`
496
- `);
497
- if (!content.includes("contractSpec(")) {
498
- errors.push({
499
- line: 1,
500
- message: "Spec must contain a contractSpec() definition",
501
- severity: "error"
502
- });
503
- }
504
- if (!content.includes("goal:")) {
505
- errors.push({
506
- line: 1,
507
- message: "Spec should have a goal field",
508
- severity: "warning"
509
- });
510
- }
511
- if (!content.includes("io:")) {
512
- errors.push({
513
- line: 1,
514
- message: "Spec should define io (input/output)",
515
- severity: "warning"
516
- });
517
- }
518
- const openBraces = (content.match(/{/g) ?? []).length;
519
- const closeBraces = (content.match(/}/g) ?? []).length;
520
- if (openBraces !== closeBraces) {
521
- errors.push({
522
- line: lines.length,
523
- message: `Unbalanced braces: ${openBraces} opening, ${closeBraces} closing`,
524
- severity: "error"
525
- });
526
- }
527
- const openParens = (content.match(/\(/g) ?? []).length;
528
- const closeParens = (content.match(/\)/g) ?? []).length;
529
- if (openParens !== closeParens) {
530
- errors.push({
531
- line: lines.length,
532
- message: `Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`,
533
- severity: "error"
534
- });
535
- }
536
- lines.forEach((line, index) => {
537
- const singleQuotes = (line.match(/'/g) ?? []).length;
538
- const doubleQuotes = (line.match(/"/g) ?? []).length;
539
- if (singleQuotes % 2 !== 0) {
540
- errors.push({
541
- line: index + 1,
542
- message: "Unclosed single quote",
543
- severity: "error"
544
- });
545
- }
546
- if (doubleQuotes % 2 !== 0) {
547
- errors.push({
548
- line: index + 1,
549
- message: "Unclosed double quote",
550
- severity: "error"
551
- });
552
- }
553
- });
554
- const result = {
555
- valid: errors.filter((e) => e.severity === "error").length === 0,
556
- errors
557
- };
558
- setValidation(result);
559
- return result;
560
- }, [content]);
561
- const reset = useCallback(() => {
562
- const generated = generateSpecFromTemplate(template);
563
- setContentState(generated);
564
- setSavedContent(generated);
565
- setValidation(null);
566
- setLastSaved(null);
567
- try {
568
- localStorage.removeItem(`${SPEC_STORAGE_KEY}-${templateId}`);
569
- } catch {}
570
- }, [templateId]);
571
- return {
572
- content,
573
- loading,
574
- isDirty: content !== savedContent,
575
- validation,
576
- setContent,
577
- save,
578
- validate,
579
- reset,
580
- lastSaved
581
- };
582
- }
583
-
584
- // src/SpecEditorPanel.tsx
585
- import { Button, LoaderBlock } from "@contractspec/lib.design-system";
586
- import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
587
- import { useCallback as useCallback2, useEffect as useEffect2 } from "react";
588
- import { jsxDEV } from "react/jsx-dev-runtime";
589
- "use client";
590
- function SpecEditorPanel({
591
- templateId,
592
- SpecEditor,
593
- onLog
594
- }) {
595
- const {
596
- content,
597
- loading,
598
- isDirty,
599
- validation,
600
- setContent,
601
- save,
602
- validate,
603
- reset,
604
- lastSaved
605
- } = useSpecContent(templateId);
606
- useEffect2(() => {
607
- if (!loading && content) {
608
- onLog?.(`Spec loaded for ${templateId}`);
609
- }
610
- }, [loading, content, templateId, onLog]);
611
- const handleSave = useCallback2(() => {
612
- save();
613
- onLog?.("Spec saved locally");
614
- }, [save, onLog]);
615
- const handleValidate = useCallback2(() => {
616
- const result = validate();
617
- if (result.valid) {
618
- onLog?.("Spec validation passed");
619
- } else {
620
- const errorCount = result.errors.filter((e) => e.severity === "error").length;
621
- const warnCount = result.errors.filter((e) => e.severity === "warning").length;
622
- onLog?.(`Spec validation: ${errorCount} errors, ${warnCount} warnings`);
623
- }
624
- }, [validate, onLog]);
625
- const handleReset = useCallback2(() => {
626
- reset();
627
- onLog?.("Spec reset to template defaults");
628
- }, [reset, onLog]);
629
- if (loading) {
630
- return /* @__PURE__ */ jsxDEV(LoaderBlock, {
631
- label: "Loading spec..."
632
- }, undefined, false, undefined, this);
633
- }
634
- return /* @__PURE__ */ jsxDEV("div", {
635
- className: "space-y-4",
636
- children: [
637
- /* @__PURE__ */ jsxDEV("div", {
638
- className: "flex items-center justify-between",
639
- children: [
640
- /* @__PURE__ */ jsxDEV("div", {
641
- className: "flex items-center gap-2",
642
- children: [
643
- /* @__PURE__ */ jsxDEV(Button, {
644
- variant: "default",
645
- size: "sm",
646
- onClick: handleSave,
647
- children: "Save"
648
- }, undefined, false, undefined, this),
649
- /* @__PURE__ */ jsxDEV(Button, {
650
- variant: "outline",
651
- size: "sm",
652
- onClick: handleValidate,
653
- children: "Validate"
654
- }, undefined, false, undefined, this),
655
- isDirty && /* @__PURE__ */ jsxDEV(Badge, {
656
- variant: "secondary",
657
- className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
658
- children: "Unsaved changes"
659
- }, undefined, false, undefined, this),
660
- validation && /* @__PURE__ */ jsxDEV(Badge, {
661
- variant: validation.valid ? "default" : "destructive",
662
- className: validation.valid ? "border-green-500/30 bg-green-500/20 text-green-400" : "",
663
- children: validation.valid ? "Valid" : `${validation.errors.filter((e) => e.severity === "error").length} errors`
664
- }, undefined, false, undefined, this)
665
- ]
666
- }, undefined, true, undefined, this),
667
- /* @__PURE__ */ jsxDEV("div", {
668
- className: "flex items-center gap-2",
669
- children: [
670
- lastSaved && /* @__PURE__ */ jsxDEV("span", {
671
- className: "text-muted-foreground text-xs",
672
- children: [
673
- "Last saved: ",
674
- new Date(lastSaved).toLocaleTimeString()
675
- ]
676
- }, undefined, true, undefined, this),
677
- /* @__PURE__ */ jsxDEV(Button, {
678
- variant: "ghost",
679
- size: "sm",
680
- onPress: handleReset,
681
- children: "Reset"
682
- }, undefined, false, undefined, this)
683
- ]
684
- }, undefined, true, undefined, this)
685
- ]
686
- }, undefined, true, undefined, this),
687
- validation && validation.errors.length > 0 && /* @__PURE__ */ jsxDEV("div", {
688
- className: "rounded-lg border border-amber-500/50 bg-amber-500/10 p-3",
689
- children: [
690
- /* @__PURE__ */ jsxDEV("p", {
691
- className: "mb-2 font-semibold text-amber-400 text-xs uppercase",
692
- children: "Validation Issues"
693
- }, undefined, false, undefined, this),
694
- /* @__PURE__ */ jsxDEV("ul", {
695
- className: "space-y-1",
696
- children: validation.errors.map((error, index) => /* @__PURE__ */ jsxDEV("li", {
697
- className: `text-xs ${error.severity === "error" ? "text-red-400" : "text-amber-400"}`,
698
- children: [
699
- "Line ",
700
- error.line,
701
- ": ",
702
- error.message
703
- ]
704
- }, `${error.line}-${error.message}-${index}`, true, undefined, this))
705
- }, undefined, false, undefined, this)
706
- ]
707
- }, undefined, true, undefined, this),
708
- /* @__PURE__ */ jsxDEV("div", {
709
- className: "rounded-2xl border border-border bg-card p-4",
710
- children: /* @__PURE__ */ jsxDEV(SpecEditor, {
711
- projectId: "sandbox",
712
- type: "CAPABILITY",
713
- content,
714
- onChange: setContent,
715
- metadata: { template: templateId },
716
- onSave: handleSave,
717
- onValidate: handleValidate
718
- }, undefined, false, undefined, this)
719
- }, undefined, false, undefined, this)
720
- ]
721
- }, undefined, true, undefined, this);
722
- }
723
- export {
724
- SpecEditorPanel
725
- };
377
+ });`}import{useCallback as A,useEffect as n,useState as K}from"react";var R="contractspec-spec-content";function v(q){let{template:Z}=x(),[H,$]=K(""),[w,X]=K(""),[y,M]=K(!0),[O,D]=K(null),[V,N]=K(null);n(()=>{M(!0);try{let z=localStorage.getItem(`${R}-${q}`);if(z){let W=JSON.parse(z);if(W.content)$(W.content),X(W.content),N(W.savedAt);else{let U=F(Z);$(U),X(U)}}else{let W=F(Z);$(W),X(W)}}catch{let z=F(Z);$(z),X(z)}M(!1)},[q]);let Y=A((z)=>{$(z),D(null)},[]),h=A(()=>{try{let z=new Date().toISOString();localStorage.setItem(`${R}-${q}`,JSON.stringify({content:H,savedAt:z})),X(H),N(z)}catch{}},[H,q]),P=A(()=>{let z=[],W=H.split(`
378
+ `);if(!H.includes("contractSpec("))z.push({line:1,message:"Spec must contain a contractSpec() definition",severity:"error"});if(!H.includes("goal:"))z.push({line:1,message:"Spec should have a goal field",severity:"warning"});if(!H.includes("io:"))z.push({line:1,message:"Spec should define io (input/output)",severity:"warning"});let U=(H.match(/{/g)??[]).length,B=(H.match(/}/g)??[]).length;if(U!==B)z.push({line:W.length,message:`Unbalanced braces: ${U} opening, ${B} closing`,severity:"error"});let _=(H.match(/\(/g)??[]).length,j=(H.match(/\)/g)??[]).length;if(_!==j)z.push({line:W.length,message:`Unbalanced parentheses: ${_} opening, ${j} closing`,severity:"error"});W.forEach((f,E)=>{let u=(f.match(/'/g)??[]).length,S=(f.match(/"/g)??[]).length;if(u%2!==0)z.push({line:E+1,message:"Unclosed single quote",severity:"error"});if(S%2!==0)z.push({line:E+1,message:"Unclosed double quote",severity:"error"})});let T={valid:z.filter((f)=>f.severity==="error").length===0,errors:z};return D(T),T},[H]),J=A(()=>{let z=F(Z);$(z),X(z),D(null),N(null);try{localStorage.removeItem(`${R}-${q}`)}catch{}},[q]);return{content:H,loading:y,isDirty:H!==w,validation:O,setContent:Y,save:h,validate:P,reset:J,lastSaved:V}}import{Button as b,LoaderBlock as a}from"@contractspec/lib.design-system";import{Badge as C}from"@contractspec/lib.ui-kit-web/ui/badge";import{useCallback as k,useEffect as t}from"react";import{jsx as Q,jsxs as G}from"react/jsx-runtime";function Xq({templateId:q,SpecEditor:Z,onLog:H}){let{content:$,loading:w,isDirty:X,validation:y,setContent:M,save:O,validate:D,reset:V,lastSaved:N}=v(q);t(()=>{if(!w&&$)H?.(`Spec loaded for ${q}`)},[w,$,q,H]);let Y=k(()=>{O(),H?.("Spec saved locally")},[O,H]),h=k(()=>{let J=D();if(J.valid)H?.("Spec validation passed");else{let z=J.errors.filter((U)=>U.severity==="error").length,W=J.errors.filter((U)=>U.severity==="warning").length;H?.(`Spec validation: ${z} errors, ${W} warnings`)}},[D,H]),P=k(()=>{V(),H?.("Spec reset to template defaults")},[V,H]);if(w)return Q(a,{label:"Loading spec..."});return G("div",{className:"space-y-4",children:[G("div",{className:"flex items-center justify-between",children:[G("div",{className:"flex items-center gap-2",children:[Q(b,{variant:"default",size:"sm",onClick:Y,children:"Save"}),Q(b,{variant:"outline",size:"sm",onClick:h,children:"Validate"}),X&&Q(C,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:"Unsaved changes"}),y&&Q(C,{variant:y.valid?"default":"destructive",className:y.valid?"border-green-500/30 bg-green-500/20 text-green-400":"",children:y.valid?"Valid":`${y.errors.filter((J)=>J.severity==="error").length} errors`})]}),G("div",{className:"flex items-center gap-2",children:[N&&G("span",{className:"text-muted-foreground text-xs",children:["Last saved: ",new Date(N).toLocaleTimeString()]}),Q(b,{variant:"ghost",size:"sm",onPress:P,children:"Reset"})]})]}),y&&y.errors.length>0&&G("div",{className:"rounded-lg border border-amber-500/50 bg-amber-500/10 p-3",children:[Q("p",{className:"mb-2 font-semibold text-amber-400 text-xs uppercase",children:"Validation Issues"}),Q("ul",{className:"space-y-1",children:y.errors.map((J,z)=>G("li",{className:`text-xs ${J.severity==="error"?"text-red-400":"text-amber-400"}`,children:["Line ",J.line,": ",J.message]},`${J.line}-${J.message}-${z}`))})]}),Q("div",{className:"rounded-2xl border border-border bg-card p-4",children:Q(Z,{projectId:"sandbox",type:"CAPABILITY",content:$,onChange:M,metadata:{template:q},onSave:Y,onValidate:h})})]})}export{Xq as SpecEditorPanel};