@huynle/hub 0.8.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -0
- package/README.md +69 -12
- package/package.json +5 -1
- package/packages/claude-code/package.json +1 -1
- package/packages/cli/dist/hub-cli.js +278 -35
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/bootstrap/adapters/opencode/README.md +4 -2
- package/packages/cli/src/bootstrap/release/harness-pins.json +22 -0
- package/packages/cli/src/bootstrap/release/harness-pins.pending.json +1 -20
- package/packages/cli/src/cli.ts +23 -10
- package/packages/cli/src/http.ts +1 -0
- package/packages/cli/src/launches.ts +5 -1
- package/packages/cli/src/mcp.ts +28 -8
- package/packages/cli/src/messaging.ts +14 -0
- package/packages/cli/src/team-intake-mcp.ts +26 -2
- package/packages/codex/package.json +1 -1
- package/packages/node/package.json +1 -1
- package/packages/preset/package.json +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/schema/app-delivery/v1.schema.json +677 -0
- package/packages/protocol/src/artifacts.ts +43 -0
- package/packages/protocol/src/credentials.ts +38 -0
- package/packages/protocol/src/index.ts +56 -5
- package/packages/protocol/src/launch.ts +7 -2
- package/packages/protocol/src/markdown.ts +753 -0
- package/packages/protocol/src/media-allowlist.ts +240 -0
- package/packages/protocol/src/media.ts +120 -0
- package/packages/protocol/src/preview-bundle.ts +221 -0
- package/packages/protocol/src/voice-recording.ts +8 -0
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/client.ts +17 -3
- package/packages/sdk/src/errors.ts +1 -0
- package/packages/sdk/src/team-intake.ts +8 -0
- package/packages/sdk/src/types.ts +16 -2
- package/packages/spoke-sdk/package.json +1 -1
- package/src/central/agent/delivery.ts +35 -6
- package/src/central/agent/errors.ts +2 -0
- package/src/central/agent/events.ts +11 -2
- package/src/central/agent/launch-engine.ts +65 -4
- package/src/central/agent/launch-routes.ts +8 -0
- package/src/central/agent/launch-spec.ts +18 -1
- package/src/central/agent/launch-store.ts +2 -2
- package/src/central/agent/message-routes.ts +49 -11
- package/src/central/agent/messages.ts +17 -4
- package/src/central/agent/not-after.ts +28 -0
- package/src/central/agent/placement.ts +36 -4
- package/src/central/agent/routes.ts +3 -1
- package/src/central/agent/webhook-routes.ts +21 -9
- package/src/central/agent/webhooks.ts +27 -9
- package/src/central/artifacts/host.ts +3 -1
- package/src/central/artifacts/maintenance.ts +2 -1
- package/src/central/artifacts/preview-bundle.ts +81 -0
- package/src/central/artifacts/previews.ts +94 -0
- package/src/central/artifacts/routes.ts +12 -1
- package/src/central/artifacts/store.ts +198 -8
- package/src/central/config.ts +34 -0
- package/src/central/fetch-budget.ts +59 -0
- package/src/central/fetcher.ts +372 -0
- package/src/central/http.ts +277 -22
- package/src/central/media-remote.ts +248 -0
- package/src/central/media-timeline.ts +317 -0
- package/src/central/media-unfurl.ts +119 -0
- package/src/central/profiles/content.ts +3 -35
- package/src/central/recording/routes.ts +2 -2
- package/src/central/recording/schema.ts +3 -1
- package/src/central/registry.ts +449 -21
- package/src/central/spoke-ws.ts +17 -3
- package/src/central/store.ts +21 -0
- package/src/central/teams/intake-routes.ts +2 -1
- package/src/central/teams/intake.ts +200 -15
- package/src/central/unfurl-parse.ts +313 -0
- package/src/index.ts +68 -8
- package/src/speakable.ts +33 -1
- package/src/spoke/conversation.ts +7 -4
- package/src/spoke/deliver.ts +19 -0
- package/src/spoke/media-bytes.ts +171 -0
- package/src/spoke/media-policy.ts +394 -0
- package/src/spoke/media.ts +189 -0
- package/src/spoke/own-server.ts +30 -0
- package/src/spoke/preview-bundle.ts +538 -0
- package/src/spoke/spoke.ts +387 -17
- package/src/spoke/timeline.ts +59 -5
- package/src/spoke/voice-front.ts +335 -0
- package/web/dist/assets/24BXRY42-Zfr7jm_p.js +2 -0
- package/web/dist/assets/4FHPYW2L-C6Z67098.js +2 -0
- package/web/dist/assets/4LWW3ZNH-CUiYeygH.js +1 -0
- package/web/dist/assets/5THPTBWS-Ce_hS451.js +1 -0
- package/web/dist/assets/5XYWFMKW-CW-BCKiK.js +4 -0
- package/web/dist/assets/64PMEAB3-BjpCDGU9.js +8 -0
- package/web/dist/assets/6BEWHPQE-BpNa96Ze.js +2 -0
- package/web/dist/assets/6PIGVMFT-YXn9Hgnb.js +1 -0
- package/web/dist/assets/75JW6PN6-BFpDBuGN.js +2 -0
- package/web/dist/assets/7WLXJQOR-D6QVYUu9.js +1 -0
- package/web/dist/assets/7XWI72E6-BMuIspEy.js +2 -0
- package/web/dist/assets/A4UD2L5Z-BQzcD9dU.js +8 -0
- package/web/dist/assets/ArtifactPage-4GoBqSpU.js +1 -0
- package/web/dist/assets/ArtifactPane-Dx_0iWwH.js +1 -0
- package/web/dist/assets/ArtifactWorkbench-CuHgARMk.js +6 -0
- package/web/dist/assets/BGW36YJW-CJMhya8S.js +4 -0
- package/web/dist/assets/CGIZI67J-DYDSLME8.js +2 -0
- package/web/dist/assets/DKTLEL5E-DciMwFJ1.js +1 -0
- package/web/dist/assets/DNNCRC2E-BIeKf5t-.js +5 -0
- package/web/dist/assets/Diagram-CZM_R7MG.js +1 -0
- package/web/dist/assets/Diagram-vdpmp3xu.css +1 -0
- package/web/dist/assets/E67EBMDD--AucIw59.js +5 -0
- package/web/dist/assets/EFYWJBJK-CVywe_D5.js +1 -0
- package/web/dist/assets/EXE5EH6O-DR1IHCTr.js +4 -0
- package/web/dist/assets/FTXIJQGB-BZVuiBn1.js +1 -0
- package/web/dist/assets/G3MDBVQH-BZmnMkFw.js +1 -0
- package/web/dist/assets/G6GPDDNC-DEz8F449.js +2 -0
- package/web/dist/assets/G76NGGFM-CrB2V34K.js +1 -0
- package/web/dist/assets/GH6G3TJC-D3RTYEH4.js +2 -0
- package/web/dist/assets/HPNUA7RO-B19NeNOJ.js +1 -0
- package/web/dist/assets/HQRGC5D7-Dmpzq6Rn.js +4 -0
- package/web/dist/assets/IX6Z2TVR-DphRHnek.js +5 -0
- package/web/dist/assets/J77NQRCJ-B7AGcxvw.js +3 -0
- package/web/dist/assets/JAD36DTZ-B9-dbsS0.js +5 -0
- package/web/dist/assets/LF3NJQAX-koL91UZ9.js +2 -0
- package/web/dist/assets/LRZCOFJZ-Cdjasfuf.js +6 -0
- package/web/dist/assets/Library-TwvrwqSk.js +2 -0
- package/web/dist/assets/Lightbox-C8KNKwOA.css +1 -0
- package/web/dist/assets/Lightbox-Du56MXGX.js +1 -0
- package/web/dist/assets/MediaImage-Ds_JUx3a.js +4 -0
- package/web/dist/assets/N5NF2FGY-OuapYTfr.js +2 -0
- package/web/dist/assets/N7OWWQHY-BNLd-Q_U.js +2 -0
- package/web/dist/assets/NT5JY5WP-1lJM-aun.js +1 -0
- package/web/dist/assets/OT7W3RNY-jDL3drzX.js +4 -0
- package/web/dist/assets/P67P66DB-CE-opRb8.js +3 -0
- package/web/dist/assets/PFLLLM7K-DDJFgzW3.js +2 -0
- package/web/dist/assets/PUAMC4MJ-CtBm-WuU.js +12 -0
- package/web/dist/assets/PVFCHRLY-CG5JXv60.js +2 -0
- package/web/dist/assets/ProfilesDestination-rKNDpLjR.js +1 -0
- package/web/dist/assets/QQW2P742-BbcEkpa4.js +1 -0
- package/web/dist/assets/RVARI57O-CgA1WoRv.js +1 -0
- package/web/dist/assets/S63OEDPG-CKlhpvl2.js +3 -0
- package/web/dist/assets/SG5TVYLW-BBtiwXPL.js +2 -0
- package/web/dist/assets/TeamsPage-DtJylLrt.js +1 -0
- package/web/dist/assets/UGRSUGA2-urZ_hx83.js +5 -0
- package/web/dist/assets/V44WA6U7-DYfX3OlM.js +1 -0
- package/web/dist/assets/V67O4WWV-xYxJ00QW.js +1 -0
- package/web/dist/assets/VBUCM3YD-JHGjuJC_.js +1 -0
- package/web/dist/assets/VINGRCPA-DZuJa3SH.js +7 -0
- package/web/dist/assets/WL6DNKX4-BHqripdA.js +2 -0
- package/web/dist/assets/WQ63CJYP-D6G5frDY.js +2 -0
- package/web/dist/assets/Watchtower-Bmgdyfjb.js +2 -0
- package/web/dist/assets/XOYAOB3S-L-KeA1EF.js +13 -0
- package/web/dist/assets/Y5LQLG3M-dr2-erb8.js +2 -0
- package/web/dist/assets/YIK6JTN6--1t-Ud8k.js +2 -0
- package/web/dist/assets/artifacts-DU9uRgs1.js +1 -0
- package/web/dist/assets/index-C3ci5UtY.css +1 -0
- package/web/dist/assets/index-D-xG9nrc.js +36 -0
- package/web/dist/assets/lazy-f3CuUNJZ.js +43 -0
- package/web/dist/assets/live-7q72Lyj2.js +5 -0
- package/web/dist/assets/mermaid.worker-WDVRAh32.js +1 -0
- package/web/dist/assets/pane-layout-0RrLHGDr.js +1 -0
- package/web/dist/assets/preload-helper-BLdHbfdN.js +1 -0
- package/web/dist/assets/{renderer2d-CYag1CnR.js → renderer2d-hfZxM6Xl.js} +1 -1
- package/web/dist/assets/{renderer3d-BRB6rgDJ.js → renderer3d-Km9vipTM.js} +1 -1
- package/web/dist/assets/{safe-area-fOy0AOuG.js → safe-area-BAbgBiFk.js} +1 -1
- package/web/dist/assets/{silero-worker-DhlIHyD_.js → silero-worker-e802lB5T.js} +1 -1
- package/web/dist/assets/smart-turn-v3.2-cpu-WpeLRbPA.onnx +0 -0
- package/web/dist/assets/smart-turn-worker-DaVJPdvJ.js +11 -0
- package/web/dist/assets/source-BWlvaODa.js +1 -0
- package/web/dist/assets/toast-Cuu2K0hx.js +1 -0
- package/web/dist/assets/ui-CFB4wQfF.js +3 -0
- package/web/dist/index.html +8 -6
- package/web/src/voice/turn/LICENSE +28 -0
- package/web/dist/assets/ArtifactPage-Dq7VW-bU.js +0 -1
- package/web/dist/assets/ArtifactPane-DKwbMTX9.js +0 -1
- package/web/dist/assets/ArtifactWorkbench-vrgIfncX.js +0 -6
- package/web/dist/assets/Library-D6-Go6Cu.js +0 -2
- package/web/dist/assets/ProfilesDestination-BOML2Dgf.js +0 -1
- package/web/dist/assets/TeamsPage-CNBTE8gB.js +0 -1
- package/web/dist/assets/Watchtower-Dor1Ubjy.js +0 -2
- package/web/dist/assets/hub-CrbItiwP.js +0 -13
- package/web/dist/assets/index-82Ce0IAf.css +0 -1
- package/web/dist/assets/index-D9SP3_0z.js +0 -22
- package/web/dist/assets/live-BNJgy0vg.js +0 -5
- package/web/dist/assets/pane-layout-EdR1kRrG.js +0 -1
- package/web/dist/assets/preload-helper-DqrJTq0t.js +0 -1
- package/web/dist/assets/source-Wbq35qGp.js +0 -1
- package/web/dist/assets/ui-j5FFk90B.js +0 -3
package/src/spoke/timeline.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import { DELIVER_ID_MAX, MESSAGE_KINDS, PROMPT_IMAGE_MIMES, redactEmbeddedMedia, summarizeToolInput, type MessageKind, type PromptImage, type TimelineMessageItem } from "../protocol"
|
|
1
|
+
import { DELIVER_ID_MAX, MEDIA_LIMITS, mediaPathRef, MESSAGE_KINDS, PROMPT_IMAGE_MIMES, redactEmbeddedMedia, summarizeToolInput, type MessageKind, type PromptImage, type TimelineMessageItem } from "../protocol"
|
|
2
2
|
import { PROMPT_SOURCE } from "./conversation"
|
|
3
|
+
import { base64Bytes, base64Dimensions, mediaMime, mimeOfPath, parseDataURI } from "./media-bytes"
|
|
4
|
+
import { markdownImageSources, sourcePath } from "./media-policy"
|
|
3
5
|
|
|
4
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Media of a row: a user message's attachment (its position in the message's files, for timelineFile), a tool
|
|
8
|
+
* row's file content item (its position in the tool's content) or an image path in an assistant text row (its
|
|
9
|
+
* position among them; `name` is the path as written). `ref` names it for timelineMedia; `bytes`, `width` and
|
|
10
|
+
* `height` when the spoke knows them without reading the file (embedded bytes and their header).
|
|
11
|
+
*/
|
|
5
12
|
export interface TimelineFile {
|
|
6
13
|
readonly index: number
|
|
14
|
+
readonly ref: string
|
|
7
15
|
readonly mime: string
|
|
8
16
|
readonly name?: string
|
|
17
|
+
readonly bytes?: number
|
|
18
|
+
readonly width?: number
|
|
19
|
+
readonly height?: number
|
|
9
20
|
}
|
|
10
21
|
|
|
11
22
|
export interface TimelineItem {
|
|
@@ -22,6 +33,8 @@ export interface TimelineItem {
|
|
|
22
33
|
readonly at?: number
|
|
23
34
|
readonly completed?: number
|
|
24
35
|
readonly files?: readonly TimelineFile[]
|
|
36
|
+
/** (S4 M2) Tool rows: their image file items; assistant text rows: local image paths in the text (≤ MEDIA_LIMITS.perRow). */
|
|
37
|
+
readonly media?: readonly TimelineFile[]
|
|
25
38
|
/**
|
|
26
39
|
* Ordering position: rows sort by it (never by `at`, whose clocks differ between parts). It follows the order
|
|
27
40
|
* of the session's messages and of the parts within a message, and depends only on the message's own creation
|
|
@@ -108,7 +121,8 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
|
|
|
108
121
|
const base = { key: `${message.id}:${partID}`, messageID: message.id, partID, sessionID, ...(at === undefined ? {} : { at }), pos: pos(index) }
|
|
109
122
|
if (part.type === "text" && typeof part.text === "string") {
|
|
110
123
|
const ordinal = texts++
|
|
111
|
-
|
|
124
|
+
const media = textMedia(part.text)
|
|
125
|
+
if (part.text) out.push({ ...base, kind: "assistant", text: part.text, ordinal, ...(media.length ? { media } : {}) })
|
|
112
126
|
}
|
|
113
127
|
if (part.type === "reasoning") out.push({ ...base, kind: "reasoning", text: String(part.text ?? ""), state: part.time?.completed === undefined ? "running" : "done", ...(typeof part.time?.created === "number" ? { at: part.time.created } : {}), ...(typeof part.time?.completed === "number" ? { completed: part.time.completed } : {}) })
|
|
114
128
|
if (part.type === "tool") {
|
|
@@ -117,6 +131,7 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
|
|
|
117
131
|
const spawn = SUBAGENT_TOOLS.has(name) ? subagentOf(part.state) : {}
|
|
118
132
|
// (CON-09b) The same short summary live activity shows (the command, the path, the hub verb).
|
|
119
133
|
const summary = summarizeToolInput(part.state?.input)
|
|
134
|
+
const media = toolMedia(part.state, index)
|
|
120
135
|
out.push({
|
|
121
136
|
...base,
|
|
122
137
|
kind: "tool",
|
|
@@ -125,6 +140,7 @@ export function timelineItems(sessionID: string, messages: readonly unknown[]):
|
|
|
125
140
|
state: status === "error" ? "failed" : status === "completed" ? "done" : "running",
|
|
126
141
|
...(typeof part.id === "string" && part.id ? { callID: part.id } : {}),
|
|
127
142
|
...spawn,
|
|
143
|
+
...(media.length ? { media } : {}),
|
|
128
144
|
...(typeof part.time?.created === "number" ? { at: part.time.created } : {}),
|
|
129
145
|
...(typeof part.time?.completed === "number" ? { completed: part.time.completed } : {}),
|
|
130
146
|
})
|
|
@@ -214,7 +230,7 @@ const ESCAPED = /^(\s*)\\(\\*(?:\[hub\]|-{3,}|↩))/i
|
|
|
214
230
|
* The body of a hub envelope (packages/sdk/src/envelope.ts renderEnvelope): the lines between its boundaries,
|
|
215
231
|
* unescaped. Text without boundaries (a reminder) shows its first line without the "[hub]" tag.
|
|
216
232
|
*/
|
|
217
|
-
function messageBody(text: string, id: string): string {
|
|
233
|
+
export function messageBody(text: string, id: string): string {
|
|
218
234
|
const lines = text.split("\n")
|
|
219
235
|
const boundary = `----- ${id} -----`
|
|
220
236
|
const start = lines.indexOf(boundary)
|
|
@@ -314,16 +330,54 @@ export class TimelineCache {
|
|
|
314
330
|
}
|
|
315
331
|
}
|
|
316
332
|
|
|
333
|
+
const NAME_MAX = 200
|
|
334
|
+
const nameOf = (value: unknown) => (typeof value === "string" && value ? { name: value.slice(0, NAME_MAX) } : {})
|
|
335
|
+
|
|
336
|
+
/** The size and header dimensions of embedded base64 bytes (a `data:` URI or bare base64), when there are any. */
|
|
337
|
+
function embedded(data: unknown, mime: string): { bytes?: number; width?: number; height?: number } {
|
|
338
|
+
if (typeof data !== "string" || !data) return {}
|
|
339
|
+
const base64 = parseDataURI(data)?.base64 ?? data
|
|
340
|
+
return { bytes: base64Bytes(base64), ...base64Dimensions(base64, mime) }
|
|
341
|
+
}
|
|
342
|
+
|
|
317
343
|
/** Attachment metadata (never the bytes) of a user message's files. */
|
|
318
344
|
function userFiles(raw: unknown): TimelineFile[] {
|
|
319
345
|
if (!Array.isArray(raw)) return []
|
|
320
346
|
return raw.flatMap((file, index) => {
|
|
321
347
|
const f = record(file)
|
|
322
348
|
if (typeof f.mime !== "string") return []
|
|
323
|
-
return [{ index, mime: f.mime, ...(
|
|
349
|
+
return [{ index, ref: `f${index}`, mime: f.mime, ...nameOf(f.name), ...embedded(f.data, f.mime) }]
|
|
324
350
|
})
|
|
325
351
|
}
|
|
326
352
|
|
|
353
|
+
/** A tool's image file content items (OpenCode ToolFileContent: `uri` a data: or media: URI), at most perRow. */
|
|
354
|
+
function toolMedia(rawState: unknown, part: number): TimelineFile[] {
|
|
355
|
+
const content = record(rawState).content
|
|
356
|
+
if (!Array.isArray(content)) return []
|
|
357
|
+
const out: TimelineFile[] = []
|
|
358
|
+
content.forEach((raw: unknown, index: number) => {
|
|
359
|
+
const item = record(raw)
|
|
360
|
+
const mime = mediaMime(typeof item.mime === "string" ? item.mime : undefined)
|
|
361
|
+
if (item.type !== "file" || !mime || out.length >= MEDIA_LIMITS.perRow) return
|
|
362
|
+
const bytes = typeof item.uri === "string" && item.uri.startsWith("data:") ? item.uri : item.data
|
|
363
|
+
out.push({ index, ref: `t${part}.${index}`, mime, ...nameOf(item.name), ...embedded(bytes, mime) })
|
|
364
|
+
})
|
|
365
|
+
return out
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Local image paths of an assistant text (markdown images with an image extension; never remote or `~`), at most perRow. */
|
|
369
|
+
function textMedia(text: string): TimelineFile[] {
|
|
370
|
+
if (!text.includes("![")) return []
|
|
371
|
+
const out: TimelineFile[] = []
|
|
372
|
+
for (const src of markdownImageSources(text)) {
|
|
373
|
+
const path = sourcePath(src, "/")
|
|
374
|
+
const mime = path ? mimeOfPath(path) : undefined
|
|
375
|
+
if (!mime || out.length >= MEDIA_LIMITS.perRow) continue
|
|
376
|
+
out.push({ index: out.length, ref: mediaPathRef(src), mime, ...nameOf(src) })
|
|
377
|
+
}
|
|
378
|
+
return out
|
|
379
|
+
}
|
|
380
|
+
|
|
327
381
|
/** Largest attachment served (base64 characters), so the answer fits in one frame. */
|
|
328
382
|
export const TIMELINE_FILE_MAX_BASE64 = 900_000
|
|
329
383
|
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// The voice front (P1, session mode; docs/plans/2026-11-06-voice-front-p1-contract.md, "Spoke"): a hidden session on
|
|
2
|
+
// the fast voice model that talks with the user and relays to the main session, whose agent and model stay untouched.
|
|
3
|
+
// This module holds the pure parts: the hidden agent (an allow-list of the two relay tools, like the Concierge's),
|
|
4
|
+
// its echo-first prompt, the relay's delivery choice, and VoiceFronts, which maps main ↔ front, keeps what the front
|
|
5
|
+
// knows about main (status, latest reply, pending asks, queued relays) for its per-turn context, and wakes the front
|
|
6
|
+
// when main's turn ends (merged over a short window; held while the front runs). Needs-you items (questions,
|
|
7
|
+
// permissions) never wake it: the hub announces them itself, so they are only listed in the front's context.
|
|
8
|
+
import { summarizeToolInput } from "../protocol"
|
|
9
|
+
import { redactCredentials } from "../../packages/protocol/src/credentials.ts"
|
|
10
|
+
import { SPEECH_RULES } from "./conversation"
|
|
11
|
+
import type { AgentEditorLike } from "./conversation"
|
|
12
|
+
|
|
13
|
+
export { SPEECH_RULES }
|
|
14
|
+
|
|
15
|
+
export const VOICE_FRONT_AGENT = "voice-front"
|
|
16
|
+
export const FRONT_SEND_TOOL = "voice_main_send"
|
|
17
|
+
export const FRONT_STOP_TOOL = "voice_main_stop"
|
|
18
|
+
/** The only tools the voice-front agent has. */
|
|
19
|
+
export const FRONT_TOOLS = [FRONT_SEND_TOOL, FRONT_STOP_TOOL] as const
|
|
20
|
+
/** Main's latest assistant text in the front's context and wakes, at most. */
|
|
21
|
+
export const MAIN_TEXT_MAX = 1500
|
|
22
|
+
/** Updates within this window become one wake. */
|
|
23
|
+
export const FRONT_MERGE_MS = 1500
|
|
24
|
+
|
|
25
|
+
export const VOICE_FRONT_NAME = "Voice front"
|
|
26
|
+
export const VOICE_FRONT_DESCRIPTION = "Hub voice front: talks with the user and relays to the session behind it. Hub-managed; not for direct use."
|
|
27
|
+
|
|
28
|
+
export const FRONT_SEND_DESCRIPTION =
|
|
29
|
+
'Relay the user\'s request to the main session (the real agent doing the work), as the user\'s own words made clear. delivery: "auto" (default: starts a turn when main is idle, steers its running turn when busy), "steer" (join main\'s running turn: a correction or related follow-up), or "queue" (an unrelated request while main is busy: it runs after the current turn).'
|
|
30
|
+
export const FRONT_STOP_DESCRIPTION = "Stop the main session's current work (its turn and every worker below it), e.g. when the user says stop, cancel or never mind."
|
|
31
|
+
|
|
32
|
+
export const VOICE_FRONT_PROMPT = `You are the voice front of an OpenCode session. The user talks to you hands-free; everything they send is a live speech-to-text transcript and everything you write is read aloud. Behind you is the main session: the user's real agent, with shell, edits and subagents. You never do the work yourself. You understand the user, say what you understood, and relay it to main with ${FRONT_SEND_TOOL}, or stop main with ${FRONT_STOP_TOOL}. Each turn you get a live note about main (its status, latest reply, open questions, workers and queued requests).
|
|
33
|
+
|
|
34
|
+
${SPEECH_RULES}
|
|
35
|
+
|
|
36
|
+
Echo first:
|
|
37
|
+
- Every reply starts with one plain sentence that says what you understood and what you are doing, for example "Got it, I'm asking the session to fix the failing login test." Only then call a tool. Never call a tool before that sentence.
|
|
38
|
+
- Never ask for confirmation before relaying: the echo is the confirmation. The user corrects you if you got it wrong.
|
|
39
|
+
- Keep replies to one or two short sentences of plain speech: no markdown, lists, code, file paths or URLs.
|
|
40
|
+
|
|
41
|
+
Relaying:
|
|
42
|
+
- Pass main the user's request as a clear instruction in their words, with what you inferred from the transcript filled in; don't add work they didn't ask for.
|
|
43
|
+
- While main is busy: a correction or a related follow-up goes in as steering (delivery "auto" or "steer"). An unrelated new request goes in with delivery "queue", and your echo says it is queued for after the current work.
|
|
44
|
+
- "no, wait", "actually", "scratch that": if it changes the request, relay the correction with delivery "steer"; if the user wants it dropped, call ${FRONT_STOP_TOOL}. Then a new echo confirms the change.
|
|
45
|
+
- Stop, cancel, never mind: call ${FRONT_STOP_TOOL} and confirm in a few words.
|
|
46
|
+
- Answer from the note about main when the user asks how it is going or what main said; don't relay a status question.
|
|
47
|
+
- A turn that is only a backchannel ("yeah", "mm-hm", "okay") needs no relay: reply with at most a few words or nothing.
|
|
48
|
+
|
|
49
|
+
Updates:
|
|
50
|
+
- Messages wrapped in <system-update> come from the hub, not the user: main's turn ended. Don't relay them to main.
|
|
51
|
+
- For a finished turn, say in one or two sentences what main did or found, from its final reply.
|
|
52
|
+
- Remember: the hub already announces questions, permissions and failures; don't repeat them. Help only if the user asks about them. When main's turn failed, don't say again that it failed: add at most one sentence explaining why, if that helps.`
|
|
53
|
+
|
|
54
|
+
export const VOICE_FRONT_RULES: ReadonlyArray<{ readonly action: string; readonly resource: string; readonly effect: "allow" | "deny" }> = [
|
|
55
|
+
{ action: "*", resource: "*", effect: "deny" },
|
|
56
|
+
...FRONT_TOOLS.map((action) => ({ action, resource: "*", effect: "allow" as const })),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
/** Adds (or refreshes) the hub's hidden "voice-front" agent; a "voice-front" agent the user defined is left alone. */
|
|
60
|
+
export function defineVoiceFrontAgent(editor: AgentEditorLike): void {
|
|
61
|
+
const existing = editor.get(VOICE_FRONT_AGENT)
|
|
62
|
+
if (existing && existing.description !== VOICE_FRONT_DESCRIPTION) return
|
|
63
|
+
editor.update(VOICE_FRONT_AGENT, (agent) => {
|
|
64
|
+
agent.name = VOICE_FRONT_NAME
|
|
65
|
+
agent.description = VOICE_FRONT_DESCRIPTION
|
|
66
|
+
agent.mode = "primary"
|
|
67
|
+
agent.hidden = true
|
|
68
|
+
agent.system = VOICE_FRONT_PROMPT
|
|
69
|
+
agent.permissions = VOICE_FRONT_RULES.map((r) => ({ ...r }))
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Every front's session title starts with this. */
|
|
74
|
+
export const FRONT_TITLE_PREFIX = "Voice: "
|
|
75
|
+
|
|
76
|
+
/** The front's session title. */
|
|
77
|
+
export function frontTitle(mainTitle: string | undefined): string {
|
|
78
|
+
const title = mainTitle?.trim()
|
|
79
|
+
return `${FRONT_TITLE_PREFIX}${title || "session"}`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* How a relay goes into main: "queue" (after the running turn) only when asked for while main is busy; otherwise
|
|
84
|
+
* steered (idle: it starts a turn; busy: it joins the running turn).
|
|
85
|
+
*/
|
|
86
|
+
export function relayDelivery(requested: unknown, busy: boolean): "steer" | "queue" {
|
|
87
|
+
return busy && requested === "queue" ? "queue" : "steer"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const record = (v: unknown): Record<string, any> => (typeof v === "object" && v !== null ? (v as Record<string, any>) : {})
|
|
91
|
+
const str = (v: unknown) => (typeof v === "string" && v ? v : undefined)
|
|
92
|
+
const flat = (text: string) => text.replace(/\s+/g, " ").trim()
|
|
93
|
+
export const trimText = (text: string, max = MAIN_TEXT_MAX) => {
|
|
94
|
+
const t = text.trim()
|
|
95
|
+
return t.length > max ? `${t.slice(0, max - 1)}…` : t
|
|
96
|
+
}
|
|
97
|
+
const oneLine = (text: string, max = 200) => trimText(flat(text), max)
|
|
98
|
+
|
|
99
|
+
type Update = { readonly text: string }
|
|
100
|
+
|
|
101
|
+
interface Main {
|
|
102
|
+
readonly front: string
|
|
103
|
+
/** The latest tool main ran in its current turn. */
|
|
104
|
+
activity?: string
|
|
105
|
+
tools: Map<string, string>
|
|
106
|
+
/** Main's latest assistant text. */
|
|
107
|
+
lastText?: string
|
|
108
|
+
/** Pending asks in main's tree: `per <id>` / `frm <id>` → description. */
|
|
109
|
+
asks: Map<string, string>
|
|
110
|
+
/** Relays admitted into main's inbox and not yet delivered: inboxID → text. */
|
|
111
|
+
queued: Map<string, string>
|
|
112
|
+
/** Updates waiting for the next wake. */
|
|
113
|
+
pending: Update[]
|
|
114
|
+
/** Done updates held while the front ran: folded into its next context, or woken after its turn. */
|
|
115
|
+
held: Update[]
|
|
116
|
+
timer?: ReturnType<typeof setTimeout>
|
|
117
|
+
/** The front stopped main: the interruption is not reported back to it. */
|
|
118
|
+
stopping: boolean
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface VoiceFrontsOptions {
|
|
122
|
+
readonly mergeMs?: number
|
|
123
|
+
/** Sends a wake to the front (a hub-sourced, steered prompt). */
|
|
124
|
+
readonly send: (front: string, text: string) => void
|
|
125
|
+
/** The front runs a turn right now. */
|
|
126
|
+
readonly frontBusy: (front: string) => boolean
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const RUN_END = new Set(["session.execution.succeeded", "session.execution.failed", "session.execution.interrupted"])
|
|
130
|
+
|
|
131
|
+
export class VoiceFronts {
|
|
132
|
+
private readonly mains = new Map<string, Main>()
|
|
133
|
+
private readonly fronts = new Map<string, string>()
|
|
134
|
+
private readonly mergeMs: number
|
|
135
|
+
|
|
136
|
+
constructor(private readonly options: VoiceFrontsOptions) {
|
|
137
|
+
this.mergeMs = options.mergeMs ?? FRONT_MERGE_MS
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
link(main: string, front: string, seed: { lastText?: string } = {}): void {
|
|
141
|
+
this.unlink(main)
|
|
142
|
+
this.mains.set(main, {
|
|
143
|
+
front,
|
|
144
|
+
tools: new Map(),
|
|
145
|
+
...(seed.lastText?.trim() ? { lastText: trimText(seed.lastText) } : {}),
|
|
146
|
+
asks: new Map(),
|
|
147
|
+
queued: new Map(),
|
|
148
|
+
pending: [],
|
|
149
|
+
held: [],
|
|
150
|
+
stopping: false,
|
|
151
|
+
})
|
|
152
|
+
this.fronts.set(front, main)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Forgets a main's front (its pending wakes are dropped); returns the front. */
|
|
156
|
+
unlink(main: string): string | undefined {
|
|
157
|
+
const entry = this.mains.get(main)
|
|
158
|
+
if (!entry) return undefined
|
|
159
|
+
clearTimeout(entry.timer)
|
|
160
|
+
this.mains.delete(main)
|
|
161
|
+
this.fronts.delete(entry.front)
|
|
162
|
+
return entry.front
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
frontOf(main: string): string | undefined {
|
|
166
|
+
return this.mains.get(main)?.front
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
mainOf(front: string): string | undefined {
|
|
170
|
+
return this.fronts.get(front)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
isFront(id: string): boolean {
|
|
174
|
+
return this.fronts.has(id)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
mainIDs(): string[] {
|
|
178
|
+
return [...this.mains.keys()]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** A relay admitted into main's inbox (shown as queued until OpenCode delivers it). */
|
|
182
|
+
queued(main: string, inboxID: string, text: string): void {
|
|
183
|
+
this.mains.get(main)?.queued.set(inboxID, oneLine(text))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The front is stopping main: the coming interruption is its own doing, not news. */
|
|
187
|
+
stopRequested(main: string): void {
|
|
188
|
+
const entry = this.mains.get(main)
|
|
189
|
+
if (entry) entry.stopping = true
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
observe(event: { type: string; data?: unknown }, rootOf: (id: string) => string): void {
|
|
193
|
+
const data = record(event.data)
|
|
194
|
+
const sessionID = str(data.sessionID) ?? str(record(data.form).sessionID)
|
|
195
|
+
if (!sessionID) return
|
|
196
|
+
const frontMain = this.fronts.get(sessionID)
|
|
197
|
+
if (frontMain) {
|
|
198
|
+
// The front's turn ended: what was held for it goes out now (merged).
|
|
199
|
+
const entry = this.mains.get(frontMain)!
|
|
200
|
+
if (RUN_END.has(event.type) && entry.held.length) {
|
|
201
|
+
entry.pending.unshift(...entry.held.splice(0))
|
|
202
|
+
this.schedule(frontMain, entry, Date.now() + this.mergeMs)
|
|
203
|
+
}
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
const main = this.mains.has(sessionID) ? sessionID : this.mains.has(rootOf(sessionID)) ? rootOf(sessionID) : undefined
|
|
207
|
+
if (!main) return
|
|
208
|
+
const entry = this.mains.get(main)!
|
|
209
|
+
switch (event.type) {
|
|
210
|
+
case "permission.asked": {
|
|
211
|
+
const id = str(data.id)
|
|
212
|
+
if (!id) return
|
|
213
|
+
const resources = Array.isArray(data.resources) ? data.resources.filter((r: unknown) => typeof r === "string").join(", ") : ""
|
|
214
|
+
const what = oneLine([str(data.action), resources].filter(Boolean).join(": ") || "a tool")
|
|
215
|
+
entry.asks.set(`per ${id}`, `${sessionID === main ? "" : "a worker's "}permission: ${what}`)
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
case "form.created": {
|
|
219
|
+
const form = record(data.form)
|
|
220
|
+
const id = str(form.id)
|
|
221
|
+
if (!id) return
|
|
222
|
+
const title = oneLine(str(form.title) ?? "a question")
|
|
223
|
+
entry.asks.set(`frm ${id}`, `${sessionID === main ? "" : "a worker's "}question: ${title}`)
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
case "permission.replied":
|
|
227
|
+
entry.asks.delete(`per ${str(data.requestID)}`)
|
|
228
|
+
return
|
|
229
|
+
case "form.replied":
|
|
230
|
+
case "form.cancelled":
|
|
231
|
+
entry.asks.delete(`frm ${str(data.id) ?? str(record(data.form).id)}`)
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
if (sessionID !== main) return
|
|
235
|
+
switch (event.type) {
|
|
236
|
+
case "session.execution.started":
|
|
237
|
+
entry.activity = undefined
|
|
238
|
+
entry.tools.clear()
|
|
239
|
+
return
|
|
240
|
+
case "session.tool.input.started": {
|
|
241
|
+
const id = str(data.id)
|
|
242
|
+
const name = str(data.name)
|
|
243
|
+
if (!id || !name) return
|
|
244
|
+
entry.tools.set(id, name)
|
|
245
|
+
if (entry.tools.size > 20) entry.tools.delete(entry.tools.keys().next().value!)
|
|
246
|
+
entry.activity = name
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
case "session.tool.called": {
|
|
250
|
+
const name = entry.tools.get(str(data.id) ?? "") ?? str(data.name) ?? "tool"
|
|
251
|
+
const summary = str(data.summary) ?? summarizeToolInput(data.input)
|
|
252
|
+
entry.activity = oneLine(summary ? `${name} · ${summary}` : name)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
case "session.text.ended":
|
|
256
|
+
if (str(data.text)?.trim()) entry.lastText = trimText(data.text)
|
|
257
|
+
return
|
|
258
|
+
case "session.inbox.delivered":
|
|
259
|
+
entry.queued.delete(str(data.inboxID) ?? "")
|
|
260
|
+
return
|
|
261
|
+
case "session.execution.succeeded":
|
|
262
|
+
case "session.execution.failed":
|
|
263
|
+
case "session.execution.interrupted": {
|
|
264
|
+
entry.activity = undefined
|
|
265
|
+
const reply = entry.lastText ? ` Its final reply: ${entry.lastText}` : ""
|
|
266
|
+
if (event.type === "session.execution.interrupted") {
|
|
267
|
+
if (entry.stopping) {
|
|
268
|
+
entry.stopping = false
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
return this.add(main, entry, { text: `Main's turn was interrupted.${reply}` })
|
|
272
|
+
}
|
|
273
|
+
entry.stopping = false
|
|
274
|
+
if (event.type === "session.execution.failed") {
|
|
275
|
+
// Scrubbed: the front may say why main failed, and never reads a credential aloud.
|
|
276
|
+
const message = oneLine(redactCredentials(str(record(data.error).message) ?? "unknown error").text)
|
|
277
|
+
return this.add(main, entry, { text: `Main's turn failed (already announced to the user; don't say it failed again): ${message}.${reply}` })
|
|
278
|
+
}
|
|
279
|
+
return this.add(main, entry, { text: `Main finished its turn.${reply}` })
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The front's per-turn note about main; undefined for a session that is no front. Updates held while the front
|
|
286
|
+
* ran are folded in (and not sent again).
|
|
287
|
+
*/
|
|
288
|
+
context(front: string, live: { title?: string; busy: boolean; workers?: string }): string | undefined {
|
|
289
|
+
const main = this.fronts.get(front)
|
|
290
|
+
if (!main) return undefined
|
|
291
|
+
const entry = this.mains.get(main)!
|
|
292
|
+
const held = entry.held.splice(0)
|
|
293
|
+
const lines = [
|
|
294
|
+
`Main session${live.title ? ` "${live.title}"` : ""} (the session you relay to), live now:`,
|
|
295
|
+
`Status: ${live.busy ? `busy${entry.activity ? `, last: ${entry.activity}` : ", working"}` : "idle"}`,
|
|
296
|
+
`Main's latest reply: ${entry.lastText ?? "(none yet)"}`,
|
|
297
|
+
]
|
|
298
|
+
if (entry.asks.size) lines.push("Waiting for the user (already announced by the hub; mention only if asked):", ...[...entry.asks.values()].map((a) => `- ${a}`))
|
|
299
|
+
if (live.workers) lines.push(`Main's workers:\n${live.workers}`)
|
|
300
|
+
if (entry.queued.size) lines.push("Your relays still waiting in main's queue:", ...[...entry.queued.values()].map((q) => `- ${q}`))
|
|
301
|
+
if (held.length) lines.push("Updates since your last reply (mention them briefly if relevant):", ...held.map((u) => `- ${u.text}`))
|
|
302
|
+
return lines.join("\n")
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
stop(): void {
|
|
306
|
+
for (const entry of this.mains.values()) clearTimeout(entry.timer)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Queues a turn-end update; updates within the merge window go out as one wake. */
|
|
310
|
+
private add(main: string, entry: Main, update: Update): void {
|
|
311
|
+
entry.pending.push(update)
|
|
312
|
+
this.schedule(main, entry, Date.now() + this.mergeMs)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private schedule(main: string, entry: Main, at: number): void {
|
|
316
|
+
if (entry.timer) return
|
|
317
|
+
entry.timer = setTimeout(() => {
|
|
318
|
+
entry.timer = undefined
|
|
319
|
+
if (this.mains.get(main) === entry) this.flush(entry)
|
|
320
|
+
}, Math.max(0, at - Date.now()))
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private flush(entry: Main): void {
|
|
324
|
+
clearTimeout(entry.timer)
|
|
325
|
+
entry.timer = undefined
|
|
326
|
+
if (!entry.pending.length) return
|
|
327
|
+
// While the front runs: held (folded into its next turn), never a separate interruption.
|
|
328
|
+
if (this.options.frontBusy(entry.front)) {
|
|
329
|
+
entry.held.push(...entry.pending.splice(0))
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
const updates = [...entry.held.splice(0), ...entry.pending.splice(0)]
|
|
333
|
+
this.options.send(entry.front, `<system-update>\n${updates.map((u) => u.text).join("\n")}\n</system-update>`)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{i as e,n as t,t as n}from"./7XWI72E6-BMuIspEy.js";import{a as r,d as i,f as a}from"./BGW36YJW-CJMhya8S.js";import{n as o,t as s}from"./G6GPDDNC-DEz8F449.js";import{t as c}from"./DKTLEL5E-DciMwFJ1.js";import{C as l,E as u,F as d,f,h as p,s as m,t as h}from"./lazy-f3CuUNJZ.js";function g(e){let t=/* @__PURE__ */ new Set;for(let n of e)for(let e of n.people)t.add(e);return[...t].sort()}var _=class{type=`journey`;title;accTitle;accDescr;sections=[];tasks=[];currentSection=``;setDiagramTitle(e){this.title=e}setAccTitle(e){this.accTitle=e.replace(/^\s+/g,``)}setAccDescription(e){this.accDescr=e.replace(/\n\s+/g,`
|
|
2
|
+
`)}addSection(e){this.currentSection=e,this.sections.push(e)}addTask(e,t){let n=t.substring(1).split(`:`),r=n.length===1?[]:n[1].split(`,`).map(e=>e.trim());this.tasks.push({task:e,score:Number(n[0]),people:r,section:this.currentSection})}getActors(){return g(this.tasks)}},v=[`$end`,`EOF`,`INVALID`,`NEWLINE`,`journey`,`title`,`acc_title`,`acc_title_value`,`acc_descr`,`acc_descr_value`,`acc_descr_multiline_value`,`section`,`taskName`,`taskData`,`:`];function y(e,t,n){let r=e.length,i=t;for(;i<r;i++){let t=e.charCodeAt(i);if(t===35||t===10||t===59||n&&t===58)break}return i}function b(r){let i=r.length,a=[],o=[],s=[],c={types:a,texts:o,starts:s},l=0,u=(e,t)=>{a.push(e),o.push(r.slice(l,t)),s.push(l),l=t};for(;l=t(r,l),!(l>=i);){let t=r.charCodeAt(l),a=l+1;if(t===10){for(;r.charCodeAt(a)===10;)a++;u(3,a)}else if(n(r,l,`journey`)&&!h(r.charCodeAt(l+7)))u(4,l+7);else{if(n(r,l,`title`)){for(a=l+5;r.charCodeAt(a)!==10&&d(r.charCodeAt(a));)a++;if(a===i||r.charCodeAt(a)===10){l=a;continue}let e=r.charCodeAt(l+5);if(e!==13&&d(e)&&(a=y(r,l+6,!1),a>l+6)){u(5,a);continue}}a=e(r,l,c,6),a===l?n(r,l,`section`)&&d(r.charCodeAt(l+7))&&(a=y(r,l+8,!0),a>l+8)?u(11,a):t===58?(a=y(r,l+1,!1),u(a>l+1?13:14,a>l+1?a:l+1)):t===59?u(2,l+1):u(12,y(r,l+1,!0)):l=a}}return(s.length===0||s[s.length-1]<i)&&u(1,i),u(0,i),c}var x=`'EOF', 'SPACE', 'NEWLINE', 'title', 'acc_title', 'acc_descr', 'acc_descr_multiline_value', 'section', 'taskName'`;function S(e,t){let{types:n,texts:r,starts:i}=b(e),a=(t,r)=>{let a=n[t]===0?`1`:v[n[t]];throw l(`journey`,e,i[t],`Expecting ${r}, got '${a}'`)};n[0]!==4&&a(0,`'journey'`);for(let e=1;;e++){let i=r[e];switch(n[e]){case 3:break;case 5:t.setDiagramTitle(i.substring(6));break;case 6:t.setAccTitle(r[++e].trim());break;case 8:t.setAccDescription(r[++e].trim());break;case 10:t.setAccDescription(i.trim());break;case 11:t.addSection(i.substring(8));break;case 12:n[++e]!==13&&a(e,`'taskData'`),t.addTask(i,r[e]);break;case 1:return;default:a(e,x)}}}var C=10,w=8,T=124,E=200,D=12,ee=6,O=24,k=26,A=11,j=5,te=13,ne=20,M=`<circle cx="-3.6" cy="-2.8" r="1.3"/><circle cx="3.6" cy="-2.8" r="1.3"/>`,N={happy:`M-5,2A6,6 0 0 0 5,2`,neutral:`M-4.5,4.5H4.5`,sad:`M-5,6.5A6,6 0 0 1 5,6.5`};function re(e,t,n){return`<g class="pele-face pele-face-${n}" transform="translate(${u(e)},${u(t)})"><circle r="${A}" fill="var(--_bg)" stroke="var(--_l)"/><g fill="var(--_l)">${M}</g><path d="${N[n]}" fill="none" stroke="var(--_l)" stroke-linecap="round"/></g>`}function P(e,t,n){return c(n,r=>F(e,t,n,r))}function F(e,t,n,c){let l=n.fontSize??16,d=Math.round(l*.875),h=Math.round(l*.75),_=n.measurer??m(n.fontFamily,n.fontFamilyMono),v=n.padding??8;if(c)return ie(e,n);let y=e.tasks,b=y.length,x=g(y).filter(e=>e!==``),S=/* @__PURE__ */ new Map;x.forEach((e,t)=>S.set(e,t));let M=T,N=y.map(e=>o(e.task,_,d,M)),P=0;for(let e of N)P=Math.max(P,e.width);P>M&&(M=Math.min(Math.ceil(P),E),N=y.map(e=>s(e.task,_,d,M)));let F=M+2*C,I=F+D,L=b>0?b*I-D:0,R=Math.round(d*1.5),ae=Math.max(L,240),z=[],B=0,V=0,H=0;for(let e of x){let t=s(e,_,d,ae-2*j-6),n=2*j+6+t.width;H>0&&H+n>ae&&(H=0,V+=4);let r=H>0?z[z.length-1].y:V,i=Math.max(t.height,R);z.push({name:e,label:t,x:H,y:r,h:i}),H+=n,B=Math.max(B,H),V=Math.max(V,r+i),H+=ne}let U=b>0?O+L:0,W=s(e.title,_,l,Math.max(U,320),1),oe=W.height>0?W.height+16:0,G=Math.max(U,W.width,b>0?O+B:0),K=v+(G-U)/2+O,q=v+oe,se=[],J=0;for(let e=0;e<b;){let t=e+1;for(;t<b&&y[t].section===y[e].section;)t++;let n;y[e].section!==``&&(n=s(y[e].section,_,d,(t-e)*I-D-2*C,1),J=Math.max(J,Math.max(n.height,R)+2*ee)),se.push({start:e,count:t-e,label:n}),e=t}let Y=R;for(let e of N)Y=Math.max(Y,e.height);let ce=x.length>0?2*j+6:0,X=Y+ce+2*w,Z=q+(J>0?J+ee:0),le=Z+X+A+16,ue=le+4*k,de=K+L,Q=``,$=q;if(b>0){let e=``,t=``;for(let n=5;n>=1;n--){let r=le+(5-n)*k;e+=`M${u(K-6)},${u(r)}H${u(de)}`,t+=`<text x="${u(K-12)}" y="${u(r+h*.35)}">${n}</text>`}Q+=`<path class="pele-grid" d="${e}" fill="none" stroke="var(--_a)"/><g class="pele-scale" font-size="${h}" text-anchor="end" fill="var(--_m)">${t}</g>`;let n=``,i=``,o=``,s=[];y.forEach((e,t)=>{let c=K+t*I,l=c+F/2,m=e.score,h=le+(5-(Number.isNaN(m)?3:Math.min(5,Math.max(1,m))))*k;n+=`${t===0?`M`:`L`}${u(l)},${u(h)}`,i+=`M${u(l)},${u(Z+X)}V${u(h-A)}`,o+=re(l,h,m>3?`happy`:m<3?`sad`:`neutral`);let g=``,_=e.people.filter(e=>e!==``),v=Math.min(te,(F-2*C-2*j)/Math.max(_.length-1,1));_.forEach((e,t)=>{g+=`<circle class="pele-actor" cx="${u(l+(t-(_.length-1)/2)*v)}" cy="${u(Z+X-w-j)}" r="${j}" fill="${r(S.get(e)??0)}" stroke="var(--_s)"><title>${f(e)}</title></circle>`}),s.push(`<g class="pele-node pele-task" data-id="${f(e.task.trim())}"><rect x="${u(c)}" y="${u(Z)}" width="${u(F)}" height="${u(X)}" fill="var(--_s)" stroke="var(--_b)"${a}/>`+p(N[t],l,Z+w+Y/2,` font-size="${d}"`)+g+`</g>`)}),Q+=`<path class="pele-stems" d="${i}" fill="none" stroke="var(--_b)" stroke-dasharray="2 3"/>`+(b>1?`<path class="pele-edge" d="${n}" fill="none" stroke="var(--_l)" stroke-linejoin="round"/>`:``)+o;for(let e of se){let t=s.slice(e.start,e.start+e.count).join(``);if(!e.label){Q+=t;continue}let n=K+e.start*I,r=e.count*I-D;Q+=`<g class="pele-cluster pele-section" data-id="${f(y[e.start].section.trim())}"><rect x="${u(n)}" y="${u(q)}" width="${u(r)}" height="${u(J)}" fill="var(--_a)"${a}/>`+p(e.label,n+r/2,q+J/2,` font-size="${d}" font-weight="var(--_hw)"`)+t+`</g>`}if($=ue+A,z.length>0){let e=K+(L-B)/2,t=$+16,n=``;for(let i of z){let a=t+i.y+i.h/2;n+=`<g class="pele-legend-item" data-id="${f(i.name)}"><circle cx="${u(e+i.x+j)}" cy="${u(a)}" r="${j}" fill="${r(S.get(i.name)??0)}"/>`+p(i.label,e+i.x+2*j+6+i.label.width/2,a,``)+`</g>`}Q+=`<g class="pele-legend" font-size="${d}">${n}</g>`,$=t+V}}let fe=Math.ceil(G+2*v),pe=Math.ceil(Math.max($,v)+v),me=p(W,v+G/2,v+W.height/2,` class="pele-title" font-weight="var(--_tw)"`)+Q;return{svg:i(`journey`,fe,pe,l,n,e,me),width:fe,height:pe,links:[]}}function ie(e,t){let n=t.fontSize??16,o=Math.round(n*.875),c=t.measurer??m(t.fontFamily,t.fontFamilyMono),l=t.padding??8,d=Math.max(140,(t.maxWidth??320)-2*l),f=s(e.title,c,n,d,1),h=g(e.tasks).filter(e=>e!==``),_=new Map(h.map((e,t)=>[e,t])),v=l+(f.height?f.height+16:0),y,b=``,x=f.width;e.tasks.forEach((t,i)=>{if(t.section!==y){y=t.section;let e=s(y,c,n,d-16,1);e.height&&(b+=p(e,l+8+e.width/2,v+e.height/2,` class="pele-cluster-label" font-weight="var(--_hw)"`),x=Math.max(x,e.width+16),v+=e.height+8)}let f=s(t.task,c,o,d-72),m=t.people.filter(e=>e!==``).map(e=>({name:e,label:s(e,c,o,d-44)})),h=Math.max(f.height,42),g=2*w+h;for(let e of m)g+=Math.max(16,e.label.height)+4;let S=Math.max(d,f.width+72);b+=`<g class="pele-node pele-journey-step" data-id="${i}"><rect x="${u(l)}" y="${u(v)}" width="${u(S)}" height="${u(g)}" fill="var(--_s)" stroke="var(--_b)"${a}/>`,b+=p(f,l+C+f.width/2,v+w+f.height/2,` class="pele-label"`);let T=l+S-28;b+=re(T,v+w+A,t.score>3?`happy`:t.score<3?`sad`:`neutral`),Number.isNaN(t.score)||(b+=`<text class="pele-journey-score" x="${u(T)}" y="${u(v+w+2*A+o)}" font-size="${o}" text-anchor="middle">${u(Math.min(5,Math.max(1,t.score)))}</text>`);let E=v+w+h+4;for(let e of m){let t=Math.max(16,e.label.height);b+=`<circle class="pele-actor" cx="${u(l+16)}" cy="${u(E+t/2)}" r="${j}" fill="${r(_.get(e.name))}"/>`+p(e.label,l+28+e.label.width/2,E+t/2,` class="pele-journey-actor" font-size="${o}"`),E+=t+4}b+=`</g>`,v+=g,i+1<e.tasks.length&&(b+=`<path class="pele-edge" d="M${u(l+S/2)},${u(v)}v16" fill="none" stroke="var(--_l)"/>`),v+=16,x=Math.max(x,S)});let S=Math.ceil(x+2*l),T=Math.ceil(Math.max(l+f.height,v-(e.tasks.length?16:0))+l),E=p(f,S/2,l+f.height/2,` class="pele-title" font-weight="var(--_tw)"`)+b;return{svg:i(`journey`,S,T,n,t,e,E),width:S,height:T,links:[]}}var I={type:`journey`,parse(e,t,n){let r=new _;return r.title=n,S(e,r),r},render:P};export{I as journey};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as e,d as t,i as n,n as r,r as i,s as a,t as o}from"./BGW36YJW-CJMhya8S.js";import{i as s,r as c}from"./DKTLEL5E-DciMwFJ1.js";import{t as l}from"./5THPTBWS-Ce_hS451.js";import{_ as u,g as d,h as f,n as p,r as m,s as h,t as g,u as _}from"./EXE5EH6O-DR1IHCTr.js";import{D as v,E as y,d as b,f as x,h as S,i as C,r as w,s as T}from"./lazy-f3CuUNJZ.js";function E(e){let t=[],n=[];for(let r of e){let e=r.type===`Leaf`,i={name:r.name,children:e?void 0:[]};for(i.classSelector=r.classSelector,r.cssCompiledStyles&&(i.cssCompiledStyles=r.cssCompiledStyles),e&&r.value!==void 0&&(i.value=r.value);n.length>0&&n[n.length-1].level>=r.level;)n.pop();n.length===0?t.push(i):n[n.length-1].node.children.push(i),e||n.push({node:i,level:r.level})}return t}function D(e,t){let n=/* @__PURE__ */ new Map;for(let t of e.TreemapRows){if(t.$type!==`ClassDefStatement`)continue;let e=n.get(t.className);e||n.set(t.className,e=[]);let r=(t.styleText??``).replace(/\\,/g,`§§§`).replace(/,/g,`;`).replace(/§§§/g,`,`);for(let t of r.split(`;`))e.push(t)}let r=[];for(let t of e.TreemapRows){if(t.$type!==`TreemapRow`)continue;let e=t.item,i=e.classSelector?n.get(e.classSelector):void 0;r.push({level:t.indent??0,name:e.name,type:e.$type,value:e.$type===`Leaf`?e.value:void 0,classSelector:e.classSelector,cssCompiledStyles:i&&i.length>0?i:void 0})}return{type:`treemap`,title:e.title||t,accTitle:e.accTitle?e.accTitle.replace(/^\s+/g,``):void 0,accDescr:e.accDescr?e.accDescr.replace(/\n\s+/g,`
|
|
2
|
+
`):void 0,roots:E(r),classes:n}}var O=[h,g,f(String.raw`\w`),{name:`TREEMAP_KEYWORD`,pattern:/treemap-beta|treemap/y},{name:`CLASS_DEF`,pattern:/classDef\s+([a-zA-Z_][a-zA-Z0-9_]+)(?:\s+([^;\r\n]*))?(?:;)?/y},{name:`STYLE_SEPARATOR`,pattern:/:::/y},{name:`SEPARATOR`,pattern:/:/y},{name:`COMMA`,pattern:/,/y},{name:`INDENTATION`,pattern:/[ \t]{1,}/y},{name:`WS`,pattern:/[ \t]+/y,hidden:!0},{name:`ML_COMMENT`,pattern:/\%\%[^\n]*/y,hidden:!0},{name:`NL`,pattern:/\r?\n/y,hidden:!0},{name:`ID2`,pattern:/[a-zA-Z_][a-zA-Z0-9_]*/y},{name:`NUMBER2`,pattern:/[0-9_\.\,]+/y},{name:`STRING2`,pattern:/"[^"]*"|'[^']*'/y}],k=/classDef\s+([A-Z_a-z]\w+)(?:\s+([^\n\r;]*))?;?/;function A(e){let t=new d(m(e,O,`treemap`),O,`treemap`),n=t.kinds,r={$type:`Treemap`,TreemapRows:[]},i=()=>t.accept(5)?t.expect(12):void 0;for(t.expect(3);t.kind!==-1;){let e=t.kind;if(e===0){r.accDescr=_(t.take());continue}if(e===1){r.accTitle=p(t.take());continue}if(e===2){r.title=u(t.take());continue}e!==8&&e!==14&&e!==4&&t.fail(`a node, a class definition, or a title`);let a=e===8?t.take().length:void 0;if(t.kind===4){let e=k.exec(t.take()),n={$type:`ClassDefStatement`,className:e[1]};e[2]&&(n.styleText=e[2]),a!==void 0&&(n.indent=a),r.TreemapRows.push(n);continue}let o=t.expect(14).slice(1,-1),s=n[t.i]===8?t.i+1:t.i,c;n[s]===6||n[s]===7?(t.i=s+1,t.accept(8),c={$type:`Leaf`,name:o,value:parseFloat(t.expect(13).replace(/,/g,``))}):c={$type:`Section`,name:o};let l=i();l!==void 0&&(c.classSelector=l);let d={$type:`TreemapRow`,item:c};a!==void 0&&(d.indent=a),r.TreemapRows.push(d)}return r}var j=/^(?:.?[<>=^])?([-+( ])?(\$)?0?\d*(,)?(?:\.(\d+))?(~)?([a-zA-Z%])?$/;function M(e){let t=``;for(let n=e.length;n>0;n-=3)t=e.slice(Math.max(0,n-3),n)+(t?`,`+t:``);return t}function N(e){let t=j.exec(e===`$0,0`?`$,`:e)??j.exec(`,`),n=t[1]===`+`,r=t[3]!==void 0,i=t[4]===void 0?void 0:Math.min(Number(t[4]),20),a=t[6]??``;return e=>{let o=Math.abs(e),s,c=``;if(a===`f`?s=o.toFixed(i??6):a===`%`?(s=(o*100).toFixed(i??6),c=`%`):s=a===`d`?Math.round(o).toFixed(0):a===`e`?o.toExponential(i??6):String(Number(o.toPrecision(Math.max(1,i??12)))),t[5]&&s.includes(`.`)&&!s.includes(`e`)&&(s=s.replace(/\.?0+$/,``)),r&&!s.includes(`e`)){let e=s.indexOf(`.`);s=e===-1?M(s):M(s.slice(0,e))+s.slice(e)}return(e<0?`-`:n?`+`:``)+(t[2]??``)+s+c}}function P(e,t,n){return{node:e,parent:t,series:n,value:e.children===void 0&&Number.isFinite(e.value)&&e.value>0?e.value:0,children:[],headed:!1,header:0,x:0,y:0,w:0,h:0}}var F=(e,t,n)=>e>=t?Math.min(e,n):t;function I(e,t,n,r,i,a,o){let s=t,c=0;for(;c<e.length;){let t=i*a/s,l=Math.min(i,a),u=e[c].value*t,d=u,f=Math.max(l*l*d/(u*u),u*u/(l*l*d)),p=c+1;for(;p<e.length;){let n=e[p].value*t,r=u+n,i=Math.max(l*l*d/(r*r),r*r/(l*l*n));if(i>f)break;f=i,u=r,p++}let m=u/l,h=0;for(let u=c;u<p;u++){let c=e[u],d=u===p-1?l-h:c.value*t/m,f=i>=a?n:n+h,g=i>=a?r+h:r,_=i>=a?m:d,v=i>=a?d:m,y=Math.round(f+_-o/2)>Math.round(f+o/2)?o/2:0,b=Math.round(g+v-o/2)>Math.round(g+o/2)?o/2:0;c.x=Math.round(f+y),c.y=Math.round(g+b),c.w=Math.round(f+_-y)-c.x,c.h=Math.round(g+v-b)-c.y,h+=d,s-=c.value}i>=a?(n+=m,i-=m):(r+=m,a-=m),c=p}}function L(e,t,n,r){let i=e.map((e,t)=>P(e,void 0,t)),a=i.slice();for(let e=0;e<a.length;e++){let t=a[e];for(let e of t.node.children??[]){let n=P(e,t,t.series);t.children.push(n),a.push(n)}}for(let e=a.length-1;e>=0;e--){let t=a[e];t.parent&&(t.parent.value+=t.value)}let{gap:o,header:s,short:c,side:l}=r,u=[],d=(e,t,n,r,i)=>{let a=e.filter(e=>e.value>0).sort((e,t)=>t.value-e.value),s=0;for(let e of a)s+=e.value;if(a.length!==0&&Number.isFinite(s)){r=Math.max(0,r),i=Math.max(0,i),r>0&&i>0&&I(a,s,t-o/2,n-o/2,r+o,i+o,o);for(let e of a){let a=F(e.x,t,t+r),o=F(e.y,n,n+i);e.w=F(e.x+e.w,a,t+r)-a,e.h=F(e.y+e.h,o,n+i)-o,e.x=a,e.y=o,u.push(e)}}};d(i,0,0,t,n);for(let e=0;e<u.length;e++){let t=u[e];if(t.children.length===0)continue;let n=t.h-s-l>=12?s:c;t.headed=t.w>Math.max(24,2*l)&&t.h-n-l>=8,t.header=t.headed?n:0,t.headed?d(t.children,t.x+l,t.y+n,t.w-2*l,t.h-n-l):d(t.children,t.x,t.y,t.w,t.h)}return u}var R=28,z=18,B=6,V=1.25,H=e=>b(v(e).trim()).trim();function U(e){let t=0,n=e.slice();for(;n.length>0;){let e=n.pop();e.children===void 0?Number.isFinite(e.value)&&e.value>0&&t++:n.push(...e.children)}return t}function W(e,t){let n=Math.min(t.w,t.h)/4;return n<4?a(e).replace(`rx:var(--_r)`,`rx:min(var(--_r),${y(n)}px)`):a(e)}function G(e,t,n,r,i){let a=e.treemap?.[t];return typeof a==`number`&&a>=r?Math.min(a,i):n}function K(a,u,d){let f=d.fontSize??16,p=Math.round(f*.875),m=Math.round(f*.75),h=Math.round(f*.5625),g=d.measurer??T(d.fontFamily,d.fontFamilyMono),_=u.treemap??{},E=d.padding??G(u,`diagramPadding`,8,0,2e3),D=Math.round(s(d,G(u,`nodeWidth`,64,1,400)*10,200,2*E)),O=Math.round(G(u,`nodeHeight`,40,1,400)*10),k=_.showValues!==!1,A=N(typeof _.valueFormat==`string`?_.valueFormat:`,`),j=Math.min(1,Math.max(0,Math.log10(D*O/Math.max(1,U(a.roots))/800))),M=L(a.roots,D,O,{gap:G(u,`padding`,Math.round(2+2*j),0,200),header:R,short:z,side:Math.round(3+3*j)}),P=n(a.title,!1,g,f,c(d),1),F=P.height>0?P.height+8:0,I=E+Math.max(0,(P.width-D)/2),K=E+F,q=``,J=``;for(let t of M){let n=t.node,a=i(n.cssCompiledStyles??[]),s=e(t.series),c=I+t.x,u=K+t.y,d=v(n.name).trim(),_=k?A(t.value):``,S=`data-id="${x(n.name)}">`,T=H(n.name),E=T===``?[]:[T];for(let e=t.parent;e!==void 0&&!e.headed;e=e.parent){let t=H(e.node.name);t!==``&&E.unshift(t)}let D=E.join(` › `),O=e=>D===``||e===d&&D===T?``:`<title>${D}</title>`,j=`<rect x="${y(c)}" y="${y(u)}" width="${y(t.w)}" height="${y(t.h)}" fill="${s}"`,M=a.fontSize,N=+!!a.bold|(a.italic?2:0),P=o(a.shape)?w(a):C(t.series);if(n.children!==void 0){let e=``,i;if(t.headed){let n=t.header<R,r=M??(n?m:p),o=M??m,s=t.w-2*B-4,f=_?g.width(_,o,N):0,h=!n&&f>0&&s-f-12>=3*r,v=r*V<t.header?l(d,h?s-f-12:s,r,r,g,N|1):void 0,x=u+t.header/2+1;i=v?.text,v&&(e+=`<text x="${y(c+B+2)}" y="${y(x+r*.35)}" font-weight="var(--_hw)"${n?` font-size="${r}"`:``}${P}${a.text}>${b(v.text)}</text>`),h&&o*V<=R&&(e+=`<text class="pele-value" x="${y(c+t.w-B-2)}" y="${y(x+o*.35)}" font-size="${m}" text-anchor="end"${P||` fill="var(--_m)"`}${a.text}>${b(_)}</text>`)}let o=/[";]fill:/.test(a.shape)?``:` fill-opacity="0.12"`,f=t.headed?`${j}${W(`${o} stroke="${s}"${a.shape}`,t)}/>`:`<rect x="${y(c)}" y="${y(u)}" width="${y(t.w)}" height="${y(t.h)}" fill="none"/>`;q+=`<g class="pele-node pele-section${r(n.classSelector??``)}" ${S}${O(i)}${f}${e}</g>`;continue}let F=``,L=t.w-8,z=M??h,U=M===void 0?Math.min(f,Math.floor((t.h-4)/V)):M*V<=t.h-4?M:0,G=U>=z?l(d,L,U,z,g,N):void 0;if(G){let e=y(c+t.w/2),n=u+t.h/2,r=M??Math.max(h,Math.round(G.size*.8)),i=(G.size+r)*V;if(_&&i<=t.h-4&&g.width(_,r,N)<=L){let t=n-i/2+G.size*V/2,o=n+i/2-r*V/2;F=`<text x="${e}" y="${y(t+G.size*.35)}" font-size="${y(G.size)}"${P}${a.text}>${b(G.text)}</text><text class="pele-value" x="${e}" y="${y(o+r*.35)}" font-size="${y(r)}"${P}${a.text}>${b(_)}</text>`}else F=`<text x="${e}" y="${y(n+G.size*.35)}" font-size="${y(G.size)}"${P}${a.text}>${b(G.text)}</text>`}J+=`<g class="pele-node pele-leaf${r(n.classSelector??``)}" ${S}${O(G?.text)}${j}${W(a.shape,t)}/>${F}</g>`}let Y=Math.ceil(Math.max(D,P.width)+2*E),X=Math.ceil(O+F+2*E),Z=S(P,Y/2,E+P.height/2,` class="pele-title" font-weight="var(--_tw)"`)+(q?`<g class="pele-sections" font-size="${p}">${q}</g>`:``)+(J?`<g class="pele-leaves" text-anchor="middle" fill="var(--_bg)">${J}</g>`:``);return{svg:t(`treemap`,Y,X,f,d,a,Z),width:Y,height:X,links:[]}}var q={type:`treemap`,parse:(e,t,n)=>D(A(e),n),render:K};export{q as treemap};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{E as e,h as t}from"./lazy-f3CuUNJZ.js";function n(e,t){for(let n=0;n<t.length;n+=3){let r=Math.round(t[n+1]),i=e.get(r);i?i.push(t[n]):e.set(r,[t[n]])}}function r(e,t,n,r){if(t===0)return e.x;let i=e.y-e.h/2,a=e.x-e.w/2,o=t/2,s=[a+r-6+o,e.x,a+e.w-r+6-o],c=n.get(Math.round(i));if(c===void 0)return s[0];let l=s[0],u=-1;for(let t of s){let n=1/0;for(let r of c)r>a&&r<a+e.w&&(n=Math.min(n,Math.abs(r-t)-o));if(n>=6)return t;n>u+.5&&(u=n,l=t)}return l}function i(e,t,n,r){let i=t>0?r.get(Math.round(e.y-e.h/2)):void 0;if(i===void 0)return!1;let a=e.x-e.w/2;for(let r of i)if(r>a&&r<a+e.w&&Math.abs(r-n)<t/2+4)return!0;return!1}function a(n,r,i,a,o,s,c,l){let u=1;for(let e=s[c].parent;e>=0&&u<8;e=s[e].parent)u++;let d=`color-mix(in srgb,var(--_a) ${e(100*(1-.5**u))}%,var(--_bg))`;return`<g class="pele-cluster-title" data-id="${o}">`+t(n,r,i,` fill="none" stroke="${d}" stroke-width="5" stroke-linejoin="round"`,l)+t(n,r,i,a,l)+`</g>`}export{i,r as n,n as r,a as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t,n,r,i,a=0){if(e===``||t<=0)return;let o=i.width(e,n,a);if(o<=t)return{text:e,size:n,width:o};let s=Math.max(r,Math.floor(n*t/o));for(;s>=r;){if(o=i.width(e,s,a),o<=t)return{text:e,size:s,width:o};s--}let c=Array.from(e),l=0,u=c.length-1;for(;l<u;){let e=l+u+1>>1;i.width(c.slice(0,e).join(``).trimEnd()+`…`,r,a)<=t?l=e:u=e-1}if(l===0)return;let d=c.slice(0,l).join(``).trimEnd()+`…`;return o=i.width(d,r,a),o<=t?{text:d,size:r,width:o}:void 0}export{e as t};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{d as e,i as t,n,o as r,p as i,r as a,s as o,u as s}from"./BGW36YJW-CJMhya8S.js";import{a as c,t as l}from"./DKTLEL5E-DciMwFJ1.js";import{d as u,l as d}from"./EFYWJBJK-CVywe_D5.js";import{a as f,i as p,r as m}from"./VBUCM3YD-JHGjuJC_.js";import{t as h}from"./NT5JY5WP-1lJM-aun.js";import{C as g,E as _,F as v,P as y,d as b,f as x,h as S,m as C,o as w,r as ee,s as te,t as T,u as E,w as D}from"./lazy-f3CuUNJZ.js";var O={REQUIREMENT:`Requirement`,FUNCTIONAL_REQUIREMENT:`Functional Requirement`,INTERFACE_REQUIREMENT:`Interface Requirement`,PERFORMANCE_REQUIREMENT:`Performance Requirement`,PHYSICAL_REQUIREMENT:`Physical Requirement`,DESIGN_CONSTRAINT:`Design Constraint`},k={LOW_RISK:`Low`,MED_RISK:`Medium`,HIGH_RISK:`High`},A={VERIFY_ANALYSIS:`Analysis`,VERIFY_DEMONSTRATION:`Demonstration`,VERIFY_INSPECTION:`Inspection`,VERIFY_TEST:`Test`},j={CONTAINS:`contains`,COPIES:`copies`,DERIVES:`derives`,SATISFIES:`satisfies`,VERIFIES:`verifies`,REFINES:`refines`,TRACES:`traces`},M=1e6,N=class{type=`requirement`;direction=`TB`;requirements=/* @__PURE__ */ new Map;elements=/* @__PURE__ */ new Map;relations=[];classes=/* @__PURE__ */ new Map;title;accTitle;accDescr;newId=``;newText=``;newRisk=``;newVerifyMethod=``;newElementType=``;newDocRef=``;styleCopies=0;copy(e,t){if(this.styleCopies+=t.length,this.styleCopies>M)throw new E(`Requirement diagram applies too many style declarations.`,`limit`,{type:`requirement`});for(let n of t)e.push(n)}setDirection(e){this.direction=e}addRequirement(e,t){let n=this.requirements.get(e);return n===void 0&&(n={name:e,type:t,requirementId:this.newId,text:this.newText,risk:this.newRisk,verifyMethod:this.newVerifyMethod,cssStyles:[],classes:[`default`]},this.requirements.set(e,n)),this.newId=this.newText=this.newRisk=this.newVerifyMethod=``,n}setNewReqId(e){this.newId=e}setNewReqText(e){this.newText=e}setNewReqRisk(e){this.newRisk=e}setNewReqVerifyMethod(e){this.newVerifyMethod=e}addElement(e){let t=this.elements.get(e);return t===void 0&&(t={name:e,type:this.newElementType,docRef:this.newDocRef,cssStyles:[],classes:[`default`]},this.elements.set(e,t)),this.newElementType=this.newDocRef=``,t}setNewElementType(e){this.newElementType=e}setNewElementDocRef(e){this.newDocRef=e}addRelationship(e,t,n){this.relations.push({type:e,src:t,dst:n})}node(e){return this.requirements.get(e)??this.elements.get(e)}setCssStyle(e,t){for(let n of e){let e=this.node(n);if(!t||!e)return;for(let n of t)this.copy(e.cssStyles,n.includes(`,`)?n.split(`,`):[n])}}setClass(e,t){for(let n of e){let e=this.node(n);if(e)for(let n of t){e.classes.push(n);let t=this.classes.get(n)?.styles;t&&this.copy(e.cssStyles,t)}}}defineClass(e,t){for(let n of e){let e=this.classes.get(n);e===void 0&&(e={id:n,styles:[],textStyles:[]},this.classes.set(n,e));for(let n of t)n.includes(`color`)&&e.textStyles.push(n.replace(`fill`,`bgFill`)),e.styles.push(n);let r=t.flatMap(e=>e.split(`,`));for(let e of this.requirements.values())e.classes.includes(n)&&this.copy(e.cssStyles,r);for(let e of this.elements.values())e.classes.includes(n)&&this.copy(e.cssStyles,r)}}setAccTitle(e){this.accTitle=e.replace(/^\s+/g,``)}setAccDescription(e){this.accDescr=e.replace(/\n\s+/g,`
|
|
2
|
+
`)}},P=/* @__PURE__ */ `$end.INVALID.title.acc_title.acc_title_value.acc_descr.acc_descr_value.acc_descr_multiline_value.direction_tb.direction_bt.direction_rl.direction_lr.NEWLINE.EOF.RD.STRUCT_START.STRUCT_STOP.STYLE_SEPARATOR.COLONSEP.ID.TEXT.RISK.VERIFYMTHD.REQUIREMENT.FUNCTIONAL_REQUIREMENT.INTERFACE_REQUIREMENT.PERFORMANCE_REQUIREMENT.PHYSICAL_REQUIREMENT.DESIGN_CONSTRAINT.LOW_RISK.MED_RISK.HIGH_RISK.VERIFY_ANALYSIS.VERIFY_DEMONSTRATION.VERIFY_INSPECTION.VERIFY_TEST.ELEMENT.CONTAINS.COPIES.DERIVES.SATISFIES.VERIFIES.REFINES.TRACES.TYPE.DOCREF.STYLE.CLASSDEF.CLASS.ALPHA.COLON.SEMICOLON.PERCENT.MINUS.BRKT.END_ARROW_L.END_ARROW_R.LINE.qString.unqString.COMMA`.split(`.`),F=/* @__PURE__ */ new Map([[`requirementdiagram`,14],[`id`,19],[`text`,20],[`risk`,21],[`verifymethod`,22],[`requirement`,23],[`functionalrequirement`,24],[`interfacerequirement`,25],[`performancerequirement`,26],[`physicalrequirement`,27],[`designconstraint`,28],[`low`,29],[`medium`,30],[`high`,31],[`analysis`,32],[`demonstration`,33],[`inspection`,34],[`test`,35],[`element`,36],[`contains`,37],[`copies`,38],[`derives`,39],[`satisfies`,40],[`verifies`,41],[`refines`,42],[`traces`,43],[`type`,44],[`docref`,45],[`style`,46],[`classdef`,47],[`class`,48]]),I=/accTitle\s*:\s*/iy,L=/accDescr\s*:\s*/iy,R=/accDescr\s*\{\s*/iy,z=/direction\s+(?:(TB)|(BT)|(RL)|LR)/gi,B=/[\n\r\u2028\u2029]/g,V=22,H=/* @__PURE__ */ new Uint8Array(128);for(let e of`:,\r
|
|
3
|
+
{<>-=`)H[e.charCodeAt(0)]=1;function ne(e){let t=e.length,n=[],r=[],i=[],a=[],o=0,s=0,c=[[],[],[],[]],l=[[],[],[],[]],u=[0,0,0,0],d=!1,f=-1;z.lastIndex=0;for(let t=z.exec(e);t!==null;t=z.exec(e)){let e=t[1]===void 0?t[2]===void 0?t[3]===void 0?3:2:1:0;c[e].push(t.index),l[e].push(z.lastIndex),d=!0}let p=(t,a,o)=>{n.push(t),r.push(e.slice(a,o)),i.push(a),s=o},m=e=>{a.push(o),o=e},h=()=>{a.length>0&&(o=a.pop())},g=(t,n)=>(t.lastIndex=n,t.test(e)?t.lastIndex:-1),_=n=>{let r=e.indexOf(`
|
|
4
|
+
`,n);return r===-1?t:r},b=()=>{if(f<s){B.lastIndex=s,f=B.test(e)?B.lastIndex-1:t;for(let e=0;e<4;e++){let t=c[e],n=u[e];for(;n<t.length&&t[n]<=f;)n++;u[e]=n}}for(let e=0;e<4;e++){let t=u[e]-1;if(t>=0&&c[e][t]>=s)return p(8+e,s,_(l[e][t])),!0}return!1},x=a=>{let o=a|32;if(o===116&&e.slice(s+1,s+5).toLowerCase()===`itle`&&v(e.charCodeAt(s+5))){let n=s+6;for(;n<t;){let t=e.charCodeAt(n);if(t===35||t===10||t===59)break;n++}if(n>s+6)return p(2,s,n),!0}if(o===97&&(e.charCodeAt(s+1)|32)==99&&(e.charCodeAt(s+2)|32)==99){let e;if((e=g(I,s))>=0)return m(3),p(3,s,e),!0;if((e=g(L,s))>=0)return m(4),p(5,s,e),!0;if((e=g(R,s))>=0)return m(5),s=e,!0}if(d&&!y(a)&&b())return!0;if(a===10||a===13&&e.charCodeAt(s+1)===10){let n=s;for(;n<t;){let t=e.charCodeAt(n);if(t===10)n++;else if(t===13&&e.charCodeAt(n+1)===10)n+=2;else break}return p(12,s,n),!0}if(v(a)){for(s++;s<t&&v(e.charCodeAt(s));)s++;return!0}if(a===35||a===37)return s=_(s),!0;if(T(a)){let a=s+1;for(;a<t&&T(e.charCodeAt(a));)a++;if(a-s<=V){let t=F.get(e.slice(s,a).toLowerCase());if(t!==void 0)return(t===46||t===47||t===48)&&m(2),p(t,s,a),!0}for(;a<t;){let t=e.charCodeAt(a);if(t<128&&H[t]===1)break;a++}return n.push(59),r.push(e.slice(s,a).trim()),i.push(s),s=a,!0}switch(a){case 123:return p(15,s,s+1),!0;case 125:return p(16,s,s+1),!0;case 58:return e.charCodeAt(s+1)===58&&e.charCodeAt(s+2)===58?p(17,s,s+3):p(18,s,s+1),!0;case 60:return e.charCodeAt(s+1)===45&&(p(55,s,s+2),!0);case 45:return e.charCodeAt(s+1)===62?p(56,s,s+2):p(57,s,s+1),!0;case 34:return m(1),s++,!0;case 44:return p(60,s,s+1),!0}return!1},S=n=>{if(T(n)){let n=s+1;for(;n<t&&T(e.charCodeAt(n));)n++;return p(49,s,n),!0}switch(n){case 58:return p(50,s,s+1),!0;case 59:return p(51,s,s+1),!0;case 37:return p(52,s,s+1),!0;case 45:return p(53,s,s+1),!0;case 35:return p(54,s,s+1),!0;case 32:return s++,!0;case 34:return m(1),s++,!0;case 10:return h(),s++,!0;case 44:return p(60,s,s+1),!0}return!1};for(;s<t;){let a=e.charCodeAt(s),c=!0;switch(o){case 0:c=x(a);break;case 2:c=S(a);break;case 1:if(a===34)h(),s++;else{let n=e.indexOf(`"`,s);p(58,s,n===-1?t:n)}break;case 3:h(),p(4,s,_(s));break;case 4:h(),p(6,s,_(s));break;case 5:if(a===125)h(),s++;else{let n=e.indexOf(`}`,s);p(7,s,n===-1?t:n)}}if(!c)return n.push(1),r.push(``),i.push(s),{types:n,texts:r,starts:i}}let C=o===0?13:o===1?58:o===3?4:o===4?6:o===5?7:-1;return C!==-1&&p(C,t,t),p(0,t,t),{types:n,texts:r,starts:i}}var re=[O.REQUIREMENT,O.FUNCTIONAL_REQUIREMENT,O.INTERFACE_REQUIREMENT,O.PERFORMANCE_REQUIREMENT,O.PHYSICAL_REQUIREMENT,O.DESIGN_CONSTRAINT,k.LOW_RISK,k.MED_RISK,k.HIGH_RISK,A.VERIFY_ANALYSIS,A.VERIFY_DEMONSTRATION,A.VERIFY_INSPECTION,A.VERIFY_TEST,``,j.CONTAINS,j.COPIES,j.DERIVES,j.SATISFIES,j.VERIFIES,j.REFINES,j.TRACES];function U(e){return e===49||e===50||e===54||e===53||e===51}function ie(e,t){let{types:n,texts:r,starts:i}=ne(e),a=0,o=[],s=[],c=(t,r=t,...o)=>{let s=n[a];if(s===1)throw g(`requirement`,e,i[a],``,!0);for(let e=r;e>=t;e--)o.unshift(e);let c=o.map(e=>`'${P[e]}'`).join(`, `);throw g(`requirement`,e,i[a],`Expecting ${c}, got '${P[s]}'`)},l=e=>(n[a]!==e&&c(e),r[a++]),u=(e,t)=>{let r=n[a];return(r<e||r>t)&&c(e,t),a++,re[r-23]},d=()=>{let e=n[a];return e!==59&&e!==58&&c(58,59),r[a++]},f=()=>{let e=n[a];if(e===3)a++,t.setAccTitle(l(4).trim());else if(e===5)a++,t.setAccDescription(l(6).trim());else if(e===7)t.setAccDescription(r[a++].trim());else return!1;return!0},p=()=>{let e=[];for(;;){let t=n[a];if(t!==49&&t!==59&&t!==58&&c(58,59,49),e.push(r[a++]),n[a]!==60)return e;a++}},m=()=>{let e=[];for(;;){U(n[a])||c(49,51,53,54);let t=r[a++];for(;U(n[a]);)t+=r[a++];if(e.push(t),n[a]!==60)return e;a++}},h=()=>{let e;return n[a]===17?(a++,e=p(),l(15)):n[a]===15?a++:c(15,15,17),l(12),e},_=e=>{for(o.length=0,s.length=0;;){let t=n[a];if(t===16){a++;break}t===12?a++:((e?t<19||t>22:t!==44&&t!==45)&&(e&&c(19,22,12,16),c(44,45,12,16)),a++,l(18),s.push(t===21?u(29,31):t===22?u(32,35):d()),o.push(t),l(12))}for(let e=o.length-1;e>=0;e--){let n=s[e];switch(o[e]){case 19:t.setNewReqId(n);break;case 20:t.setNewReqText(n);break;case 21:t.setNewReqRisk(n);break;case 22:t.setNewReqVerifyMethod(n);break;case 44:t.setNewElementType(n);break;default:t.setNewElementDocRef(n)}}};for(;f();)n[a]===12&&a++;for(n[a]!==14&&c(14,14,3,5,7),a++,l(12);;){let e=n[a];if(e===13)return;if(e===12)a++;else{if(f())continue;if(e>=8&&e<=11)a++,t.setDirection(e===8?`TB`:e===9?`BT`:e===10?`RL`:`LR`);else if(e>=23&&e<=28){let e=u(23,28),n=d(),r=h();_(!0),t.addRequirement(n,e),r&&t.setClass([n],r)}else if(e===36){a++;let e=d(),n=h();_(!1),t.addElement(e),n&&t.setClass([e],n)}else if(e===46){a++;let e=p();t.setCssStyle(e,m())}else if(e===47){a++;let e=p();t.defineClass(e,m())}else if(e===48){a++;let e=p();t.setClass(e,p())}else if(e===59||e===58){let e=r[a++],i=n[a];if(i===17)a++,t.setClass([e],p());else if(i===55||i===57){a++;let n=u(37,43);l(i===57?56:57);let r=d();i===57?t.addRelationship(n,e,r):t.addRelationship(n,r,e)}else c(17,17,55,57)}else c(12,13,3,5,7,8,9,10,11,23,24,25,26,27,28,36,47,48,46,59,58)}}}function ae(e){let t=[];for(let n of e.requirements.values())t.push({id:n.name,requirement:n,element:void 0,cssStyles:n.cssStyles,cssClasses:n.classes.join(` `)});for(let n of e.elements.values())t.push({id:n.name,requirement:void 0,element:n,cssStyles:n.cssStyles,cssClasses:n.classes.join(` `)});let n=t=>e.requirements.has(t)||e.elements.has(t)?t:void 0;return{nodes:t,edges:e.relations.map((e,t)=>({id:`${e.src}-${e.dst}-${t}`,start:n(e.src),end:n(e.dst),type:e.type,contains:e.type===j.CONTAINS})),direction:e.direction}}var oe=40,se=16,ce=56,W=14,G=10,K=2,q=12,le=120,ue=280,de=240,J=26,Y=6,X=9,Z=4.5,fe=28,pe=14;function Q(e,t,n){if(t===0||e.lines.length===0)return e;let r=0,i=[],a=e.lines.map(a=>{let o=0,s=a.map(r=>{let i=r.style|t,a=r.icon!==void 0||r.math?r.width:n.width(r.text,e.size,i);return o+=a,{...r,style:i,width:a}});return i.push(o),o>r&&(r=o),s});return{...e,lines:a,widths:i,width:r}}function $(e,t,n,a){let o=C(e),c=``,l=``;for(let u=0;u<e.lines.length;u++){let d=e.lines[u],f=n+r(e,u);if(i(d)){l+=S(s(e,u),t+e.widths[u]/2,f,a);continue}let p=f+e.size*.35,m=!0;for(let e of d)e.icon!==void 0||e.text===``||(c+=`<tspan${m?` x="${_(t)}" y="${_(p)}"`:``}${w(e.style&~o)}>${b(e.text)}</tspan>`,m=!1)}return(c===``?``:`<text${D(a,o)} text-anchor="start" xml:space="preserve">${c}</text>`)+l}function me(e,t,n,r){let i=Y,a=e-n*i,o=t-r*i;return`<circle class="pele-marker" cx="${_(a)}" cy="${_(o)}" r="${i}"/><path class="pele-marker" d="M${_(a-n*i)},${_(o-r*i)}L${_(a+n*i)},${_(o+r*i)}M${_(a+r*i)},${_(o-n*i)}L${_(a-r*i)},${_(o+n*i)}"/>`}function he(e,t,n,r){let i=e-n*X,a=t-r*X;return`<path class="pele-marker" d="M${_(i-r*Z)},${_(a+n*Z)}L${_(e)},${_(t)}L${_(i+r*Z)},${_(a-n*Z)}" stroke-linejoin="round"/>`}function ge(e,t,n){let r=/* @__PURE__ */ new Map;for(let n of t){let t=n.route;if(!(n.src===n.dst||t.length<6))for(let i of[0,t.length-3]){let a=i===0?n.src:n.dst,o=e[a],s=t[i]-o.x,c=t[i+1]-o.y,l=i===0?3:i-3,u;if(Math.abs(Math.abs(c)-o.h/2)<.5)u=c<0?0:2;else if(Math.abs(Math.abs(s)-o.w/2)<.5)u=s<0?3:1;else continue;let d=a*4+u,f={route:t,at:i,toward:t[u%2==0?l:l+1]},p=r.get(d);p?p.push(f):r.set(d,[f])}}for(let[t,i]of r){if(i.length<2)continue;let r=t>>2,a=t%2==1,o=(a?n[r].h:n[r].w)-2*pe,s=Math.max(0,Math.min(fe,o/(i.length-1)));i.sort((e,t)=>e.toward-t.toward);for(let t=0;t<i.length;t++){let n=(t-(i.length-1)/2)*s;a?i[t].route[i[t].at+1]=e[r].y+n:i[t].route[i[t].at]=e[r].x+n}}}function _e(e,t,n){return c(e.direction)?l(n,r=>ve(e,t,n,r)):ve(e,t,n,!1)}function ve(r,i,s,c){let l=ae(r),g=s.fontSize??16,v=Math.round(g*.875),y=s.measurer??te(s.fontFamily,s.fontFamilyMono),b=s.padding??8,C=/* @__PURE__ */ new Map,w=[],T=[];for(let e of l.nodes){let n=a(e.cssStyles),r=+!!n.bold|(n.italic?2:0),i=n.fontSize??g,o=n.fontSize??v,s=e.requirement,c=e.element,l=Q(t(`«${s?s.type:`Element`}»`,!1,y,o,4e3),r,y),u=Q(t(e.id,!0,y,i,ue),r|1,y),d=s?[[`ID`,s.requirementId],[`Text`,s.text],[`Risk`,s.risk],[`Verification`,s.verifyMethod]]:[[`Type`,c.type],[`Doc Ref`,c.docRef]],p=[],m=0,h=0,_=0;for(let[e,n]of d){if(!n)continue;let i=Q(t(e,!1,y,o,4e3),r,y),a=Q(t(n,!0,y,o,de),r,y),s=Math.max(i.height,a.height);p.push({key:i,value:a,height:s}),m=Math.max(m,i.width),h=Math.max(h,a.width),_+=s+K}p.length>0&&(_+=2*G-K);let b=2*G+l.height+u.height,x=Math.max(le,Math.max(l.width,u.width)+2*W,p.length>0?m+q+h+2*W:0);C.has(e.id)||C.set(e.id,w.length);let S=f(x,b+_);S.seq=w.length,w.push({node:e,style:n,kind:l,name:u,rows:p,keyWidth:m,headHeight:b,w:x,h:b+_}),T.push(S)}let E=c?`TB`:p(l.direction),D=E===`LR`||E===`RL`,O=[],k=[],A=[];for(let e of l.edges){let n=e.start===void 0?void 0:C.get(e.start),r=e.end===void 0?void 0:C.get(e.end);if(n===void 0||r===void 0)continue;let i=t(`«${e.type}»`,!1,y,v,4e3);n===r&&(D?T[n].h+=2*(J+i.height+8):T[n].w+=2*(J+i.width+12)),A.push(e),k.push(i),O.push({src:n,dst:r,minlen:1,labelW:i.width+12,labelH:i.height+4,route:[],labelX:0,labelY:0})}let j=m(T,O,E,{nodeSep:oe,edgeSep:se,rankSep:ce,portSep:20});ge(T,O,w);let M=h(r.title,y,g,4e3),N=M.height>0?M.height+12:0,P=Math.max(j.width,M.width),F=b+(P-j.width)/2,I=b+N,L=Math.ceil(P+2*b),R=Math.ceil(j.height+N+2*b),z=``;for(let e=0;e<w.length;e++){let t=w[e],{w:r,h:i,style:a}=t,s=ee(a),c=s||` fill="var(--_m)"`,l=-r/2,u=-i/2,d=`<rect x="${_(l)}" y="${_(u)}" width="${_(r)}" height="${_(i)}" fill="var(--_s)" stroke="var(--_b)"${o(a.shape)}/>`+S(t.kind,0,u+G+t.kind.height/2,` class="pele-stereotype" font-size="${t.kind.size}"${c}${a.text}`)+S(t.name,0,u+G+t.kind.height+t.name.height/2,` class="pele-label" font-size="${t.name.size}" font-weight="var(--_hw)"${s}${a.text}`);if(t.rows.length>0){let e=u+t.headHeight;d+=`<path class="pele-divider" d="M${_(l)},${_(e)}H${_(-l)}" stroke="var(--_b)"${a.line}/>`;let n=e+G,r=``;for(let e of t.rows)r+=$(e.key,l+W,n,` class="pele-field"${c}${a.text}`)+$(e.value,l+W+t.keyWidth+q,n,` class="pele-value"${s}${a.text}`),n+=e.height+K;d+=`<g class="pele-fields" font-size="${t.rows[0].key.size}">${r}</g>`}z+=`<g class="pele-node ${t.node.requirement?`pele-requirement`:`pele-element`}${n(t.node.cssClasses.replace(/^default\s?/,``))}" data-id="${x(t.node.id)}" transform="translate(${_(T[e].x+F)},${_(T[e].y+I)})">${d}</g>`}let B=``,V=``,H=/* @__PURE__ */ new Map;for(let e=0;e<O.length;e++){let t=O[e],n=A[e],r=k[e],i=n.contains?2*Y:0,a,o=t.labelX+F,s=t.labelY+I;if(t.src===t.dst){let e=w[t.src],n=T[t.src].x+F,r=T[t.src].y+I,c=H.get(t.src)??0;H.set(t.src,c+1);let l=(D?e.w:e.h)/2,u=Math.min(l-4,12+c*6),d=J+c*8,f=(t,i)=>D?`${_(n+i)},${_(r+e.h/2+t)}`:`${_(n+e.w/2+t)},${_(r+i)}`,p=d/.75;a={d:`M${f(i,-u)}C${f(p,-u)} ${f(p,u)} ${f(0,u)}`,sx:D?n-u:n+e.w/2,sy:D?r+e.h/2:r-u,sdx:D?0:-1,sdy:D?-1:0,ex:D?n+u:n+e.w/2,ey:D?r+e.h/2:r+u,edx:D?0:-1,edy:D?-1:0},D?(o=n,s=r+e.h/2+d+4+t.labelH/2):(o=n+e.w/2+d+4+t.labelW/2,s=r)}else{let e=t.route.slice();for(let t=0;t<e.length;t+=3)e[t]+=F,e[t+1]+=I;a=u(e,void 0,i,0)}B+=`<g class="pele-edge pele-relationship-${n.contains?`contains`:`arrow`}" data-id="${x(n.id)}"><path d="${a.d}"${n.contains?``:` stroke-dasharray="5 4"`}/>`+(n.contains?me(a.sx,a.sy,a.sdx,a.sdy):he(a.ex,a.ey,a.edx,a.edy))+`</g>`,V+=d(n.id,r,o,s,``)}return{svg:e(`requirement`,L,R,g,s,r,S(M,L/2,b+M.height/2,` class="pele-title" font-weight="var(--_tw)"`)+(B?`<g class="pele-edges" fill="none" stroke="var(--_l)" stroke-linecap="round">${B}</g>`:``)+(V?`<g class="pele-edge-labels" font-size="${v}" fill="var(--_m)">${V}</g>`:``)+`<g class="pele-nodes">${z}</g>`),width:L,height:R,links:[]}}var ye={type:`requirement`,math:!0,parse(e,t,n){let r=new N;return n&&(r.title=n),ie(e,r),r},render:_e};export{ye as requirement};
|