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