libpetri 1.8.4 → 2.0.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 (41) hide show
  1. package/README.md +47 -0
  2. package/dist/{chunk-B2D5DMTO.js → chunk-4L6JVKH4.js} +165 -8
  3. package/dist/chunk-4L6JVKH4.js.map +1 -0
  4. package/dist/chunk-H62Z76FY.js +1346 -0
  5. package/dist/chunk-H62Z76FY.js.map +1 -0
  6. package/dist/chunk-SXK2Z45Z.js +50 -0
  7. package/dist/chunk-SXK2Z45Z.js.map +1 -0
  8. package/dist/debug/index.d.ts +50 -3
  9. package/dist/debug/index.js +64 -6
  10. package/dist/debug/index.js.map +1 -1
  11. package/dist/doclet/index.d.ts +152 -31
  12. package/dist/doclet/index.js +458 -57
  13. package/dist/doclet/index.js.map +1 -1
  14. package/dist/doclet/resources/petrinet-diagrams.css +384 -7
  15. package/dist/doclet/resources/petrinet-diagrams.js +7214 -106
  16. package/dist/dot-exporter-PMHOQHZ3.js +8 -0
  17. package/dist/{event-store-BnyHh3TF.d.ts → event-store-2zkXeQkd.d.ts} +1 -1
  18. package/dist/export/index.d.ts +16 -2
  19. package/dist/export/index.js +1 -1
  20. package/dist/index.d.ts +41 -5
  21. package/dist/index.js +1221 -35
  22. package/dist/index.js.map +1 -1
  23. package/dist/petri-net-BDrj4XZE.d.ts +1461 -0
  24. package/dist/render-P6GROU7J.js +16 -0
  25. package/dist/render-P6GROU7J.js.map +1 -0
  26. package/dist/verification/index.d.ts +3 -144
  27. package/dist/verification/index.js +30 -1214
  28. package/dist/verification/index.js.map +1 -1
  29. package/dist/viewer/index.d.ts +196 -0
  30. package/dist/viewer/index.js +442 -0
  31. package/dist/viewer/index.js.map +1 -0
  32. package/dist/viewer/viewer-static.iife.js +3 -0
  33. package/dist/viewer/viewer.css +503 -0
  34. package/dist/viewer/viewer.iife.js +7219 -0
  35. package/package.json +17 -8
  36. package/dist/chunk-B2D5DMTO.js.map +0 -1
  37. package/dist/chunk-VQ4XMJTD.js +0 -107
  38. package/dist/chunk-VQ4XMJTD.js.map +0 -1
  39. package/dist/dot-exporter-3CVCH6J4.js +0 -8
  40. package/dist/petri-net-D-GN9g_D.d.ts +0 -570
  41. /package/dist/{dot-exporter-3CVCH6J4.js.map → dot-exporter-PMHOQHZ3.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/doclet/petri-net-plugin.ts","../../src/doclet/net-resolver.ts","../../src/doclet/svg-renderer.ts","../../src/doclet/diagram-renderer.ts"],"sourcesContent":["/**\n * TypeDoc plugin for `@petrinet` tag — auto-generates interactive SVG diagrams\n * from PetriNet definitions and embeds them in TypeDoc output.\n *\n * Mirrors: `org.libpetri.doclet.PetriNetTaglet`\n *\n * Plugin lifecycle (TypeDoc hooks):\n * 1. `bootstrapEnd` → register `@petrinet` as known block tag\n * 2. `preRenderAsyncJobs` → walk reflections, resolve nets, generate SVGs, cache HTML\n * 3. `comment.beforeTags` → inject cached HTML via JSX.Raw, skip default rendering\n *\n * @module doclet/petri-net-plugin\n */\n\nimport {\n type Application,\n type CommentTag,\n type DeclarationReflection,\n type ProjectReflection,\n type Reflection,\n JSX,\n} from 'typedoc';\nimport { resolveNet } from './net-resolver.js';\nimport { dotToSvg } from './svg-renderer.js';\nimport { renderSvg, escapeHtml } from './diagram-renderer.js';\n\n// Lazy import to avoid requiring libpetri/export at module level\nasync function getDotExport(): Promise<typeof import('../export/dot-exporter.js').dotExport> {\n const mod = await import('../export/dot-exporter.js');\n return mod.dotExport;\n}\n\n/** Tag name including @ prefix. */\nconst TAG_NAME = '@petrinet' as `@${string}`;\n\n/** Cache: reflection ID → rendered HTML string */\nconst htmlCache = new Map<number, string>();\n\n/**\n * Loads the petri-net plugin into the TypeDoc application.\n *\n * @param app - the TypeDoc Application instance\n */\nexport function load(app: Application): void {\n // 1. Register @petrinet as a known block tag at bootstrap time\n app.on('bootstrapEnd', () => {\n const blockTags = app.options.getValue('blockTags') as string[];\n if (!blockTags.includes(TAG_NAME)) {\n app.options.setValue('blockTags', [...blockTags, TAG_NAME]);\n }\n });\n\n // 2. Resolve nets and generate SVG during pre-render async phase\n app.renderer.preRenderAsyncJobs.push(async (output) => {\n htmlCache.clear();\n await processProject(app, output.project);\n });\n\n // 3. Inject cached HTML into comment output, suppress default tag rendering\n app.renderer.hooks.on('comment.beforeTags', (_context, comment, reflection) => {\n const cached = htmlCache.get(reflection.id);\n if (!cached) return JSX.createElement(JSX.Raw, { html: '' });\n\n // Mark @petrinet tags as skip so TypeDoc doesn't render them as plain text\n for (const tag of comment.blockTags) {\n if (tag.tag === TAG_NAME) {\n tag.skipRendering = true;\n }\n }\n\n // Inject raw HTML using TypeDoc's JSX\n return JSX.createElement(JSX.Raw, { html: cached });\n });\n}\n\n/**\n * Walks all reflections in the project and processes `@petrinet` tags.\n */\nasync function processProject(app: Application, project: ProjectReflection): Promise<void> {\n const reflections = Object.values(project.reflections);\n\n for (const reflection of reflections) {\n const comment = reflection.comment;\n if (!comment) continue;\n\n const petrinetTags = comment.blockTags.filter(\n (tag: CommentTag) => tag.tag === TAG_NAME,\n );\n if (petrinetTags.length === 0) continue;\n\n for (const tag of petrinetTags) {\n const reference = tagContent(tag);\n try {\n const html = await generateDiagram(reference, reflection, app);\n const existing = htmlCache.get(reflection.id) ?? '';\n htmlCache.set(reflection.id, existing + html);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n app.logger.warn(`@petrinet error for ${reflection.getFriendlyFullName()}: ${msg}`);\n const html = errorHtml(`Error generating diagram for '${reference}': ${msg}`);\n const existing = htmlCache.get(reflection.id) ?? '';\n htmlCache.set(reflection.id, existing + html);\n }\n }\n }\n}\n\n/**\n * Generates a diagram HTML string for a single `@petrinet` reference.\n */\nasync function generateDiagram(\n reference: string,\n reflection: Reflection,\n app: Application,\n): Promise<string> {\n const trimmed = reference.trim();\n if (!trimmed) {\n return errorHtml(`Empty @petrinet reference on ${reflection.getFriendlyFullName()}`);\n }\n\n const sourceFile = getSourceFilePath(reflection);\n if (!sourceFile) {\n return errorHtml(`Cannot determine source file for ${reflection.getFriendlyFullName()}`);\n }\n\n const resolved = await resolveNet(trimmed, sourceFile);\n if (!resolved) {\n return errorHtml(`Cannot resolve PetriNet: ${trimmed}`);\n }\n\n const dotExport = await getDotExport();\n const dot = dotExport(resolved.net);\n\n try {\n const svg = await dotToSvg(dot);\n return renderSvg(resolved.title, svg, dot);\n } catch (e) {\n app.logger.warn(`SVG rendering failed, falling back to DOT source: ${e}`);\n return renderSvg(\n resolved.title,\n `<pre><code>${escapeHtml(dot)}</code></pre>`,\n dot,\n );\n }\n}\n\n/**\n * Extracts text content from a CommentTag.\n */\nfunction tagContent(tag: CommentTag): string {\n return tag.content\n .map((part) => part.text)\n .join('')\n .trim();\n}\n\n/**\n * Gets the source file path from a reflection.\n */\nfunction getSourceFilePath(reflection: Reflection): string | null {\n const decl = reflection as DeclarationReflection;\n if (decl.sources && decl.sources.length > 0) {\n return decl.sources[0]!.fullFileName;\n }\n if (reflection.parent) {\n return getSourceFilePath(reflection.parent);\n }\n return null;\n}\n\n/**\n * Renders an error message as styled HTML.\n */\nfunction errorHtml(message: string): string {\n return `<div class=\"petrinet-error\" style=\"color: #dc3545; border: 1px solid #dc3545; padding: 10px; border-radius: 4px;\">\n<strong>@petrinet Error:</strong> ${escapeHtml(message)}\n</div>`;\n}\n","/**\n * Dynamic import-based PetriNet resolver for TypeDoc.\n *\n * TypeScript cannot use reflection like Java — modules must be importable\n * at doc time (from `dist/` after `npm run build`).\n *\n * Tag format:\n * ```\n * @petrinet ./path/to/module#exportName — access a PetriNet constant\n * @petrinet ./path/to/module#functionName() — call a function returning PetriNet\n * @petrinet #localExport — resolve from same file\n * ```\n *\n * Mirrors: `org.libpetri.doclet.PetriNetTaglet.resolvePetriNet()`\n *\n * @module doclet/net-resolver\n */\n\nimport { resolve, dirname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { PetriNet } from '../core/petri-net.js';\n\nexport interface ResolvedNet {\n readonly net: PetriNet;\n readonly title: string;\n}\n\n/**\n * Parses a `@petrinet` reference and resolves the PetriNet via dynamic import.\n *\n * @param reference - the tag content, e.g. `./definition#buildDebugNet()`\n * @param sourceFilePath - absolute path to the source file containing the tag\n * @returns the resolved PetriNet with title, or null on failure\n */\nexport async function resolveNet(\n reference: string,\n sourceFilePath: string,\n): Promise<ResolvedNet | null> {\n const trimmed = reference.trim().split(/\\s+/)[0] ?? '';\n if (!trimmed) return null;\n\n const hashIndex = trimmed.indexOf('#');\n if (hashIndex === -1) return null;\n\n const modulePath = trimmed.substring(0, hashIndex);\n const exportRef = trimmed.substring(hashIndex + 1);\n if (!exportRef) return null;\n\n const isCall = exportRef.endsWith('()');\n const exportName = isCall ? exportRef.slice(0, -2) : exportRef;\n\n // Resolve module path relative to the source file\n let absolutePath: string;\n if (modulePath) {\n absolutePath = resolve(dirname(sourceFilePath), modulePath);\n } else {\n // #localExport — resolve from same file's compiled output\n absolutePath = sourceFilePath;\n }\n\n // Try .js extension for compiled output, then .ts for ts-node scenarios\n const candidates = [\n absolutePath,\n absolutePath + '.js',\n absolutePath + '.ts',\n absolutePath.replace(/\\.ts$/, '.js'),\n ];\n\n let mod: Record<string, unknown> | undefined;\n for (const candidate of candidates) {\n try {\n mod = await import(pathToFileURL(candidate).href) as Record<string, unknown>;\n break;\n } catch {\n // Try next candidate\n }\n }\n\n if (!mod) return null;\n\n const exported = mod[exportName];\n if (exported == null) return null;\n\n let net: PetriNet;\n if (isCall) {\n if (typeof exported !== 'function') return null;\n const result = exported() as PetriNet | { net: PetriNet };\n // Support functions that return { net: PetriNet, ... }\n net = 'net' in result ? result.net : result;\n } else {\n net = exported as PetriNet;\n }\n\n // Validate it looks like a PetriNet (has name and transitions)\n if (typeof net?.name !== 'string' || !(net?.transitions instanceof Set)) {\n return null;\n }\n\n return { net, title: net.name };\n}\n","/**\n * SVG renderer using `@viz-js/viz` (Graphviz WASM).\n *\n * Pure WASM — zero external dependencies. No system `dot` binary required.\n *\n * Mirrors: Java's `PetriNetTaglet.dotToSvg()` (which uses `dot -Tsvg` subprocess)\n *\n * @module doclet/svg-renderer\n */\n\n/**\n * Renders a DOT string to SVG using the `@viz-js/viz` WASM engine.\n *\n * Strips the XML prolog/DOCTYPE and explicit width/height attributes so the SVG\n * scales via viewBox + CSS instead of overriding with fixed pt sizes.\n *\n * @param dot - the DOT source string\n * @returns SVG markup string\n * @throws if `@viz-js/viz` is not installed or DOT parsing fails\n */\nexport async function dotToSvg(dot: string): Promise<string> {\n // Dynamic import — @viz-js/viz is an optional peer dependency\n const { instance } = await import('@viz-js/viz');\n const viz = await instance();\n let svg = viz.renderString(dot, { format: 'svg', engine: 'dot' });\n\n // Strip XML prolog and DOCTYPE — invalid inside HTML5\n const svgStart = svg.indexOf('<svg');\n if (svgStart > 0) svg = svg.substring(svgStart);\n\n // Strip explicit width/height attributes (e.g. \"1942pt\") so the SVG\n // scales via viewBox + CSS instead of overriding with fixed pt sizes\n svg = svg.replace(/\\s+(?:width|height)=\"[^\"]*\"/g, '');\n\n return svg;\n}\n","/**\n * Shared HTML renderer for Petri Net diagrams in TypeDoc.\n *\n * Generates consistent HTML markup with interactive controls for zoom, pan,\n * and fullscreen functionality. Accepts pre-rendered SVG from `@viz-js/viz`.\n *\n * CSS and JS are loaded from bundled resources and inlined into the generated\n * HTML, making the plugin fully self-contained with no external file dependencies.\n *\n * Mirrors: `org.libpetri.doclet.DiagramRenderer`\n *\n * @module doclet/diagram-renderer\n */\n\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadResource(filename: string): string {\n return readFileSync(join(__dirname, 'resources', filename), 'utf-8');\n}\n\nlet inlineCss: string | undefined;\nlet inlineJs: string | undefined;\n\nfunction css(): string {\n return (inlineCss ??= loadResource('petrinet-diagrams.css'));\n}\n\nfunction js(): string {\n return (inlineJs ??= loadResource('petrinet-diagrams.js'));\n}\n\n/**\n * Escapes HTML special characters for safe embedding.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/**\n * Renders a pre-built SVG diagram with an optional title.\n *\n * Inlines CSS and JS from bundled resources. The JS is guarded by an\n * idempotency check so it only executes once per page, even when multiple\n * `@petrinet` tags appear.\n *\n * @param title - optional title (null/undefined for no title)\n * @param svgContent - the SVG markup\n * @param dotSource - the DOT source code for display in a collapsible block\n * @returns HTML markup with diagram controls\n */\nexport function renderSvg(\n title: string | null | undefined,\n svgContent: string,\n dotSource: string,\n): string {\n const titleHtml = title ? `<h4>${escapeHtml(title)}</h4>\\n` : '';\n const summaryText = title ? 'View DOT Source' : 'View Source';\n\n return `<style>${css()}</style>\n<script>if(!window._petriNetDiagramsInit){window._petriNetDiagramsInit=true;\n${js()}\n}</script>\n<div class=\"petrinet-diagram\">\n${titleHtml}<div class=\"diagram-container\">\n<div class=\"diagram-controls\">\n<button class=\"diagram-btn btn-reset\" title=\"Reset zoom\">Reset</button>\n<button class=\"diagram-btn btn-fullscreen\" onclick=\"PetriNetDiagrams.toggleFullscreen(this)\">Fullscreen</button>\n</div>\n${svgContent}\n</div>\n<details>\n<summary>${summaryText}</summary>\n<pre><code>${escapeHtml(dotSource)}</code></pre>\n</details>\n</div>`;\n}\n"],"mappings":"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","names":["dirname"]}
1
+ {"version":3,"sources":["../../src/doclet/petri-net-plugin.ts","../../src/doclet/net-resolver.ts","../../src/doclet/diagram-renderer.ts","../../src/doclet/subnet-header.ts","../../src/doclet/subnet-dot-export.ts","../../src/doclet/svg-renderer.ts"],"sourcesContent":["/**\n * TypeDoc plugin for `@petrinet` and `@subnet` tags — auto-generates\n * interactive SVG diagrams from PetriNet / SubnetDef / Instance definitions\n * and embeds them in TypeDoc output.\n *\n * Mirrors: `org.libpetri.doclet.PetriNetTaglet` + `SubnetTaglet`\n *\n * Plugin lifecycle (TypeDoc hooks):\n * 1. `bootstrapEnd` → register `@petrinet` and `@subnet` as known block tags.\n * 2. `preRenderAsyncJobs` → walk reflections, resolve references, generate\n * SVGs, cache HTML.\n * 3. `comment.beforeTags` → inject cached HTML via `JSX.Raw`, skip default\n * rendering for the recognised tags.\n *\n * @module doclet/petri-net-plugin\n */\n\nimport {\n type Application,\n type CommentTag,\n type DeclarationReflection,\n type ProjectReflection,\n type Reflection,\n JSX,\n} from 'typedoc';\nimport { resolveNet, type ResolvedNet } from './net-resolver.js';\nimport { renderSvg, renderSubnetSvg, escapeHtml } from './diagram-renderer.js';\nimport { forSubnetDef, forInstance } from './subnet-header.js';\nimport { fullBody as subnetFullBody, interfaceOnly as subnetInterfaceOnly } from './subnet-dot-export.js';\n\n// Lazy import to avoid requiring libpetri/export at module level.\nasync function getDotExport(): Promise<typeof import('../export/dot-exporter.js').dotExport> {\n const mod = await import('../export/dot-exporter.js');\n return mod.dotExport;\n}\n\n/** Block tag for full body diagrams — resolves PetriNet / SubnetDef / Instance. */\nconst TAG_PETRINET = '@petrinet' as `@${string}`;\n/** Block tag for interface-only subnet diagrams. */\nconst TAG_SUBNET = '@subnet' as `@${string}`;\n\n/** Cache: reflection ID → rendered HTML string */\nconst htmlCache = new Map<number, string>();\n\n/**\n * Loads the petri-net plugin into the TypeDoc application.\n *\n * @param app - the TypeDoc Application instance\n */\nexport function load(app: Application): void {\n // 1. Register both tags as known block tags at bootstrap time.\n app.on('bootstrapEnd', () => {\n const blockTags = app.options.getValue('blockTags') as string[];\n const additions: string[] = [];\n if (!blockTags.includes(TAG_PETRINET)) additions.push(TAG_PETRINET);\n if (!blockTags.includes(TAG_SUBNET)) additions.push(TAG_SUBNET);\n if (additions.length > 0) {\n app.options.setValue('blockTags', [...blockTags, ...additions]);\n }\n });\n\n // 2. Resolve references and generate SVG during pre-render async phase.\n app.renderer.preRenderAsyncJobs.push(async (output) => {\n htmlCache.clear();\n await processProject(app, output.project);\n });\n\n // 3. Inject cached HTML into comment output, suppress default tag rendering.\n app.renderer.hooks.on('comment.beforeTags', (_context, comment, reflection) => {\n const cached = htmlCache.get(reflection.id);\n if (!cached) return JSX.createElement(JSX.Raw, { html: '' });\n\n // Mark our tags as skip so TypeDoc doesn't render them as plain text.\n for (const tag of comment.blockTags) {\n if (tag.tag === TAG_PETRINET || tag.tag === TAG_SUBNET) {\n tag.skipRendering = true;\n }\n }\n\n return JSX.createElement(JSX.Raw, { html: cached });\n });\n}\n\n/**\n * Walks all reflections in the project and processes `@petrinet` / `@subnet`\n * tags. Per-reflection HTML accumulates in {@link htmlCache} so multiple tags\n * on one symbol render in declaration order.\n */\nasync function processProject(app: Application, project: ProjectReflection): Promise<void> {\n const reflections = Object.values(project.reflections);\n\n for (const reflection of reflections) {\n const comment = reflection.comment;\n if (!comment) continue;\n\n for (const tag of comment.blockTags) {\n const isPetrinet = tag.tag === TAG_PETRINET;\n const isSubnet = tag.tag === TAG_SUBNET;\n if (!isPetrinet && !isSubnet) continue;\n\n const reference = tagContent(tag);\n try {\n const html = isSubnet\n ? await generateInterfaceOnly(reference, reflection)\n : await generateDiagram(reference, reflection);\n const existing = htmlCache.get(reflection.id) ?? '';\n htmlCache.set(reflection.id, existing + html);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n const tagName = isSubnet ? '@subnet' : '@petrinet';\n app.logger.warn(`${tagName} error for ${reflection.getFriendlyFullName()}: ${msg}`);\n const html = errorHtml(`Error generating diagram for '${reference}': ${msg}`);\n const existing = htmlCache.get(reflection.id) ?? '';\n htmlCache.set(reflection.id, existing + html);\n }\n }\n }\n}\n\n/**\n * Generates a diagram HTML string for a single `@petrinet` reference. Resolves\n * the reference to one of {@link ResolvedNet} variants and dispatches to the\n * matching renderer.\n */\nasync function generateDiagram(\n reference: string,\n reflection: Reflection,\n): Promise<string> {\n const trimmed = reference.trim();\n if (!trimmed) {\n return errorHtml(`Empty @petrinet reference on ${reflection.getFriendlyFullName()}`);\n }\n\n const sourceFile = getSourceFilePath(reflection);\n if (!sourceFile) {\n return errorHtml(`Cannot determine source file for ${reflection.getFriendlyFullName()}`);\n }\n\n const resolved = await resolveNet(trimmed, sourceFile);\n if (!resolved) {\n return errorHtml(`Cannot resolve PetriNet/SubnetDef/Instance: ${trimmed}`);\n }\n\n return await renderResolved(resolved, /* interfaceOnly */ false);\n}\n\n/**\n * Generates an interface-only diagram for a `@subnet` reference. When the\n * resolved reference is not a {@link import('../core/subnet-def.js').SubnetDef},\n * falls back to the regular diagram so the tag still produces useful output.\n */\nasync function generateInterfaceOnly(\n reference: string,\n reflection: Reflection,\n): Promise<string> {\n const trimmed = reference.trim();\n if (!trimmed) {\n return errorHtml(`Empty @subnet reference on ${reflection.getFriendlyFullName()}`);\n }\n\n const sourceFile = getSourceFilePath(reflection);\n if (!sourceFile) {\n return errorHtml(`Cannot determine source file for ${reflection.getFriendlyFullName()}`);\n }\n\n const resolved = await resolveNet(trimmed, sourceFile);\n if (!resolved) {\n return errorHtml(`Cannot resolve SubnetDef: ${trimmed}`);\n }\n\n return await renderResolved(resolved, /* interfaceOnly */ true);\n}\n\n/**\n * Dispatches a {@link ResolvedNet} to the matching renderer. The\n * `interfaceOnly` flag is honoured only when the resolved kind is `subnet` —\n * for `net` and `instance` it is ignored (the body is the whole point of\n * those references).\n */\nasync function renderResolved(\n resolved: ResolvedNet,\n interfaceOnly: boolean,\n): Promise<string> {\n switch (resolved.kind) {\n case 'net': {\n const dotExport = await getDotExport();\n const dot = dotExport(resolved.value);\n return renderDiagramHtml(resolved.title, dot, /* header */ null);\n }\n case 'subnet': {\n const dot = interfaceOnly\n ? subnetInterfaceOnly(resolved.value)\n : subnetFullBody(resolved.value);\n const header = forSubnetDef(resolved.value, interfaceOnly);\n const title = resolved.value.name + (interfaceOnly ? ' (interface)' : '');\n return renderDiagramHtml(title, dot, header);\n }\n case 'instance': {\n const dotExport = await getDotExport();\n const dot = dotExport(resolved.value.renamedBody);\n const header = forInstance(resolved.value);\n return renderDiagramHtml(resolved.title, dot, header);\n }\n }\n}\n\n/**\n * Emits the diagram HTML. The canonical viewer renders DOT to SVG\n * client-side via Graphviz-WASM, so no server-side SVG rendering happens\n * here. Routes through the subnet-aware renderer when `header` is non-null,\n * the plain renderer otherwise.\n */\nfunction renderDiagramHtml(\n title: string,\n dot: string,\n header: string | null,\n): string {\n if (header !== null) {\n return renderSubnetSvg(title, header, dot);\n }\n return renderSvg(title, dot);\n}\n\n/**\n * Extracts text content from a CommentTag.\n */\nfunction tagContent(tag: CommentTag): string {\n return tag.content\n .map((part) => part.text)\n .join('')\n .trim();\n}\n\n/**\n * Gets the source file path from a reflection. Walks parents because\n * comment-bearing reflections sometimes lack their own `sources` (e.g.\n * inherited members).\n */\nfunction getSourceFilePath(reflection: Reflection): string | null {\n const decl = reflection as DeclarationReflection;\n if (decl.sources && decl.sources.length > 0) {\n return decl.sources[0]!.fullFileName;\n }\n if (reflection.parent) {\n return getSourceFilePath(reflection.parent);\n }\n return null;\n}\n\n/**\n * Renders an error message as styled HTML.\n */\nfunction errorHtml(message: string): string {\n return `<div class=\"petrinet-error\" style=\"color: #dc3545; border: 1px solid #dc3545; padding: 10px; border-radius: 4px;\">\n<strong>@petrinet Error:</strong> ${escapeHtml(message)}\n</div>`;\n}\n","/**\n * Dynamic import-based resolver for `@petrinet` / `@subnet` references.\n *\n * Resolves three kinds of static exports per Java's `PetriNetTaglet` design:\n * 1. `PetriNet` — full body diagram (existing behaviour).\n * 2. `SubnetDef<?>` — body diagram with interface ports highlighted, plus a\n * subnet header with port/channel badges.\n * 3. `Instance<?>` — renamed body diagram (already prefix-clustered by the\n * DOT exporter), plus an instance header with prefix, def name, and\n * params summary.\n *\n * TypeScript cannot use reflection like Java — modules must be importable at\n * doc time (typically from `dist/` after `npm run build`).\n *\n * Tag format:\n * ```\n * @petrinet ./path/to/module#exportName — access a static export\n * @petrinet ./path/to/module#functionName() — call a function returning one\n * @petrinet #localExport — resolve from same file\n * ```\n *\n * Mirrors: `org.libpetri.doclet.PetriNetTaglet.resolveReference`\n *\n * @module doclet/net-resolver\n */\n\nimport { resolve, dirname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { PetriNet } from '../core/petri-net.js';\nimport type { SubnetDef } from '../core/subnet-def.js';\nimport type { Instance } from '../core/instance.js';\n\n/**\n * Discriminated-union result from {@link resolveNet}. Each variant carries\n * the resolved value plus a display title.\n */\nexport type ResolvedNet =\n | { readonly kind: 'net'; readonly value: PetriNet; readonly title: string }\n | { readonly kind: 'subnet'; readonly value: SubnetDef<unknown>; readonly title: string }\n | { readonly kind: 'instance'; readonly value: Instance<unknown>; readonly title: string };\n\n/**\n * Parses a `@petrinet` / `@subnet` reference and resolves it via dynamic\n * import. Returns `null` when the reference cannot be parsed or imported, or\n * when the resolved value is none of the three supported kinds.\n *\n * @param reference - the tag content, e.g. `./definition#buildDebugNet()`\n * @param sourceFilePath - absolute path to the source file containing the tag\n */\nexport async function resolveNet(\n reference: string,\n sourceFilePath: string,\n): Promise<ResolvedNet | null> {\n const trimmed = reference.trim().split(/\\s+/)[0] ?? '';\n if (!trimmed) return null;\n\n const hashIndex = trimmed.indexOf('#');\n if (hashIndex === -1) return null;\n\n const modulePath = trimmed.substring(0, hashIndex);\n const exportRef = trimmed.substring(hashIndex + 1);\n if (!exportRef) return null;\n\n const isCall = exportRef.endsWith('()');\n const exportName = isCall ? exportRef.slice(0, -2) : exportRef;\n\n // Resolve module path relative to the source file.\n let absolutePath: string;\n if (modulePath) {\n absolutePath = resolve(dirname(sourceFilePath), modulePath);\n } else {\n // `#localExport` — resolve from same file's compiled output.\n absolutePath = sourceFilePath;\n }\n\n // Try `.js` extension for compiled output, then `.ts` for ts-node scenarios.\n const candidates = [\n absolutePath,\n absolutePath + '.js',\n absolutePath + '.ts',\n absolutePath.replace(/\\.ts$/, '.js'),\n ];\n\n let mod: Record<string, unknown> | undefined;\n for (const candidate of candidates) {\n try {\n mod = await import(pathToFileURL(candidate).href) as Record<string, unknown>;\n break;\n } catch {\n // Try next candidate.\n }\n }\n\n if (!mod) return null;\n\n const exported = mod[exportName];\n if (exported == null) return null;\n\n let value: unknown;\n if (isCall) {\n if (typeof exported !== 'function') return null;\n const result = (exported as (...args: unknown[]) => unknown)();\n // Support functions that return `{ net: PetriNet, ... }` for legacy\n // callers; otherwise pass the result through directly.\n if (\n result !== null && typeof result === 'object' && 'net' in result &&\n isPetriNetLike((result as { net: unknown }).net)\n ) {\n value = (result as { net: unknown }).net;\n } else {\n value = result;\n }\n } else {\n value = exported;\n }\n\n return classify(value);\n}\n\n/**\n * Wraps the resolved value into a {@link ResolvedNet} variant by structural\n * shape — checks {@link Instance} before {@link SubnetDef} before\n * {@link PetriNet} so the most specific match wins (Instance has both `def`\n * and `renamedBody`; SubnetDef has `iface` and `body`; PetriNet has only\n * `transitions` + `name`).\n *\n * Returns `null` when the value matches none of the supported shapes.\n */\nfunction classify(value: unknown): ResolvedNet | null {\n if (value === null || typeof value !== 'object') return null;\n\n if (isInstanceLike(value)) {\n const inst = value as Instance<unknown>;\n return { kind: 'instance', value: inst, title: `${inst.def.name} :: ${inst.prefix}` };\n }\n if (isSubnetDefLike(value)) {\n const def = value as SubnetDef<unknown>;\n return { kind: 'subnet', value: def, title: def.name };\n }\n if (isPetriNetLike(value)) {\n const net = value as PetriNet;\n return { kind: 'net', value: net, title: net.name };\n }\n return null;\n}\n\n/**\n * Structural test for {@link PetriNet}: has a string `name` and a `transitions`\n * Set. Avoids `instanceof` because the imported module may have re-bound the\n * class identity (TypeDoc's bundled output vs `dist/` import path).\n */\nfunction isPetriNetLike(value: unknown): value is PetriNet {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as { name?: unknown; transitions?: unknown };\n return typeof candidate.name === 'string' && candidate.transitions instanceof Set;\n}\n\n/**\n * Structural test for {@link SubnetDef}: has a string `name`, a {@link\n * PetriNet}-shaped `body`, and an `iface` carrying `ports` + `channels` maps.\n */\nfunction isSubnetDefLike(value: unknown): value is SubnetDef<unknown> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as { name?: unknown; body?: unknown; iface?: unknown };\n if (typeof candidate.name !== 'string') return false;\n if (!isPetriNetLike(candidate.body)) return false;\n const iface = candidate.iface as { ports?: unknown; channels?: unknown } | undefined;\n return iface !== undefined && iface !== null\n && iface.ports instanceof Map && iface.channels instanceof Map;\n}\n\n/**\n * Structural test for {@link Instance}: has a string `prefix`, a {@link\n * SubnetDef}-shaped `def`, and a {@link PetriNet}-shaped `renamedBody`.\n */\nfunction isInstanceLike(value: unknown): value is Instance<unknown> {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as { prefix?: unknown; def?: unknown; renamedBody?: unknown };\n return typeof candidate.prefix === 'string'\n && isSubnetDefLike(candidate.def)\n && isPetriNetLike(candidate.renamedBody);\n}\n","/**\n * Shared HTML renderer for Petri Net diagrams in TypeDoc.\n *\n * Generates consistent HTML markup that mounts the canonical\n * {@link https://libpetri.org | libpetri} viewer client-side. The viewer IIFE\n * bundle (Graphviz WASM inlined, offline-safe) is inlined into the generated\n * HTML, making the plugin fully self-contained with no external file\n * dependencies.\n *\n * Each diagram container carries its DOT source as a `data-dot` attribute. An\n * init snippet reads the attribute and calls\n * `window.LibpetriViewer.mount(dot, container, { chrome: true })` to render the\n * SVG and wire pan/zoom + cluster legend/filter chrome.\n *\n * Mirrors: `org.libpetri.doclet.DiagramRenderer`\n *\n * @module doclet/diagram-renderer\n */\n\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadResource(filename: string): string {\n return readFileSync(join(__dirname, 'resources', filename), 'utf-8');\n}\n\nlet inlineCss: string | undefined;\nlet inlineJs: string | undefined;\n\nfunction css(): string {\n return (inlineCss ??= loadResource('petrinet-diagrams.css'));\n}\n\nfunction js(): string {\n return (inlineJs ??= loadResource('petrinet-diagrams.js'));\n}\n\n/**\n * Escapes HTML special characters for safe embedding as text content.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/**\n * Escapes a string for safe inclusion in an HTML attribute value enclosed in\n * double quotes. Identical to {@link escapeHtml} plus single-quote escaping\n * (defence-in-depth even though we always emit double-quoted attributes).\n */\nfunction escapeAttr(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\n/**\n * Renders a diagram from a DOT source string. The viewer renders the SVG\n * client-side via Graphviz-WASM; no server-side SVG is emitted on the\n * embedded path.\n *\n * Inlines CSS and the viewer IIFE from bundled resources. The IIFE is guarded\n * by an idempotency check (`window._libpetriViewerInit`) so it only executes\n * once per page even when multiple `@petrinet` tags appear.\n *\n * @param title - optional title (null/undefined for no title)\n * @param dotSource - the DOT source code; embedded as `data-dot` and shown in\n * the collapsible \"View DOT Source\" block\n * @returns HTML markup with diagram controls\n */\nexport function renderSvg(\n title: string | null | undefined,\n dotSource: string,\n): string {\n return renderInternal(title ?? null, /* headerHtml */ null, dotSource);\n}\n\n/**\n * Renders a subnet- or instance-aware diagram. Identical to {@link renderSvg}\n * apart from an extra {@code headerHtml} block (the port/channel/params\n * badge bar) prepended above the diagram, and the CSS class\n * {@code subnet-diagram} on the wrapper.\n *\n * @param title diagram title (e.g. \"BoundedBuffer :: b1\")\n * @param headerHtml the HTML produced by {@link import('./subnet-header.js')}\n * @param dotSource the DOT source code\n * @returns HTML markup\n */\nexport function renderSubnetSvg(\n title: string | null | undefined,\n headerHtml: string,\n dotSource: string,\n): string {\n return renderInternal(title ?? null, headerHtml, dotSource);\n}\n\nlet diagramIdCounter = 0;\n\nfunction renderInternal(\n title: string | null,\n headerHtml: string | null,\n dotSource: string,\n): string {\n const titleHtml = title !== null ? `<h4>${escapeHtml(title)}</h4>\\n` : '';\n const summaryText = title !== null ? 'View DOT Source' : 'View Source';\n const diagramClass = headerHtml !== null ? 'petrinet-diagram subnet-diagram' : 'petrinet-diagram';\n const headerBlock = headerHtml !== null ? headerHtml : '';\n const dotAttr = escapeAttr(dotSource);\n const containerId = `libpetri-diagram-${++diagramIdCounter}`;\n\n return `<style>${css()}</style>\n<script>if(!window._libpetriViewerInit){window._libpetriViewerInit=true;\n${js()}\n}</script>\n<div class=\"${diagramClass}\">\n${titleHtml}${headerBlock}<div class=\"diagram-container\" id=\"${containerId}\" data-libpetri-diagram=\"true\" data-dot=\"${dotAttr}\"></div>\n<details>\n<summary>${summaryText}</summary>\n<pre><code>${escapeHtml(dotSource)}</code></pre>\n</details>\n</div>\n<script>(function(){function mount(){var el=document.getElementById(${JSON.stringify(containerId)});if(!el)return;if(el.dataset.libpetriMounted)return;if(!window.LibpetriViewer||typeof window.LibpetriViewer.mount!=='function'){return setTimeout(mount,30);}el.dataset.libpetriMounted='1';try{window.LibpetriViewer.mount(el.dataset.dot,el,{chrome:true});}catch(e){console.error('[libpetri] mount failed',e);}}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',mount);}else{mount();}})();</script>`;\n}\n","/**\n * HTML-fragment generator for the small \"subnet header\" badge bar that the\n * {@link import('./diagram-renderer.js')} layers above subnet diagrams.\n *\n * For a {@link import('../core/subnet-def.js').SubnetDef} the header lists\n * the subnet name, the parameter type (best-effort — TypeScript erases\n * generics at runtime so the slot is shown as a placeholder), and one badge\n * per port and channel — colour-coded by direction\n * (`interface-port-input` / `-output` / `-inout` CSS classes, plus\n * `interface-channel` for sync channels).\n *\n * For an {@link import('../core/instance.js').Instance} the header lists the\n * instance prefix, the originating definition's name, and the {@link\n * import('../core/instance.js').Instance#params} summary.\n *\n * Mirrors: `org.libpetri.doclet.SubnetHeader`\n *\n * @module doclet/subnet-header\n */\n\nimport type { Instance } from '../core/instance.js';\nimport type { Interface, Port } from '../core/interface.js';\nimport type { SubnetDef } from '../core/subnet-def.js';\nimport { escapeHtml } from './diagram-renderer.js';\n\n/**\n * Builds the header HTML for a {@link SubnetDef} field.\n *\n * @param def the subnet definition\n * @param interfaceOnly when `true`, the header carries the \"(interface only)\"\n * badge so the reader knows the body is intentionally\n * omitted\n */\nexport function forSubnetDef(def: SubnetDef<unknown>, interfaceOnly: boolean): string {\n const sb: string[] = [];\n sb.push(`<div class=\"subnet-header\">\\n`);\n sb.push(` <div class=\"subnet-header-row\">\\n`);\n sb.push(` <span class=\"subnet-header-label\">subnet</span>\\n`);\n sb.push(` <span class=\"subnet-header-name\">${escapeHtml(def.name)}</span>\\n`);\n // TypeScript erases generics at runtime, so the parameter-type slot is\n // populated with a placeholder (\"?\"). Java emits the simple class name —\n // the structural HTML is otherwise byte-equivalent.\n sb.push(` <span class=\"subnet-header-param\">&lt;?&gt;</span>\\n`);\n if (interfaceOnly) {\n sb.push(` <span class=\"subnet-header-badge subnet-header-badge-interface\">interface only</span>\\n`);\n }\n sb.push(` </div>\\n`);\n sb.push(renderPortChannelBadges(def.iface));\n sb.push(`</div>\\n`);\n return sb.join('');\n}\n\n/**\n * Builds the header HTML for an {@link Instance} field.\n */\nexport function forInstance(instance: Instance<unknown>): string {\n const def = instance.def;\n const sb: string[] = [];\n sb.push(`<div class=\"subnet-header\">\\n`);\n sb.push(` <div class=\"subnet-header-row\">\\n`);\n sb.push(` <span class=\"subnet-header-label\">instance</span>\\n`);\n sb.push(` <span class=\"subnet-header-name\">${escapeHtml(instance.prefix)}</span>\\n`);\n sb.push(` <span class=\"subnet-header-of\">of</span>\\n`);\n sb.push(` <span class=\"subnet-header-name\">${escapeHtml(def.name)}</span>\\n`);\n const params = instance.params;\n if (params !== null && params !== undefined) {\n sb.push(` <span class=\"subnet-header-params\">params=${escapeHtml(String(params))}</span>\\n`);\n }\n sb.push(` </div>\\n`);\n sb.push(renderPortChannelBadges(def.iface));\n sb.push(`</div>\\n`);\n return sb.join('');\n}\n\n/**\n * Renders the per-port and per-channel badge row. Each badge carries a CSS\n * class identifying its kind so the stylesheet can colour-code by direction.\n */\nfunction renderPortChannelBadges(iface: Interface): string {\n if (iface.ports.size === 0 && iface.channels.size === 0) {\n return '';\n }\n const sb: string[] = [];\n sb.push(` <div class=\"subnet-header-badges\">\\n`);\n for (const port of iface.ports.values()) {\n const kind = portKind(port);\n sb.push(` <span class=\"interface-port-badge interface-port-badge-${kind}\">`);\n sb.push(`<span class=\"badge-direction\">${kind}</span>`);\n sb.push(`<span class=\"badge-name\">${escapeHtml(port.name)}</span>`);\n // TypeScript erases generics so we can't print the runtime token type;\n // omit the type-name span rather than emit a bogus value.\n sb.push(`</span>\\n`);\n }\n for (const channel of iface.channels.values()) {\n sb.push(` <span class=\"interface-channel-badge\">`);\n sb.push(`<span class=\"badge-direction\">channel</span>`);\n sb.push(`<span class=\"badge-name\">${escapeHtml(channel.name)}</span>`);\n sb.push(`</span>\\n`);\n }\n sb.push(` </div>\\n`);\n return sb.join('');\n}\n\nfunction portKind(port: Port<unknown>): 'input' | 'output' | 'inout' {\n return port.direction;\n}\n","/**\n * DOT generators for the subnet-aware diagrams the {@link\n * import('./petri-net-plugin.js')} plugin draws.\n *\n * Two flavours:\n * - {@link fullBody} — the subnet's complete body, but with its interface-port\n * places restyled with the `interface-port` category from\n * `petri-net-styles.json` so they read as boundary elements at a glance. A\n * small `subgraph cluster_iface_*` with label `\"interface\"` surrounds the\n * port places and channel transitions to advertise the subnet boundary\n * visually.\n * - {@link interfaceOnly} — a tiny diagram of just the port places, the\n * channel transitions, and a single stub node indicating the body is\n * omitted. Used by the `@subnet` block tag.\n *\n * This module stays in the doclet directory (rather than living in\n * `export/`) because it is a doclet-presentation concern — the production\n * export pipeline never adds the `interface` cluster.\n *\n * Mirrors: `org.libpetri.doclet.SubnetDotExport`\n *\n * @module doclet/subnet-dot-export\n */\n\nimport type { SubnetDef } from '../core/subnet-def.js';\nimport type { Interface, Port, Channel } from '../core/interface.js';\nimport { dotExport } from '../export/dot-exporter.js';\nimport { sanitize } from '../export/petri-net-mapper.js';\nimport { nodeStyle, FONT } from '../export/styles.js';\n\n/**\n * Renders the full body of a {@link SubnetDef} with the interface ports\n * restyled. The output is a post-processed version of {@link dotExport}: the\n * port-place node attribute lines are rewritten so the post-processor swaps in\n * the {@code interface-port} category, channel-transition lines swap in\n * {@code sync-channel}, and an extra {@code subgraph cluster_iface_<name>}\n * block is appended that lists the port nodes by id — overriding their\n * cluster membership without re-emitting them (DOT semantics: a node\n * referenced inside a subgraph block by id alone is treated as a member of\n * that cluster).\n */\nexport function fullBody(def: SubnetDef<unknown>): string {\n let dot = dotExport(def.body);\n const portIds = portNodeIds(def.iface);\n const channelIds = channelNodeIds(def.iface);\n\n // Restyle: rewrite the matched node lines so the post-processor swaps in\n // the interface-port and sync-channel categories. The renderer emits each\n // node as one line ending with `];` so a per-id rewrite is sufficient and\n // deterministic.\n for (const portId of portIds) {\n dot = restyleNode(dot, portId, nodeStyle('interface-port'));\n }\n for (const chId of channelIds) {\n dot = restyleNode(dot, chId, nodeStyle('sync-channel'));\n }\n\n // Wrap the port and channel ids in a header cluster so the doclet's viewer\n // can render the boundary distinctly. Done by injecting an extra subgraph\n // block before the closing `}` of the digraph.\n if (portIds.size > 0 || channelIds.size > 0) {\n let iface = '';\n iface += ` subgraph cluster_iface_${sanitize(def.name)} {\\n`;\n iface += ` label=\"interface\";\\n`;\n iface += ` style=\"rounded,dashed\";\\n`;\n iface += ` bgcolor=\"#F0F6FF\";\\n`;\n iface += ` penwidth=2;\\n`;\n for (const id of portIds) {\n iface += ` ${id};\\n`;\n }\n for (const id of channelIds) {\n iface += ` ${id};\\n`;\n }\n iface += ` }\\n`;\n\n const lastBrace = dot.lastIndexOf('}');\n if (lastBrace > 0) {\n dot = dot.substring(0, lastBrace) + iface + dot.substring(lastBrace);\n }\n }\n return dot;\n}\n\n/**\n * Renders an interface-only diagram: just the port places + channel\n * transitions + a single stub node labelled \"(internals omitted)\" so the\n * reader knows the body has not been drawn.\n */\nexport function interfaceOnly(def: SubnetDef<unknown>): string {\n const sb: string[] = [];\n sb.push(`digraph ${sanitize(def.name)} {\\n`);\n sb.push(` rankdir=LR;\\n`);\n sb.push(` nodesep=0.5;\\n`);\n sb.push(` ranksep=0.6;\\n`);\n sb.push(` fontname=\"${FONT.family}\";\\n`);\n sb.push(` node [fontname=\"${FONT.family}\", fontsize=12];\\n`);\n sb.push(` edge [fontname=\"${FONT.family}\", fontsize=10];\\n`);\n sb.push('\\n');\n\n const stubId = 'stub_' + sanitize(def.name);\n\n // Group ports by direction: inputs on the left, outputs on the right,\n // inouts above. This keeps the interface diagram visually structured even\n // when the body internals are absent.\n const inputs: Port<unknown>[] = [];\n const outputs: Port<unknown>[] = [];\n const inouts: Port<unknown>[] = [];\n for (const port of def.iface.ports.values()) {\n switch (port.direction) {\n case 'input': inputs.push(port); break;\n case 'output': outputs.push(port); break;\n case 'inout': inouts.push(port); break;\n }\n }\n\n // Emit port nodes.\n for (const port of inputs) emitPortNode(sb, port, 'input');\n for (const port of outputs) emitPortNode(sb, port, 'output');\n for (const port of inouts) emitPortNode(sb, port, 'inout');\n\n // Emit channel nodes.\n const syncStyle = nodeStyle('sync-channel');\n for (const ch of def.iface.channels.values()) {\n sb.push(` ${channelNodeId(ch)} [`);\n sb.push(`label=\"⇄ ${escapeLabel(ch.name)}\", `);\n sb.push(`shape=${syncStyle.shape}, `);\n sb.push(`style=\"filled\", `);\n sb.push(`fillcolor=\"${syncStyle.fill}\", `);\n sb.push(`color=\"${syncStyle.stroke}\", `);\n sb.push(`penwidth=${trimNumber(syncStyle.penwidth)}`);\n sb.push(`];\\n`);\n }\n\n // Emit stub node.\n sb.push(` ${stubId} [`);\n sb.push(`label=\"${escapeLabel(def.name)}\\\\n(internals omitted)\", `);\n sb.push(`shape=box, style=\"filled,dashed,rounded\", `);\n sb.push(`fillcolor=\"#FFFFFF\", color=\"#666666\", penwidth=1, `);\n sb.push(`fontsize=11`);\n sb.push(`];\\n`);\n sb.push('\\n');\n\n // Connect inputs -> stub -> outputs; inouts and channels link both ways.\n for (const port of inputs) {\n sb.push(` ${portNodeId(port)} -> ${stubId} [color=\"#1d4ed8\", arrowhead=normal];\\n`);\n }\n for (const port of outputs) {\n sb.push(` ${stubId} -> ${portNodeId(port)} [color=\"#1d4ed8\", arrowhead=normal];\\n`);\n }\n for (const port of inouts) {\n sb.push(` ${portNodeId(port)} -> ${stubId} [color=\"#1d4ed8\", arrowhead=normalnormal, dir=both];\\n`);\n }\n for (const ch of def.iface.channels.values()) {\n sb.push(` ${channelNodeId(ch)} -> ${stubId} [color=\"#6d28d9\", arrowhead=normal, style=dashed];\\n`);\n }\n\n sb.push(`}\\n`);\n return sb.join('');\n}\n\n// ============================================================\n// Helpers\n// ============================================================\n\nfunction emitPortNode(sb: string[], port: Port<unknown>, direction: string): void {\n const style = nodeStyle('interface-port');\n // Token type isn't carried at runtime in TypeScript (generics erased), so\n // the xlabel is the direction marker only — Java additionally appends the\n // port's tokenType simple name. The structural content is otherwise\n // identical.\n sb.push(` ${portNodeId(port)} [`);\n sb.push(`label=\"${escapeLabel(port.name)}\", `);\n sb.push(`xlabel=\"${escapeLabel(direction)}\", `);\n sb.push(`shape=${style.shape}, `);\n sb.push(`style=\"filled,${style.style ?? 'solid'}\", `);\n sb.push(`fillcolor=\"${style.fill}\", `);\n sb.push(`color=\"${style.stroke}\", `);\n sb.push(`penwidth=${trimNumber(style.penwidth)}`);\n if (style.width !== undefined) {\n sb.push(`, width=${trimNumber(style.width)}, fixedsize=true`);\n }\n sb.push(`];\\n`);\n}\n\nfunction portNodeId(port: Port<unknown>): string {\n return 'p_' + sanitize(port.place.name);\n}\n\nfunction channelNodeId(ch: Channel): string {\n return 't_' + sanitize(ch.transition.name);\n}\n\nfunction portNodeIds(iface: Interface): Set<string> {\n const ids = new Set<string>();\n for (const port of iface.ports.values()) ids.add(portNodeId(port));\n return ids;\n}\n\nfunction channelNodeIds(iface: Interface): Set<string> {\n const ids = new Set<string>();\n for (const ch of iface.channels.values()) ids.add(channelNodeId(ch));\n return ids;\n}\n\n/**\n * Rewrites the {@code fillcolor=}, {@code color=}, {@code style=}, and\n * {@code penwidth=} attributes of the line declaring the given node id so\n * the supplied style takes precedence. Operates as a plain text rewrite:\n * the {@link import('../export/dot-renderer.js').renderDot} output formats\n * each node line deterministically, so a per-attribute regex is reliable.\n */\nfunction restyleNode(\n dot: string,\n nodeId: string,\n style: ReturnType<typeof nodeStyle>,\n): string {\n // The node line begins with: <id> [\n // and ends with: ];\n // Find the line, then surgically swap fillcolor / color / style / penwidth.\n const marker = nodeId + ' [';\n let start = dot.indexOf('\\n ' + marker);\n if (start < 0) start = dot.indexOf(' ' + marker);\n if (start < 0) return dot;\n // Skip the leading newline if present so we operate on the line proper.\n if (dot.charAt(start) === '\\n') start += 1;\n const lineEnd = dot.indexOf('];', start);\n if (lineEnd < 0) return dot;\n let line = dot.substring(start, lineEnd + 2);\n\n // Order matters: keep `fillcolor` before `color` so the substring search\n // for `color=\"` doesn't accidentally hit `fillcolor=\"`.\n line = replaceAttr(line, 'fillcolor', style.fill);\n line = replaceAttr(line, 'color', style.stroke);\n line = replaceAttr(line, 'penwidth', trimNumber(style.penwidth));\n if (style.style !== undefined) {\n // Compose with `filled` because the renderer already includes that.\n line = replaceAttr(line, 'style', `\"filled,${style.style}\"`);\n }\n\n return dot.substring(0, start) + line + dot.substring(lineEnd + 2);\n}\n\n/**\n * Replaces the value of a {@code key=value} pair within a single DOT\n * attribute list. Quotes the value when the existing form was quoted, leaves\n * the value unquoted when the existing form was unquoted (numeric).\n */\nfunction replaceAttr(line: string, key: string, value: string): string {\n // Match key=\"...\" (quoted) — the key must be preceded by a non-word char\n // (start, comma, or space) so `color=` doesn't match inside `fillcolor=`.\n const quotedPat = key + '=\"';\n const qStart = findAttrStart(line, quotedPat);\n if (qStart >= 0) {\n const valStart = qStart + quotedPat.length;\n const valEnd = line.indexOf('\"', valStart);\n if (valEnd < 0) return line;\n let v = value;\n if (v.startsWith('\"') && v.endsWith('\"') && v.length >= 2) {\n v = v.substring(1, v.length - 1);\n }\n return line.substring(0, valStart) + v + line.substring(valEnd);\n }\n // Match key=NN (numeric, ends at ',' or ']').\n const unquoted = key + '=';\n const uStart = findAttrStart(line, unquoted);\n if (uStart < 0) return line;\n const valStart = uStart + unquoted.length;\n let valEnd = valStart;\n while (valEnd < line.length) {\n const c = line.charAt(valEnd);\n if (c === ',' || c === ']') break;\n valEnd++;\n }\n return line.substring(0, valStart) + value + line.substring(valEnd);\n}\n\n/**\n * Finds the start of a `key=` attribute pattern in `line`, ensuring the\n * match is not a suffix of another attribute name (e.g. `color=` must not\n * match inside `fillcolor=`). Returns -1 when no boundary-respecting match\n * exists.\n */\nfunction findAttrStart(line: string, pattern: string): number {\n let from = 0;\n while (from < line.length) {\n const idx = line.indexOf(pattern, from);\n if (idx < 0) return -1;\n if (idx === 0) return idx;\n const prev = line.charAt(idx - 1);\n // Word-boundary check: previous char must not be alphanumeric/underscore.\n if (!/[A-Za-z0-9_]/.test(prev)) return idx;\n from = idx + 1;\n }\n return -1;\n}\n\nfunction trimNumber(d: number): string {\n if (Number.isInteger(d)) return String(d);\n return String(d);\n}\n\nfunction escapeLabel(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n","/**\n * SVG renderer using `@viz-js/viz` (Graphviz WASM).\n *\n * Pure WASM — zero external dependencies. No system `dot` binary required.\n *\n * Mirrors: Java's `PetriNetTaglet.dotToSvg()` (which uses `dot -Tsvg` subprocess)\n *\n * @module doclet/svg-renderer\n */\n\n/**\n * Renders a DOT string to SVG using the `@viz-js/viz` WASM engine.\n *\n * Strips the XML prolog/DOCTYPE and explicit width/height attributes so the SVG\n * scales via viewBox + CSS instead of overriding with fixed pt sizes.\n *\n * @param dot - the DOT source string\n * @returns SVG markup string\n * @throws if `@viz-js/viz` is not installed or DOT parsing fails\n */\nexport async function dotToSvg(dot: string): Promise<string> {\n // Dynamic import — @viz-js/viz is an optional peer dependency\n const { instance } = await import('@viz-js/viz');\n const viz = await instance();\n let svg = viz.renderString(dot, { format: 'svg', engine: 'dot' });\n\n // Strip XML prolog and DOCTYPE — invalid inside HTML5\n const svgStart = svg.indexOf('<svg');\n if (svgStart > 0) svg = svg.substring(svgStart);\n\n // Strip explicit width/height attributes (e.g. \"1942pt\") so the SVG\n // scales via viewBox + CSS instead of overriding with fixed pt sizes\n svg = svg.replace(/\\s+(?:width|height)=\"[^\"]*\"/g, '');\n\n return svg;\n}\n"],"mappings":"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","names":["dirname","interfaceOnly","valStart","valEnd","interfaceOnly","dotExport"]}
@@ -1,15 +1,62 @@
1
+ /* ============================================================
2
+ * Canonical libpetri Petri-net diagram viewer stylesheet.
3
+ *
4
+ * Source of truth: typescript/src/viewer/resources/viewer.css.
5
+ * This file is the union of the old petrinet-diagrams.css (Javadoc taglet)
6
+ * and the cluster-overlay rules from debug-ui/src/styles.css. It is
7
+ * shipped as `petrinet-diagrams.css` to all consumers via the build
8
+ * script (scripts/build-viewer.sh). Edit HERE, not in the consumer dirs.
9
+ *
10
+ * All visual constants surface as CSS custom properties (`--lpv-*`) so
11
+ * individual consumers can theme without forking. Defaults match the
12
+ * doclet palette + the user's "clusters must be visually obvious"
13
+ * feedback (deeper tint, thicker stroke, more prominent dash).
14
+ * ============================================================ */
15
+
16
+ :root,
17
+ .petrinet-diagram,
18
+ .libpetri-viewer {
19
+ --lpv-bg: #fafafa;
20
+ --lpv-header-bg: #f0f0f0;
21
+ --lpv-border: #ddd;
22
+ --lpv-text: #111827;
23
+ --lpv-muted: #6b7280;
24
+
25
+ /* Cluster visuals — user feedback: clusters must be visually obvious.
26
+ Bumped the cluster background from a near-white tint to a saturated
27
+ indigo wash, thickened the stroke, and lengthened the dashes. */
28
+ --lpv-cluster-bg: #eef2ff;
29
+ --lpv-cluster-bg-collapsed: #cbd5e1;
30
+ --lpv-cluster-stroke-width: 2.2;
31
+ --lpv-cluster-stroke-dash: 6 3;
32
+ --lpv-cluster-fill-tint: rgba(99, 102, 241, 0.08);
33
+
34
+ --lpv-dim-opacity: 0.2;
35
+ --lpv-active-filter-outline: rgba(245, 158, 11, 0.4);
36
+
37
+ --lpv-legend-bg: rgba(255, 255, 255, 0.95);
38
+ --lpv-chip-bg: #ffffff;
39
+ --lpv-chip-active-bg: #111827;
40
+ }
41
+
42
+ /* ============================================================
43
+ * Top-level diagram container
44
+ * Kept for backward compatibility with the existing Javadoc/Rustdoc
45
+ * taglet markup. Live consumers (debug-ui, dev-preview) don't use the
46
+ * outer .petrinet-diagram chrome; their styling lives in debug-ui CSS.
47
+ * ============================================================ */
1
48
  .petrinet-diagram {
2
49
  margin: 1.5em 0;
3
- border: 1px solid #ddd;
50
+ border: 1px solid var(--lpv-border);
4
51
  border-radius: 8px;
5
- background: #fafafa;
52
+ background: var(--lpv-bg);
6
53
  }
7
54
 
8
55
  .petrinet-diagram h4 {
9
56
  margin: 0;
10
57
  padding: 12px 16px;
11
- background: #f0f0f0;
12
- border-bottom: 1px solid #ddd;
58
+ background: var(--lpv-header-bg);
59
+ border-bottom: 1px solid var(--lpv-border);
13
60
  border-radius: 8px 8px 0 0;
14
61
  }
15
62
 
@@ -36,7 +83,35 @@
36
83
  height: 100%;
37
84
  }
38
85
 
39
- .petrinet-diagram .diagram-controls {
86
+ /* 2.0 mount target — the host div emitted by the Java/Rust/TS doclet
87
+ taglets. Needs the same sizing constraints as the legacy
88
+ .diagram-container, otherwise the host grows to the Graphviz-emitted
89
+ SVG intrinsic size and produces a 9000+ px tall column. With this
90
+ rule the SVG fills the constrained host and viewBox auto-fits. */
91
+ .petrinet-diagram-viewer {
92
+ display: block;
93
+ position: relative;
94
+ width: 100%;
95
+ height: 70vh;
96
+ min-height: 400px;
97
+ max-height: 700px;
98
+ overflow: hidden;
99
+ background: var(--lpv-bg);
100
+ border: 1px solid var(--lpv-border);
101
+ border-radius: 4px;
102
+ }
103
+
104
+ /* Override Graphviz's explicit pt width/height on the rendered SVG.
105
+ Without this the SVG retains its intrinsic 5021pt × 7203pt size,
106
+ defeating the host height constraint. */
107
+ .petrinet-diagram-viewer > svg {
108
+ display: block;
109
+ width: 100% !important;
110
+ height: 100% !important;
111
+ }
112
+
113
+ .petrinet-diagram .diagram-controls,
114
+ .libpetri-viewer .diagram-controls {
40
115
  position: absolute;
41
116
  top: 8px;
42
117
  right: 8px;
@@ -45,7 +120,8 @@
45
120
  z-index: 10;
46
121
  }
47
122
 
48
- .petrinet-diagram .diagram-btn {
123
+ .petrinet-diagram .diagram-btn,
124
+ .libpetri-viewer .diagram-btn {
49
125
  padding: 6px 10px;
50
126
  border: 1px solid #ccc;
51
127
  border-radius: 4px;
@@ -55,7 +131,8 @@
55
131
  min-width: 32px;
56
132
  }
57
133
 
58
- .petrinet-diagram .diagram-btn:hover {
134
+ .petrinet-diagram .diagram-btn:hover,
135
+ .libpetri-viewer .diagram-btn:hover {
59
136
  background: #e8e8e8;
60
137
  }
61
138
 
@@ -107,6 +184,15 @@
107
184
  max-height: none;
108
185
  }
109
186
 
187
+ /* 2.0 host is the mount target itself (no .diagram-container wrapper),
188
+ so size the direct child SVG to fill the viewport in fullscreen. */
189
+ .libpetri-viewer.diagram-fullscreen > svg {
190
+ flex: 1;
191
+ width: 100%;
192
+ height: 100%;
193
+ min-height: 0;
194
+ }
195
+
110
196
  /* Zoom indicator */
111
197
  .zoom-indicator {
112
198
  position: absolute;
@@ -124,3 +210,294 @@
124
210
  .zoom-indicator.visible {
125
211
  opacity: 1;
126
212
  }
213
+
214
+ /* ============================================================
215
+ * Subnet header (badges row above the diagram)
216
+ * ============================================================ */
217
+ .petrinet-diagram .subnet-header {
218
+ padding: 12px 16px;
219
+ border-bottom: 1px solid #e5e7eb;
220
+ background: #f8fafc;
221
+ }
222
+
223
+ .subnet-header-row {
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 8px;
227
+ font-size: 14px;
228
+ flex-wrap: wrap;
229
+ }
230
+
231
+ .subnet-header-label {
232
+ font-weight: 600;
233
+ color: var(--lpv-muted);
234
+ text-transform: uppercase;
235
+ font-size: 11px;
236
+ letter-spacing: 0.05em;
237
+ }
238
+
239
+ .subnet-header-name {
240
+ font-weight: 600;
241
+ color: var(--lpv-text);
242
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
243
+ }
244
+
245
+ .subnet-header-of {
246
+ color: var(--lpv-muted);
247
+ font-size: 12px;
248
+ }
249
+
250
+ .subnet-header-param,
251
+ .subnet-header-params {
252
+ color: #6d28d9;
253
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
254
+ font-size: 12px;
255
+ }
256
+
257
+ .subnet-header-params-null {
258
+ color: #9ca3af;
259
+ font-style: italic;
260
+ }
261
+
262
+ .subnet-header-badge {
263
+ padding: 2px 8px;
264
+ border-radius: 4px;
265
+ background: #fef3c7;
266
+ color: #92400e;
267
+ font-size: 11px;
268
+ font-weight: 500;
269
+ }
270
+
271
+ .subnet-header-badge-interface {
272
+ background: #e0e7ff;
273
+ color: #3730a3;
274
+ }
275
+
276
+ .subnet-header-badges {
277
+ display: flex;
278
+ gap: 6px;
279
+ margin-top: 8px;
280
+ flex-wrap: wrap;
281
+ }
282
+
283
+ .interface-port-badge,
284
+ .interface-channel-badge {
285
+ display: inline-flex;
286
+ align-items: center;
287
+ gap: 4px;
288
+ padding: 3px 8px;
289
+ border-radius: 12px;
290
+ font-size: 11px;
291
+ border: 1px solid;
292
+ background: white;
293
+ }
294
+
295
+ .interface-port-badge .badge-direction,
296
+ .interface-channel-badge .badge-direction {
297
+ font-weight: 600;
298
+ text-transform: uppercase;
299
+ font-size: 10px;
300
+ letter-spacing: 0.04em;
301
+ }
302
+
303
+ .interface-port-badge .badge-name,
304
+ .interface-channel-badge .badge-name {
305
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
306
+ font-weight: 500;
307
+ }
308
+
309
+ .interface-port-badge .badge-type {
310
+ color: var(--lpv-muted);
311
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
312
+ font-size: 10px;
313
+ }
314
+
315
+ .interface-port-badge-input { border-color: #1d4ed8; color: #1d4ed8; background: #e7f1ff; }
316
+ .interface-port-badge-output { border-color: #047857; color: #047857; background: #d1fae5; }
317
+ .interface-port-badge-inout { border-color: #b45309; color: #b45309; background: #fef3c7; }
318
+ .interface-channel-badge { border-color: #6d28d9; color: #6d28d9; background: #f3e8ff; }
319
+
320
+ /* ============================================================
321
+ * Cluster legend sidebar
322
+ * ============================================================ */
323
+ .petrinet-diagram .diagram-legend,
324
+ .libpetri-viewer .diagram-legend {
325
+ position: absolute;
326
+ top: 8px;
327
+ left: 8px;
328
+ background: var(--lpv-legend-bg);
329
+ border: 1px solid #e5e7eb;
330
+ border-radius: 6px;
331
+ padding: 8px;
332
+ font-size: 12px;
333
+ z-index: 9;
334
+ max-height: calc(100% - 16px);
335
+ overflow-y: auto;
336
+ min-width: 160px;
337
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
338
+ }
339
+
340
+ .diagram-legend .legend-title {
341
+ font-weight: 600;
342
+ color: var(--lpv-muted);
343
+ text-transform: uppercase;
344
+ font-size: 10px;
345
+ letter-spacing: 0.05em;
346
+ margin-bottom: 6px;
347
+ }
348
+
349
+ .diagram-legend .legend-row {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 6px;
353
+ padding: 4px 6px;
354
+ border-radius: 4px;
355
+ cursor: pointer;
356
+ transition: background 0.15s;
357
+ }
358
+
359
+ .diagram-legend .legend-row:hover {
360
+ background: #f3f4f6;
361
+ }
362
+
363
+ .diagram-legend .legend-ribbon {
364
+ width: 4px;
365
+ height: 16px;
366
+ border-radius: 2px;
367
+ flex: 0 0 auto;
368
+ }
369
+
370
+ .diagram-legend .legend-label {
371
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
372
+ flex: 1 1 auto;
373
+ color: var(--lpv-text);
374
+ }
375
+
376
+ .diagram-legend .legend-count {
377
+ color: #9ca3af;
378
+ font-size: 10px;
379
+ }
380
+
381
+ /* ============================================================
382
+ * Filter chips
383
+ * ============================================================ */
384
+ .petrinet-diagram .diagram-filter-strip,
385
+ .libpetri-viewer .diagram-filter-strip {
386
+ position: absolute;
387
+ bottom: 8px;
388
+ left: 8px;
389
+ right: 8px;
390
+ display: flex;
391
+ flex-wrap: wrap;
392
+ align-items: center;
393
+ gap: 6px;
394
+ padding: 8px;
395
+ background: var(--lpv-legend-bg);
396
+ border: 1px solid #e5e7eb;
397
+ border-radius: 6px;
398
+ z-index: 8;
399
+ box-shadow: 0 1px 3px rgba(0,0,0,0.05);
400
+ }
401
+
402
+ .filter-strip-label {
403
+ font-size: 11px;
404
+ font-weight: 600;
405
+ color: var(--lpv-muted);
406
+ text-transform: uppercase;
407
+ letter-spacing: 0.04em;
408
+ }
409
+
410
+ .filter-chip {
411
+ display: inline-flex;
412
+ align-items: center;
413
+ gap: 4px;
414
+ padding: 3px 8px;
415
+ border-radius: 14px;
416
+ font-size: 11px;
417
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
418
+ background: var(--lpv-chip-bg);
419
+ border: 1px solid #d1d5db;
420
+ cursor: pointer;
421
+ transition: background 0.15s, transform 0.15s;
422
+ }
423
+
424
+ .filter-chip:hover {
425
+ background: #f3f4f6;
426
+ }
427
+
428
+ .filter-chip-active {
429
+ background: var(--lpv-chip-active-bg);
430
+ color: white;
431
+ border-color: var(--lpv-chip-active-bg);
432
+ }
433
+
434
+ .filter-chip-active .chip-dot {
435
+ outline: 2px solid rgba(255, 255, 255, 0.4);
436
+ }
437
+
438
+ .filter-chip .chip-dot {
439
+ width: 8px;
440
+ height: 8px;
441
+ border-radius: 50%;
442
+ display: inline-block;
443
+ }
444
+
445
+ /* ============================================================
446
+ * Cluster visual baseline — make clusters visually obvious
447
+ *
448
+ * These rules apply globally to any rendered SVG that lives inside
449
+ * a `.petrinet-diagram` OR a `.libpetri-viewer` host. Tinted fill,
450
+ * thicker stroke, more prominent dash. Stroke colour is injected
451
+ * inline by the cluster-overlay (deterministic HSL per prefix).
452
+ * ============================================================ */
453
+ g.cluster > polygon,
454
+ g.cluster > path {
455
+ fill: var(--lpv-cluster-bg);
456
+ stroke-width: var(--lpv-cluster-stroke-width);
457
+ stroke-dasharray: var(--lpv-cluster-stroke-dash);
458
+ transition: fill 0.2s ease, stroke 0.2s ease;
459
+ }
460
+
461
+ /* ============================================================
462
+ * Cluster collapse / expand state
463
+ * ============================================================ */
464
+ .cluster-collapsed > polygon,
465
+ .cluster-collapsed > path {
466
+ fill: var(--lpv-cluster-bg-collapsed) !important;
467
+ stroke-dasharray: 3 3;
468
+ }
469
+
470
+ .cluster-collapsed-badge {
471
+ font-style: italic;
472
+ pointer-events: none;
473
+ }
474
+
475
+ /* Sibling nodes/edges hidden while their cluster is collapsed. The cluster
476
+ * <g> retains its layout box (border, label, badge); the interior elements
477
+ * disappear so the box reads as a single tile. */
478
+ g.node.petri-collapsed-inside,
479
+ g.edge.petri-collapsed-inside {
480
+ display: none;
481
+ }
482
+
483
+ /* ============================================================
484
+ * Filter dim state (CSS-driven, applied per node by JS)
485
+ * ============================================================ */
486
+ g.node.petri-dimmed,
487
+ g.edge.petri-dimmed {
488
+ opacity: var(--lpv-dim-opacity);
489
+ transition: opacity 0.2s ease;
490
+ }
491
+
492
+ g.node:not(.petri-dimmed),
493
+ g.edge:not(.petri-dimmed) {
494
+ transition: opacity 0.2s ease;
495
+ }
496
+
497
+ /* When the filter is active, give the SVG a hairline ring so the user
498
+ has a global signal that they're seeing a filtered view, not a partial
499
+ net. Matches the debug-ui's previous treatment. */
500
+ svg.has-active-filter {
501
+ outline: 1px solid var(--lpv-active-filter-outline);
502
+ outline-offset: -1px;
503
+ }