@work-graph/cli 0.2.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.
Files changed (194) hide show
  1. package/README.md +31 -0
  2. package/bin/work-graph.mjs +238 -0
  3. package/package.json +38 -0
  4. package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
  5. package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
  6. package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
  7. package/vendor/packages/workgraph-mcp/README.md +28 -0
  8. package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
  9. package/vendor/packages/workgraph-mcp/package.json +37 -0
  10. package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
  11. package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
  12. package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
  13. package/vendor/public/assets/workgraph-logo.svg +11 -0
  14. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
  15. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
  16. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
  17. package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
  18. package/vendor/public/graph-canvas-lit-flow.css +154 -0
  19. package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
  20. package/vendor/public/graph-canvas-lit-flow.js +8530 -0
  21. package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
  22. package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
  23. package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
  24. package/vendor/src/agentRunApi.mjs +136 -0
  25. package/vendor/src/agentToolLoopGuard.mjs +88 -0
  26. package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
  27. package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
  28. package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
  29. package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
  30. package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
  31. package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
  32. package/vendor/src/analyticsPanelProjection.mjs +212 -0
  33. package/vendor/src/analyticsRecordStore.mjs +165 -0
  34. package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
  35. package/vendor/src/architectureL1Canon.mjs +419 -0
  36. package/vendor/src/architectureLayout.mjs +229 -0
  37. package/vendor/src/architectureSnapshot.mjs +490 -0
  38. package/vendor/src/architectureViewsProjection.mjs +116 -0
  39. package/vendor/src/atomInspector.mjs +253 -0
  40. package/vendor/src/atomInspectorApi.mjs +130 -0
  41. package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
  42. package/vendor/src/backlogSchemaLint.mjs +176 -0
  43. package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
  44. package/vendor/src/bracketIrTraceSignal.mjs +93 -0
  45. package/vendor/src/bvcAtomParser.mjs +210 -0
  46. package/vendor/src/bvcDialectRegistry.mjs +86 -0
  47. package/vendor/src/bvcFileFormat.mjs +218 -0
  48. package/vendor/src/bvcFormatCli.mjs +55 -0
  49. package/vendor/src/bvcLintCli.mjs +48 -0
  50. package/vendor/src/bvcNewWritePolicy.mjs +70 -0
  51. package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
  52. package/vendor/src/claimNoEligibleEval.mjs +205 -0
  53. package/vendor/src/closingAnalysisSuggest.mjs +59 -0
  54. package/vendor/src/codeGapAnalyzer.mjs +308 -0
  55. package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
  56. package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
  57. package/vendor/src/codeGapOperatorProjection.mjs +60 -0
  58. package/vendor/src/codeSyntaxHighlight.mjs +123 -0
  59. package/vendor/src/codegenEvidence.mjs +187 -0
  60. package/vendor/src/compilerRoundTripCli.mjs +164 -0
  61. package/vendor/src/dagreGraphLayout.mjs +78 -0
  62. package/vendor/src/draftIntakePromotionRules.mjs +205 -0
  63. package/vendor/src/epicWorkScope.mjs +85 -0
  64. package/vendor/src/evalLiveLlmEnv.mjs +63 -0
  65. package/vendor/src/evidenceReadModel.mjs +167 -0
  66. package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
  67. package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
  68. package/vendor/src/goldenPath.mjs +69 -0
  69. package/vendor/src/graphCanvasLayout.mjs +464 -0
  70. package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
  71. package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
  72. package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
  73. package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
  74. package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
  75. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
  76. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
  77. package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
  78. package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
  79. package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
  80. package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
  81. package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
  82. package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
  83. package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
  84. package/vendor/src/graphRagContextSlice.mjs +461 -0
  85. package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
  86. package/vendor/src/homeSnapshotApi.mjs +131 -0
  87. package/vendor/src/homeSnapshotProjection.mjs +275 -0
  88. package/vendor/src/inboxEventStream.mjs +140 -0
  89. package/vendor/src/intentComposerApi.mjs +245 -0
  90. package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
  91. package/vendor/src/intentGraphProjection.mjs +208 -0
  92. package/vendor/src/intentHierarchy.mjs +241 -0
  93. package/vendor/src/intentNodeLint.mjs +107 -0
  94. package/vendor/src/intentNodeRuntime.mjs +185 -0
  95. package/vendor/src/intentRoadmapCanvas.mjs +393 -0
  96. package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
  97. package/vendor/src/intentRoadmapMermaid.mjs +165 -0
  98. package/vendor/src/intentRoadmapProjection.mjs +85 -0
  99. package/vendor/src/intentTreeLint.mjs +114 -0
  100. package/vendor/src/intentTreeMigration.mjs +150 -0
  101. package/vendor/src/intentTreeWorkItems.mjs +227 -0
  102. package/vendor/src/kanbanBoardProjection.mjs +58 -0
  103. package/vendor/src/languageAdapterRegistry.mjs +180 -0
  104. package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
  105. package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
  106. package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
  107. package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
  108. package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
  109. package/vendor/src/languageAdapters/shared.mjs +68 -0
  110. package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
  111. package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
  112. package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
  113. package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
  114. package/vendor/src/markdownDocumentRender.mjs +208 -0
  115. package/vendor/src/memoryPanelProjection.mjs +116 -0
  116. package/vendor/src/memoryRecordWriter.mjs +243 -0
  117. package/vendor/src/memoryWorkerSlice.mjs +238 -0
  118. package/vendor/src/migrateStepToBvc.mjs +133 -0
  119. package/vendor/src/missionControlServerHandlers.mjs +195 -0
  120. package/vendor/src/missionControlUiClient.mjs +278 -0
  121. package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
  122. package/vendor/src/onebaseCliRunner.mjs +145 -0
  123. package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
  124. package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
  125. package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
  126. package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
  127. package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
  128. package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
  129. package/vendor/src/onebaseWorkerTools.mjs +586 -0
  130. package/vendor/src/operatorShellProjection.mjs +102 -0
  131. package/vendor/src/pipelineProseRender.mjs +180 -0
  132. package/vendor/src/pipelineStageLint.mjs +118 -0
  133. package/vendor/src/promptRulesEditorApi.mjs +174 -0
  134. package/vendor/src/promptRulesProjection.mjs +134 -0
  135. package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
  136. package/vendor/src/pvrgTaskScope.mjs +152 -0
  137. package/vendor/src/releaseGateMatrix.mjs +188 -0
  138. package/vendor/src/schematicView.mjs +305 -0
  139. package/vendor/src/seedAnalyticsRecord.mjs +217 -0
  140. package/vendor/src/semanticSearchBm25.mjs +103 -0
  141. package/vendor/src/semanticSearchExcerpts.mjs +68 -0
  142. package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
  143. package/vendor/src/semanticSearchWorkflow.mjs +366 -0
  144. package/vendor/src/stepAtomFormatter.mjs +413 -0
  145. package/vendor/src/stepGraphSlice.mjs +318 -0
  146. package/vendor/src/ui/atoms/badge.mjs +40 -0
  147. package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
  148. package/vendor/src/ui/atoms/button.mjs +114 -0
  149. package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
  150. package/vendor/src/ui/atoms/icon.mjs +23 -0
  151. package/vendor/src/ui/atoms/input.mjs +38 -0
  152. package/vendor/src/ui/atoms/modal.mjs +44 -0
  153. package/vendor/src/ui/atoms/select.mjs +98 -0
  154. package/vendor/src/ui/backlogShellButtons.mjs +238 -0
  155. package/vendor/src/ui/htmlEscape.mjs +11 -0
  156. package/vendor/src/ui/molecules/rating.mjs +48 -0
  157. package/vendor/src/ui/molecules/tabs.mjs +70 -0
  158. package/vendor/src/ui/organisms/modal.mjs +1 -0
  159. package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
  160. package/vendor/src/ui/workItemStatusTone.mjs +36 -0
  161. package/vendor/src/unifiedLinkageProjection.mjs +264 -0
  162. package/vendor/src/verificationLoop.mjs +206 -0
  163. package/vendor/src/workGraphBacklogPersist.mjs +234 -0
  164. package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
  165. package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
  166. package/vendor/src/workGraphCycleSlice.mjs +184 -0
  167. package/vendor/src/workGraphDaemonTick.mjs +307 -0
  168. package/vendor/src/workGraphDaemonWatch.mjs +157 -0
  169. package/vendor/src/workGraphEngineRoot.mjs +136 -0
  170. package/vendor/src/workGraphInstallLayout.mjs +65 -0
  171. package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
  172. package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
  173. package/vendor/src/workGraphProjectHost.mjs +149 -0
  174. package/vendor/src/workGraphProjectInit.mjs +392 -0
  175. package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
  176. package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
  177. package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
  178. package/vendor/src/workGraphRuntime.mjs +1008 -0
  179. package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
  180. package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
  181. package/vendor/src/workGraphWorkerProvider.mjs +600 -0
  182. package/vendor/src/workItemBvcQuality.mjs +262 -0
  183. package/vendor/src/workItemCreateAnalysis.mjs +157 -0
  184. package/vendor/src/workItemDecisionPipeline.mjs +278 -0
  185. package/vendor/src/workItemEpicCascade.mjs +176 -0
  186. package/vendor/src/workItemExecutionGate.mjs +78 -0
  187. package/vendor/src/workItemHierarchy.mjs +226 -0
  188. package/vendor/src/workItemProseLint.mjs +133 -0
  189. package/vendor/src/workItemTextRusify.mjs +794 -0
  190. package/vendor/src/workItemTraceEnvelope.mjs +158 -0
  191. package/vendor/src/workItemUiReferences.mjs +272 -0
  192. package/vendor/src/workflowEpicGrouping.mjs +67 -0
  193. package/vendor/src/workflowTreeProjection.mjs +53 -0
  194. package/vendor/src/workspaceRegistry.mjs +150 -0
@@ -0,0 +1,328 @@
1
+ import { css, html } from 'lit';
2
+ import { FlowNode } from 'lit-flow';
3
+
4
+ const KIND_LAYER_TONE: Record<string, string> = {
5
+ intent_question: 'tone-question',
6
+ intent_analysis: 'tone-analysis',
7
+ intent_option: 'tone-option',
8
+ intent_decision: 'tone-decision',
9
+ work_item: 'tone-work',
10
+ work_epic: 'tone-epic',
11
+ architecture_block: 'tone-architecture',
12
+ schematic_block: 'tone-schematic',
13
+ };
14
+
15
+ function statusBadgeClass(status: unknown): string {
16
+ const value = String(status ?? '').trim().toLowerCase();
17
+ if (value === 'done' || value === 'verified') {
18
+ return 'is-done';
19
+ }
20
+ if (value === 'blocked') {
21
+ return 'is-blocked';
22
+ }
23
+ if (value === 'doing' || value === 'in_progress' || value === 'claimed') {
24
+ return 'is-doing';
25
+ }
26
+ if (value === 'ready') {
27
+ return 'is-ready';
28
+ }
29
+ return 'is-neutral';
30
+ }
31
+
32
+ export class GraphCardNode extends FlowNode {
33
+ static styles = [
34
+ ...(Array.isArray(FlowNode.styles) ? FlowNode.styles : [FlowNode.styles]),
35
+ css`
36
+ :host {
37
+ --node-background: var(--wg-graph-node-bg, #ffffff);
38
+ --node-border: var(--wg-graph-node-border, #dfe1e6);
39
+ --node-text: var(--wg-graph-node-text, #172b4d);
40
+ --node-subtext: var(--wg-graph-node-subtext, #5e6c84);
41
+ --node-selected-border: var(--wg-graph-node-selected-border, #0052cc);
42
+ background: transparent;
43
+ border: none;
44
+ border-radius: 0;
45
+ box-shadow: none;
46
+ font-family: "Segoe UI", system-ui, sans-serif;
47
+ padding: 0;
48
+ }
49
+
50
+ :host(:hover),
51
+ :host([selected]),
52
+ :host([dragging]) {
53
+ box-shadow: none;
54
+ }
55
+
56
+ .graph-card {
57
+ box-sizing: border-box;
58
+ width: 100%;
59
+ height: 100%;
60
+ padding: 10px 12px 11px;
61
+ border: 1px solid var(--node-border);
62
+ border-radius: 10px;
63
+ background: var(--node-background);
64
+ color: var(--node-text);
65
+ text-align: left;
66
+ cursor: pointer;
67
+ box-shadow: 0 1px 2px rgba(9, 30, 66, 0.08);
68
+ transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
69
+ }
70
+
71
+ .graph-card:hover {
72
+ border-color: var(--wg-graph-node-hover-border, #c1c7d0);
73
+ }
74
+
75
+ :host([selected]) .graph-card,
76
+ .graph-card.is-focused,
77
+ .graph-card.is-selected:not(.is-rejected) {
78
+ border-color: var(--node-selected-border);
79
+ box-shadow: none;
80
+ }
81
+
82
+ .graph-card.is-rejected {
83
+ opacity: 0.58;
84
+ border-style: dashed;
85
+ background: var(--wg-graph-node-rejected-bg, #f4f5f7);
86
+ box-shadow: none;
87
+ }
88
+
89
+ .graph-card.tone-question {
90
+ border-left: 3px solid var(--wg-graph-tone-question, #0052cc);
91
+ }
92
+
93
+ .graph-card.tone-analysis {
94
+ border-left: 3px solid var(--wg-graph-tone-analysis, #5b21b6);
95
+ }
96
+
97
+ .graph-card.tone-option.is-selected:not(.is-rejected) {
98
+ background: var(--wg-graph-node-selected-bg, linear-gradient(180deg, #deebff 0%, #ffffff 72%));
99
+ }
100
+
101
+ .graph-card.tone-decision {
102
+ border-left: 3px solid var(--wg-graph-tone-decision, #9a6700);
103
+ }
104
+
105
+ .graph-card.tone-work.is-done {
106
+ border-color: var(--wg-graph-tone-work, #1a7f37);
107
+ }
108
+
109
+ .graph-card.tone-epic {
110
+ border-left: 3px solid var(--wg-graph-tone-epic, #8250df);
111
+ }
112
+
113
+ .graph-card.tone-epic.is-done {
114
+ border-color: var(--wg-graph-tone-work, #1a7f37);
115
+ }
116
+
117
+ .graph-card.tone-epic:not(.is-done) .status-badge.is-ready {
118
+ background: rgba(130, 80, 223, 0.18);
119
+ }
120
+
121
+ .graph-card.tone-architecture,
122
+ .graph-card.tone-schematic {
123
+ border-left: 3px solid var(--wg-graph-tone-architecture, #0052cc);
124
+ }
125
+
126
+ .layer {
127
+ display: inline-block;
128
+ font-size: 10px;
129
+ letter-spacing: 0.06em;
130
+ text-transform: uppercase;
131
+ color: var(--node-subtext);
132
+ margin-bottom: 7px;
133
+ padding: 2px 7px;
134
+ border-radius: 999px;
135
+ background: var(--wg-graph-layer-bg, rgba(9, 30, 66, 0.05));
136
+ border: 1px solid var(--wg-graph-layer-border, rgba(9, 30, 66, 0.1));
137
+ }
138
+
139
+ .tone-question .layer {
140
+ color: var(--wg-graph-tone-question, #0052cc);
141
+ }
142
+
143
+ .tone-analysis .layer {
144
+ color: var(--wg-graph-tone-analysis, #5b21b6);
145
+ }
146
+
147
+ .tone-option.is-selected:not(.is-rejected) .layer {
148
+ color: var(--wg-graph-tone-question, #0052cc);
149
+ }
150
+
151
+ .tone-decision .layer {
152
+ color: var(--wg-graph-tone-decision, #9a6700);
153
+ }
154
+
155
+ .tone-work .layer {
156
+ color: var(--wg-graph-tone-work, #1a7f37);
157
+ }
158
+
159
+ .tone-epic .layer {
160
+ color: var(--wg-graph-tone-epic, #8250df);
161
+ }
162
+
163
+ .title {
164
+ font-size: 13px;
165
+ line-height: 1.35;
166
+ font-weight: 600;
167
+ margin: 0;
168
+ display: -webkit-box;
169
+ -webkit-line-clamp: 3;
170
+ -webkit-box-orient: vertical;
171
+ overflow: hidden;
172
+ }
173
+
174
+ .summary {
175
+ font-size: 12px;
176
+ line-height: 1.35;
177
+ color: var(--node-subtext);
178
+ margin: 6px 0 0;
179
+ display: -webkit-box;
180
+ -webkit-line-clamp: 2;
181
+ -webkit-box-orient: vertical;
182
+ overflow: hidden;
183
+ }
184
+
185
+ .status-row {
186
+ margin-top: 9px;
187
+ }
188
+
189
+ .status-badge {
190
+ display: inline-flex;
191
+ align-items: center;
192
+ gap: 4px;
193
+ padding: 2px 8px;
194
+ border-radius: 999px;
195
+ font-size: 10px;
196
+ line-height: 1.4;
197
+ text-transform: lowercase;
198
+ border: 1px solid var(--wg-graph-badge-border, #dfe1e6);
199
+ color: var(--node-subtext);
200
+ background: var(--wg-graph-badge-bg, #f4f5f7);
201
+ }
202
+
203
+ .status-badge.is-done {
204
+ border-color: var(--wg-graph-badge-done-border, #abf5d1);
205
+ color: var(--wg-graph-badge-done-text, #006644);
206
+ background: var(--wg-graph-badge-done-bg, #e3fcef);
207
+ }
208
+
209
+ .status-badge.is-doing {
210
+ border-color: var(--wg-graph-badge-doing-border, #f0c36d);
211
+ color: var(--wg-graph-badge-doing-text, #7a4f01);
212
+ background: var(--wg-graph-badge-doing-bg, #fff7d6);
213
+ }
214
+
215
+ .status-badge.is-ready {
216
+ border-color: var(--wg-graph-badge-ready-border, #85b8ff);
217
+ color: var(--wg-graph-badge-ready-text, #0052cc);
218
+ background: var(--wg-graph-badge-ready-bg, #deebff);
219
+ }
220
+
221
+ .status-badge.is-blocked {
222
+ border-color: var(--wg-graph-badge-blocked-border, #ffbdad);
223
+ color: var(--wg-graph-badge-blocked-text, #bf2600);
224
+ background: var(--wg-graph-badge-blocked-bg, #ffebe6);
225
+ }
226
+
227
+ .graph-card-wrap {
228
+ position: relative;
229
+ width: 100%;
230
+ height: 100%;
231
+ }
232
+
233
+ .edge-port {
234
+ position: absolute;
235
+ width: 8px;
236
+ height: 8px;
237
+ opacity: 0;
238
+ pointer-events: none;
239
+ }
240
+
241
+ .edge-port.target {
242
+ left: -4px;
243
+ top: 50%;
244
+ transform: translateY(-50%);
245
+ }
246
+
247
+ .edge-port.source {
248
+ right: -4px;
249
+ top: 50%;
250
+ transform: translateY(-50%);
251
+ }
252
+
253
+ .edge-port.target-top {
254
+ top: -4px;
255
+ left: 50%;
256
+ transform: translateX(-50%);
257
+ }
258
+
259
+ .edge-port.source-bottom {
260
+ bottom: -4px;
261
+ left: 50%;
262
+ transform: translateX(-50%);
263
+ }
264
+ `,
265
+ ];
266
+
267
+ override render() {
268
+ const data = (this.data ?? {}) as Record<string, unknown>;
269
+ const kind = String(data.kind ?? '');
270
+ const tone = KIND_LAYER_TONE[kind] ?? '';
271
+ const rejected = data.rejected === true || (kind === 'intent_option' && data.selected !== true);
272
+ const done = data.status === 'done' || data.status === 'verified';
273
+ const classes = [
274
+ 'graph-card',
275
+ tone,
276
+ data.selected === true ? 'is-selected' : '',
277
+ rejected ? 'is-rejected' : '',
278
+ data.focused === true ? 'is-focused' : '',
279
+ done ? 'is-done' : '',
280
+ ].filter(Boolean).join(' ');
281
+
282
+ const progress = Number(data.childCount) > 0
283
+ ? ` ${data.doneChildCount ?? 0}/${data.childCount}`
284
+ : '';
285
+
286
+ const statusText = data.status ? `${String(data.status)}${progress}` : '';
287
+
288
+ return html`
289
+ <div class="graph-card-wrap">
290
+ <button type="button" class=${classes} @click=${this.onCardClick}>
291
+ <div class="layer">${String(data.layer ?? kind)}</div>
292
+ <p class="title">${String(data.title ?? this.id)}</p>
293
+ ${data.summary ? html`<p class="summary">${String(data.summary)}</p>` : null}
294
+ ${statusText ? html`
295
+ <div class="status-row">
296
+ <span class="status-badge ${statusBadgeClass(data.status)}">${statusText}</span>
297
+ </div>
298
+ ` : null}
299
+ </button>
300
+ <div class="edge-port target" data-handle-id="target"></div>
301
+ <div class="edge-port source" data-handle-id="source"></div>
302
+ <div class="edge-port target-top" data-handle-id="target-top"></div>
303
+ <div class="edge-port source-bottom" data-handle-id="source-bottom"></div>
304
+ </div>
305
+ `;
306
+ }
307
+
308
+ private onCardClick(event: Event) {
309
+ event.stopPropagation();
310
+ const data = (this.data ?? {}) as Record<string, unknown>;
311
+ this.dispatchEvent(new CustomEvent('graph-node-click', {
312
+ bubbles: true,
313
+ composed: true,
314
+ detail: {
315
+ nodeId: this.id,
316
+ kind: data.kind,
317
+ taskId: data.taskId || undefined,
318
+ intentNodeId: data.intentNodeId || undefined,
319
+ blockId: data.blockId || undefined,
320
+ schematicId: data.schematicId || undefined,
321
+ },
322
+ }));
323
+ }
324
+ }
325
+
326
+ if (!customElements.get('graph-card-node')) {
327
+ customElements.define('graph-card-node', GraphCardNode);
328
+ }
@@ -0,0 +1,322 @@
1
+ import 'lit-flow/dist/style.css';
2
+ import './graphCanvasTheme.css';
3
+ import {
4
+ FlowBackground,
5
+ FlowCanvas,
6
+ FlowControls,
7
+ } from 'lit-flow';
8
+ import { mountGraphCanvasMinimap, unmountGraphCanvasMinimap } from './graphCanvasMinimap.js';
9
+ import {
10
+ injectFlowCanvasNativeEdgeHide,
11
+ mountGraphCanvasSvgEdges,
12
+ repaintGraphCanvasSvgEdges,
13
+ unmountGraphCanvasSvgEdges,
14
+ } from './graphCanvasSvgEdges.js';
15
+ import { graphCanvasProjectionToFlow } from '../graphCanvasProjectionToFlow.mjs';
16
+ import {
17
+ getDownstreamNodeIds,
18
+ getIncomingNodeIds,
19
+ getOutgoingNodeIds,
20
+ getSiblingNodeIds,
21
+ getUpstreamNodeIds,
22
+ sortNodeIdsByVerticalPosition,
23
+ } from '../graphCanvasTraversal.mjs';
24
+ import './graphCardNode.js';
25
+
26
+ type GraphCanvasProjection = {
27
+ schema: string;
28
+ layoutDirection?: string;
29
+ viewId?: string;
30
+ nodes: Array<{ id: string; y?: number }>;
31
+ edges: Array<{ id: string; from: string; to: string }>;
32
+ };
33
+
34
+ type MountOptions = {
35
+ height?: number;
36
+ fill?: boolean;
37
+ };
38
+
39
+ const mountedHosts = new WeakMap<HTMLElement, FlowCanvas>();
40
+ const themeObservers = new WeakMap<HTMLElement, MutationObserver>();
41
+ const resizeObservers = new WeakMap<HTMLElement, ResizeObserver>();
42
+ const minimapHosts = new WeakMap<HTMLElement, HTMLElement>();
43
+
44
+ function resolveGraphCanvasTheme(): 'dark' | 'light' {
45
+ return document.body?.dataset?.theme === 'dark' ? 'dark' : 'light';
46
+ }
47
+
48
+ function applyGraphCanvasHostTheme(host: HTMLElement, theme: 'dark' | 'light') {
49
+ host.dataset.graphTheme = theme;
50
+ }
51
+
52
+ function applyFlowCanvasTheme(
53
+ canvas: FlowCanvas,
54
+ background: FlowBackground,
55
+ theme: 'dark' | 'light',
56
+ ) {
57
+ canvas.setAttribute('theme', theme);
58
+ background.setAttribute('variant', 'dots');
59
+ background.setAttribute('gap', theme === 'dark' ? '20' : '18');
60
+ background.setAttribute('color', theme === 'dark' ? '#3c3c3c' : '#dfe1e6');
61
+ }
62
+
63
+ function watchGraphCanvasTheme(
64
+ host: HTMLElement,
65
+ canvas: FlowCanvas,
66
+ background: FlowBackground,
67
+ projection: GraphCanvasProjection,
68
+ getSelectedNodeId: () => string,
69
+ ) {
70
+ themeObservers.get(host)?.disconnect();
71
+
72
+ const observer = new MutationObserver(() => {
73
+ const theme = resolveGraphCanvasTheme();
74
+ applyGraphCanvasHostTheme(host, theme);
75
+ applyFlowCanvasTheme(canvas, background, theme);
76
+ minimapHosts.get(host)?.setAttribute('data-theme', theme);
77
+ const { nodes } = graphCanvasProjectionToFlow(projection, { theme });
78
+ const selectedNodeId = getSelectedNodeId();
79
+ canvas.setNodes(nodes.map((node) => ({
80
+ ...node,
81
+ selected: node.id === selectedNodeId,
82
+ })));
83
+ canvas.setEdges([]);
84
+ repaintGraphCanvasSvgEdges(canvas, projection);
85
+ canvas.requestUpdate();
86
+ });
87
+
88
+ observer.observe(document.body, { attributes: true, attributeFilter: ['data-theme'] });
89
+ themeObservers.set(host, observer);
90
+ }
91
+
92
+ function ensureFlowTagsRegistered() {
93
+ if (!customElements.get('flow-canvas')) {
94
+ customElements.define('flow-background', FlowBackground);
95
+ customElements.define('flow-controls', FlowControls);
96
+ customElements.define('flow-canvas', FlowCanvas);
97
+ }
98
+ }
99
+
100
+ function parseProjection(host: HTMLElement): GraphCanvasProjection | null {
101
+ const raw = host.getAttribute('data-graph-canvas-projection');
102
+ if (!raw) {
103
+ return null;
104
+ }
105
+ try {
106
+ return JSON.parse(raw) as GraphCanvasProjection;
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ function selectNode(canvas: FlowCanvas, nodeId: string) {
113
+ const nodes = canvas.nodes.map((node) => ({
114
+ ...node,
115
+ selected: node.id === nodeId,
116
+ }));
117
+ canvas.setNodes(nodes);
118
+ }
119
+
120
+ function focusNodeByKeyboard(canvas: FlowCanvas, projection: GraphCanvasProjection, nodeId: string) {
121
+ selectNode(canvas, nodeId);
122
+ canvas.instance.updateNode(nodeId, { selected: true });
123
+ canvas.requestUpdate();
124
+ }
125
+
126
+ function injectFlowCanvasChromeReset(canvas: FlowCanvas) {
127
+ const root = canvas.shadowRoot;
128
+ if (!root || root.querySelector('[data-wg-flow-chrome-reset]')) {
129
+ return;
130
+ }
131
+ const style = document.createElement('style');
132
+ style.setAttribute('data-wg-flow-chrome-reset', 'true');
133
+ style.textContent = `
134
+ .edge-label {
135
+ background: transparent !important;
136
+ border: none !important;
137
+ border-radius: 0 !important;
138
+ box-shadow: none !important;
139
+ padding: 0 !important;
140
+ }
141
+ `;
142
+ root.appendChild(style);
143
+ }
144
+
145
+ export function mountGraphCanvasLitFlow(host: HTMLElement, options: MountOptions = {}) {
146
+ const previous = mountedHosts.get(host);
147
+ if (previous) {
148
+ unmountGraphCanvasSvgEdges(previous);
149
+ previous.instance?.destroy();
150
+ }
151
+ themeObservers.get(host)?.disconnect();
152
+ themeObservers.delete(host);
153
+ resizeObservers.get(host)?.disconnect();
154
+ resizeObservers.delete(host);
155
+ const previousMinimapHost = minimapHosts.get(host);
156
+ if (previousMinimapHost) {
157
+ unmountGraphCanvasMinimap(previousMinimapHost);
158
+ minimapHosts.delete(host);
159
+ }
160
+ mountedHosts.delete(host);
161
+
162
+ ensureFlowTagsRegistered();
163
+
164
+ const projection = parseProjection(host);
165
+ if (!projection?.nodes?.length) {
166
+ host.innerHTML = '<div class="empty">Graph projection пуст</div>';
167
+ return;
168
+ }
169
+
170
+ const fill = options.fill === true || host.dataset.graphCanvasFill === 'true';
171
+ host.innerHTML = '';
172
+ host.classList.add('graph-canvas-lit-flow-host');
173
+ host.style.position = fill ? 'absolute' : 'relative';
174
+ host.style.width = '100%';
175
+ if (fill) {
176
+ host.style.inset = '0';
177
+ host.style.height = '100%';
178
+ host.style.minHeight = '0';
179
+ } else {
180
+ const height = options.height ?? Number(host.dataset.graphCanvasHeight ?? 480);
181
+ host.style.minHeight = `${height}px`;
182
+ host.style.height = `${height}px`;
183
+ }
184
+
185
+ const shell = document.createElement('div');
186
+ shell.className = 'graph-canvas-lit-flow-shell';
187
+ shell.style.width = '100%';
188
+ shell.style.height = '100%';
189
+ shell.style.position = 'relative';
190
+
191
+ const canvas = document.createElement('flow-canvas') as FlowCanvas;
192
+ canvas.style.display = 'block';
193
+ canvas.style.width = '100%';
194
+ canvas.style.height = '100%';
195
+ canvas.nodeTypes = { 'graph-card': 'graph-card-node' };
196
+
197
+ const background = document.createElement('flow-background');
198
+ background.setAttribute('slot', 'background');
199
+
200
+ const theme = resolveGraphCanvasTheme();
201
+ applyGraphCanvasHostTheme(host, theme);
202
+ applyFlowCanvasTheme(canvas, background, theme);
203
+
204
+ const controls = document.createElement('flow-controls');
205
+ const minimapHost = document.createElement('div');
206
+ minimapHost.className = 'graph-canvas-minimap-host';
207
+
208
+ canvas.appendChild(background);
209
+ canvas.appendChild(controls);
210
+ canvas.appendChild(minimapHost);
211
+ shell.appendChild(canvas);
212
+ host.appendChild(shell);
213
+ minimapHosts.set(host, minimapHost);
214
+
215
+ const { nodes } = graphCanvasProjectionToFlow(projection, { theme });
216
+ let selectedNodeId = projection.nodes[0]?.id ?? '';
217
+
218
+ const applyGraph = () => {
219
+ canvas.setNodes(nodes.map((node) => ({
220
+ ...node,
221
+ selected: node.id === selectedNodeId,
222
+ })));
223
+ canvas.setEdges([]);
224
+ };
225
+
226
+ customElements.whenDefined('flow-canvas').then(() => {
227
+ injectFlowCanvasChromeReset(canvas);
228
+ injectFlowCanvasNativeEdgeHide(canvas);
229
+ applyGraph();
230
+ mountGraphCanvasSvgEdges(canvas, projection);
231
+ controls.instance = canvas.instance;
232
+ mountGraphCanvasMinimap(minimapHost, () => canvas, {
233
+ width: 168,
234
+ height: 108,
235
+ theme,
236
+ });
237
+ watchGraphCanvasTheme(host, canvas, background, projection, () => selectedNodeId);
238
+ window.requestAnimationFrame(() => {
239
+ canvas.instance.fitView();
240
+ });
241
+ if (fill) {
242
+ const resizeTarget = host.parentElement ?? host;
243
+ const observer = new ResizeObserver(() => {
244
+ window.requestAnimationFrame(() => {
245
+ canvas.instance?.fitView({ padding: 0.12, duration: 0 });
246
+ });
247
+ });
248
+ observer.observe(resizeTarget);
249
+ resizeObservers.set(host, observer);
250
+ }
251
+ });
252
+
253
+ host.addEventListener('graph-node-click', (event) => {
254
+ const detail = (event as CustomEvent).detail ?? {};
255
+ if (detail.nodeId) {
256
+ selectedNodeId = detail.nodeId;
257
+ selectNode(canvas, selectedNodeId);
258
+ }
259
+ host.dispatchEvent(new CustomEvent('workgraph-graph-node-click', {
260
+ bubbles: true,
261
+ detail,
262
+ }));
263
+ });
264
+
265
+ host.tabIndex = 0;
266
+ host.addEventListener('keydown', (event) => {
267
+ if (!selectedNodeId) {
268
+ return;
269
+ }
270
+
271
+ let nextId = selectedNodeId;
272
+ if (event.key === 'ArrowRight') {
273
+ const outgoing = getOutgoingNodeIds(selectedNodeId, projection.edges);
274
+ nextId = outgoing[0] ?? selectedNodeId;
275
+ } else if (event.key === 'ArrowLeft') {
276
+ const incoming = getIncomingNodeIds(selectedNodeId, projection.edges);
277
+ nextId = incoming[0] ?? selectedNodeId;
278
+ } else if (event.key === 'ArrowDown') {
279
+ const siblings = getSiblingNodeIds(selectedNodeId, projection.edges, projection.nodes);
280
+ const downstream = getDownstreamNodeIds(selectedNodeId, projection.edges);
281
+ const candidates = sortNodeIdsByVerticalPosition([...siblings, ...downstream], projection.nodes)
282
+ .filter((id) => id !== selectedNodeId);
283
+ nextId = candidates[0] ?? selectedNodeId;
284
+ } else if (event.key === 'ArrowUp') {
285
+ const upstream = getUpstreamNodeIds(selectedNodeId, projection.edges);
286
+ nextId = upstream[0] ?? selectedNodeId;
287
+ } else if (event.key === 'Enter') {
288
+ host.dispatchEvent(new CustomEvent('workgraph-graph-node-click', {
289
+ bubbles: true,
290
+ detail: {
291
+ nodeId: selectedNodeId,
292
+ ...(nodes.find((node) => node.id === selectedNodeId)?.data ?? {}),
293
+ },
294
+ }));
295
+ return;
296
+ } else {
297
+ return;
298
+ }
299
+
300
+ event.preventDefault();
301
+ if (nextId !== selectedNodeId) {
302
+ selectedNodeId = nextId;
303
+ focusNodeByKeyboard(canvas, projection, selectedNodeId);
304
+ }
305
+ });
306
+
307
+ mountedHosts.set(host, canvas);
308
+ }
309
+
310
+ export function mountAllGraphCanvasLitFlowHosts(root: ParentNode = document) {
311
+ for (const host of root.querySelectorAll<HTMLElement>('[data-graph-canvas-projection]')) {
312
+ mountGraphCanvasLitFlow(host);
313
+ }
314
+ }
315
+
316
+ declare global {
317
+ interface Window {
318
+ __WORKGRAPH_MOUNT_GRAPH_CANVAS__?: typeof mountAllGraphCanvasLitFlowHosts;
319
+ }
320
+ }
321
+
322
+ window.__WORKGRAPH_MOUNT_GRAPH_CANVAS__ = mountAllGraphCanvasLitFlowHosts;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @param {string} value
3
+ */
4
+ function escapeHtml(value) {
5
+ return String(value ?? '')
6
+ .replace(/&/g, '&amp;')
7
+ .replace(/</g, '&lt;')
8
+ .replace(/>/g, '&gt;')
9
+ .replace(/"/g, '&quot;');
10
+ }
11
+
12
+ /**
13
+ * @param {string} label
14
+ * @param {'dark' | 'light'} theme
15
+ * @param {{ rejected?: boolean }} [options]
16
+ */
17
+ export function buildGraphCanvasEdgeLabelHtml(label, theme, options = {}) {
18
+ const text = String(label ?? '').trim();
19
+ if (text === '') {
20
+ return '';
21
+ }
22
+
23
+ const rejected = options.rejected === true;
24
+ const palette = theme === 'light'
25
+ ? {
26
+ bg: rejected ? '#f4f5f7' : '#ffffff',
27
+ border: rejected ? '#dfe1e6' : '#c1c7d0',
28
+ color: rejected ? '#97a0af' : '#5e6c84',
29
+ }
30
+ : {
31
+ bg: rejected ? '#2a2a2a' : '#2d2d30',
32
+ border: rejected ? '#3c3c3c' : '#4a4a4a',
33
+ color: rejected ? '#6e6e6e' : '#9d9d9d',
34
+ };
35
+
36
+ return `<span style="display:inline-block;padding:2px 7px;border-radius:4px;font-size:11px;line-height:1.25;font-family:Segoe UI,system-ui,sans-serif;background:${palette.bg};border:1px solid ${palette.border};color:${palette.color};${rejected ? 'opacity:0.75;font-style:italic;' : ''}">${escapeHtml(text)}</span>`;
37
+ }
38
+
39
+ /**
40
+ * @param {object} edge
41
+ * @param {'dark' | 'light'} theme
42
+ */
43
+ export function buildGraphCanvasEdgeStrokeStyle(edge, theme) {
44
+ const rejected = edge.rejected === true;
45
+ const upstream = edge.upstream === true;
46
+ const stroke = rejected
47
+ ? (theme === 'light' ? '#c1c7d0' : '#555555')
48
+ : upstream
49
+ ? (theme === 'light' ? '#97a0af' : '#6e6e6e')
50
+ : (theme === 'light' ? '#8b8b95' : '#858585');
51
+
52
+ return {
53
+ stroke,
54
+ strokeWidth: rejected ? 1.5 : 2,
55
+ ...(rejected ? { strokeDasharray: '6 4', opacity: 0.42 } : {}),
56
+ ...(upstream && !rejected ? { strokeDasharray: '5 4', opacity: 0.72 } : {}),
57
+ };
58
+ }