@tumaet/apollon 4.7.0 → 4.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.
Files changed (73) hide show
  1. package/README.md +45 -20
  2. package/dist/assets/style.css +1 -1
  3. package/dist/export.d.ts +73 -0
  4. package/dist/export.js +219 -0
  5. package/dist/exportStyles-Co-WBqTa.js +5 -0
  6. package/dist/external/LICENSE-InterFont +92 -0
  7. package/dist/external/exportFonts-DxO9jrR1.js +19 -0
  8. package/dist/external/exportStyles-Co-WBqTa.js +5 -0
  9. package/dist/external/index.js +22191 -0
  10. package/dist/fontStack-DOtVH2j8.js +5 -0
  11. package/dist/index.d.ts +150 -3
  12. package/dist/index.js +19993 -19219
  13. package/dist/internals.d.ts +65 -1
  14. package/dist/internals.js +14 -12
  15. package/dist/react/apollon-editor.d.ts +56 -3
  16. package/dist/react/components/AlignmentGuides.d.ts +1 -1
  17. package/dist/react/components/CustomControls.d.ts +7 -0
  18. package/dist/react/components/DraggableGhost.d.ts +5 -0
  19. package/dist/react/components/popovers/PopoverManager.d.ts +1 -1
  20. package/dist/react/components/popovers/activityDiagram/ActivitySwimlaneEditPopover.d.ts +2 -0
  21. package/dist/react/components/popovers/activityDiagram/index.d.ts +1 -0
  22. package/dist/react/components/react/ApollonControl.d.ts +18 -0
  23. package/dist/react/components/svgs/nodes/InterfaceLabel.d.ts +26 -0
  24. package/dist/react/components/svgs/nodes/activityDiagram/ActivitySwimlaneSVG.d.ts +9 -0
  25. package/dist/react/components/svgs/nodes/activityDiagram/index.d.ts +1 -0
  26. package/dist/react/components/svgs/nodes/componentDiagram/ComponentInterfaceNodeSVG.d.ts +2 -0
  27. package/dist/react/components/svgs/nodes/deploymentDiagram/DeploymentInterfaceSVG.d.ts +2 -0
  28. package/dist/react/constants.d.ts +28 -0
  29. package/dist/react/edges/labelTypes/EdgeIncludeExtendLabel.d.ts +1 -4
  30. package/dist/react/edges/labelTypes/EdgeMiddleLabels.d.ts +7 -4
  31. package/dist/react/edges/labelTypes/EdgeMultipleLabels.d.ts +2 -3
  32. package/dist/react/edges/labelTypes/messageLayout.d.ts +51 -0
  33. package/dist/react/export/exportErrors.d.ts +10 -0
  34. package/dist/react/export/index.d.ts +13 -0
  35. package/dist/react/export/normalizeExportSvg.d.ts +16 -0
  36. package/dist/react/export/preProcessSvgForPdf.d.ts +20 -0
  37. package/dist/react/export/svgToPdf.d.ts +17 -0
  38. package/dist/react/export/svgToPng.d.ts +38 -0
  39. package/dist/react/exportStyles-yVX7Dykt.js +6 -0
  40. package/dist/react/hooks/index.d.ts +0 -1
  41. package/dist/react/hooks/useRemoteDraggingNodes.d.ts +20 -0
  42. package/dist/react/hooks/useStepPathEdge.d.ts +11 -0
  43. package/dist/react/index.d.ts +1 -0
  44. package/dist/react/nodes/activityDiagram/ActivitySwimlane.d.ts +3 -0
  45. package/dist/react/nodes/activityDiagram/index.d.ts +1 -0
  46. package/dist/react/nodes/types.d.ts +2 -1
  47. package/dist/react/overlay/OverlayLayer.d.ts +8 -0
  48. package/dist/react/overlay/RegionMount.d.ts +10 -0
  49. package/dist/react/overlay/overlayStore.d.ts +20 -0
  50. package/dist/react/overlay/types.d.ts +48 -0
  51. package/dist/react/react.d.ts +1 -0
  52. package/dist/react/react.js +13625 -18669
  53. package/dist/react/store/context.d.ts +3 -0
  54. package/dist/react/store/diagramStore.d.ts +17 -1
  55. package/dist/react/sync/perfCounters.d.ts +6 -0
  56. package/dist/react/sync/ydoc.d.ts +2 -0
  57. package/dist/react/sync/yjsSync.d.ts +3 -2
  58. package/dist/react/types/nodes/NodeProps.d.ts +24 -0
  59. package/dist/react/typings.d.ts +17 -0
  60. package/dist/react/utils/collaboration.d.ts +10 -1
  61. package/dist/react/utils/exportUtils.d.ts +0 -130
  62. package/dist/react/utils/geometry/edgeLabelLayout.d.ts +123 -0
  63. package/dist/react/utils/geometry/interfaceLabelLayout.d.ts +44 -0
  64. package/dist/react/utils/index.d.ts +1 -0
  65. package/dist/react/utils/paletteLayout.d.ts +70 -0
  66. package/dist/react/utils/swimlaneUtils.d.ts +58 -0
  67. package/dist/yjsSync-BCNFF_EI.js +45418 -0
  68. package/package.json +33 -8
  69. package/schema/uml-model-4.schema.json +1 -0
  70. package/dist/exportStyles-Xk-Vm7Ul.js +0 -5
  71. package/dist/react/exportStyles-DZCHk5mK.js +0 -6
  72. package/dist/react/hooks/useMessagePositioning.d.ts +0 -30
  73. package/dist/yjsSync-aPxjWNdZ.js +0 -50939
@@ -24,6 +24,21 @@ declare function ActivityMergeNode({ id, width, height, data, parentId, }: NodeP
24
24
 
25
25
  declare function ActivityObjectNode({ id, width, height, data, parentId, }: NodeProps<Node_2<DefaultNodeProps>>): JSX.Element | null;
26
26
 
27
+ declare function ActivitySwimlane({ id, width, height, data, parentId, }: NodeProps<Node_2<ActivitySwimlaneProps>>): JSX.Element | null;
28
+
29
+ /**
30
+ * Activity-partition (swimlane) container. Lanes are equal divisions along the
31
+ * primary axis (columns when vertical, rows when horizontal). Elements dropped
32
+ * into the swimlane become its children and move with it; which lane an element
33
+ * belongs to is expressed by where the modeller places it, matching the UML
34
+ * activity-partition notation where partition membership is positional rather
35
+ * than stored per element.
36
+ */
37
+ declare type ActivitySwimlaneProps = DefaultNodeProps & {
38
+ orientation: "vertical" | "horizontal";
39
+ lanes: SwimlaneLane[];
40
+ };
41
+
27
42
  declare type ApollonEdge = {
28
43
  id: string;
29
44
  source: string;
@@ -174,6 +189,7 @@ declare type CollaborationState = {
174
189
  viewport?: CollaborationViewport | null;
175
190
  followingClientId?: number | null;
176
191
  selectedElementId?: string | null;
192
+ draggingNodes?: DraggingNode[] | null;
177
193
  };
178
194
 
179
195
  declare type CollaborationUser = {
@@ -359,6 +375,7 @@ declare const diagramNodeTypes: {
359
375
  activityMergeNode: typeof ActivityMergeNode;
360
376
  activityForkNode: typeof ActivityForkNode;
361
377
  activityForkNodeHorizontal: typeof ActivityForkNodeHorizontal;
378
+ activitySwimlane: typeof ActivitySwimlane;
362
379
  useCase: typeof UseCase;
363
380
  useCaseActor: typeof UseCaseActor;
364
381
  useCaseSystem: typeof UseCaseSystem;
@@ -411,8 +428,24 @@ declare type DiagramStore = {
411
428
  canUndo: boolean;
412
429
  canRedo: boolean;
413
430
  undoManager: Y.UndoManager | null;
431
+ collaborationEnabled: boolean;
414
432
  previewMode: boolean;
415
433
  setDiagramId: (diagramId: string) => void;
434
+ setCollaborationEnabled: (enabled: boolean) => void;
435
+ /**
436
+ * Inject the sink that forwards transient drag/resize frames onto the
437
+ * ephemeral awareness channel (wired by `YjsSync` for every editor). Kept as
438
+ * runtime wiring rather than diagram state so a `reset()` never clears it.
439
+ * The broadcast itself is gated by `collaborationEnabled`, not by this sink;
440
+ * `null` (headless, where no `YjsSync` runs) just leaves it unwired.
441
+ */
442
+ setDraggingNodesPublisher: (publisher: ((draggingNodes: DraggingNode[] | null) => void) | null) => void;
443
+ /**
444
+ * Clear the peers' live-drag overlay once a gesture's settled value is
445
+ * committed. Called from `onNodeDragStop` (after the doc write) and on
446
+ * collaboration teardown; a no-op when nothing is being broadcast.
447
+ */
448
+ endTransientNodeBroadcast: () => void;
416
449
  setNodes: (payload: Node_2[] | ((nodes: Node_2[]) => Node_2[])) => void;
417
450
  setEdges: (payload: Edge[] | ((edges: Edge[]) => Edge[])) => void;
418
451
  setNodesAndEdges: (nodes: Node_2[], edges: Edge[]) => void;
@@ -445,6 +478,23 @@ declare type DiagramStore = {
445
478
  setPreviewMode: (active: boolean) => void;
446
479
  };
447
480
 
481
+ /**
482
+ * One node a peer is actively dragging or resizing. Broadcast over the
483
+ * ephemeral awareness channel — never written to the Yjs document — so peers
484
+ * can render the in-progress gesture live without growing the CRDT or entering
485
+ * anyone's undo history. The settled position/size is committed once on
486
+ * drop/release through the document like any other edit.
487
+ */
488
+ declare type DraggingNode = {
489
+ id: string;
490
+ position: {
491
+ x: number;
492
+ y: number;
493
+ };
494
+ width?: number | null;
495
+ height?: number | null;
496
+ };
497
+
448
498
  declare type ExtendedEdgeProps = EdgeProps<Edge<CustomEdgeProps>> & {
449
499
  markerEnd?: string;
450
500
  markerPadding?: number;
@@ -607,6 +657,19 @@ declare type SfcTransitionBranchNodeProps = DefaultNodeProps & {
607
657
  showHint?: boolean;
608
658
  };
609
659
 
660
+ /** A single partition (lane) inside an activity swimlane. */
661
+ declare type SwimlaneLane = {
662
+ id: string;
663
+ name: string;
664
+ /**
665
+ * Lane's extent along the swimlane's primary axis, in absolute flow px. The
666
+ * last lane is elastic (fills the remainder), so resizing the swimlane only
667
+ * changes the last lane. Optional: lanes without it divide the space equally,
668
+ * so existing saved swimlanes render as equal lanes until a separator moves.
669
+ */
670
+ size?: number;
671
+ };
672
+
610
673
  declare function SyntaxTreeNonterminal({ id, width, height, data, parentId, }: NodeProps<Node_2<DefaultNodeProps>>): JSX.Element | null;
611
674
 
612
675
  declare function SyntaxTreeTerminal({ id, width, height, data, parentId, }: NodeProps<Node_2<DefaultNodeProps>>): JSX.Element | null;
@@ -849,6 +912,7 @@ export declare class YjsSync {
849
912
  setLocalAwarenessViewport: (viewport: CollaborationViewport | null) => void;
850
913
  setLocalAwarenessFollowing: (followingClientId: number | null) => void;
851
914
  setLocalAwarenessSelectedElement: (selectedElementId: string | null) => void;
915
+ setLocalAwarenessDraggingNodes: (draggingNodes: DraggingNode[] | null) => void;
852
916
  setLocalAwarenessState: (state: Partial<CollaborationState>) => void;
853
917
  subscribeToAwarenessChanges: (callback: (states: Map<number, CollaborationState>) => void) => () => void;
854
918
  getAwarenessStates: () => Map<number, CollaborationState>;
@@ -870,7 +934,7 @@ export declare class YjsSync {
870
934
  /**
871
935
  * Convert Base64 string to Uint8Array
872
936
  */
873
- private base64ToUint8;
937
+ static base64ToUint8(base64: string): Uint8Array;
874
938
  }
875
939
 
876
940
  export { }
package/dist/internals.js CHANGED
@@ -1,6 +1,7 @@
1
- import { W as n, aE as r } from "./yjsSync-aPxjWNdZ.js";
2
- import { ab as p, aU as i, aV as u, aW as S, aX as g, aY as T, aZ as b, bo as F, bp as l, bq as v } from "./yjsSync-aPxjWNdZ.js";
3
- function V(t = new n()) {
1
+ import { aD as r } from "./yjsSync-BCNFF_EI.js";
2
+ import { a8 as m, aT as p, aU as g, aV as u, aW as S, aX as T, aY as b, bo as l, bp as F, bq as Y } from "./yjsSync-BCNFF_EI.js";
3
+ import * as n from "yjs";
4
+ function d(t = new n.Doc()) {
4
5
  const e = () => {
5
6
  }, a = {
6
7
  getState: () => ({
@@ -8,6 +9,7 @@ function V(t = new n()) {
8
9
  updateEdgesFromYjs: e,
9
10
  updateAssessmentFromYjs: e,
10
11
  updateUndoRedoState: e,
12
+ setDraggingNodesPublisher: e,
11
13
  undoManager: null
12
14
  // Other DiagramStore fields are typed but never accessed by
13
15
  // YjsSync; cast through `unknown` keeps the helper from
@@ -30,16 +32,16 @@ function V(t = new n()) {
30
32
  };
31
33
  }
32
34
  export {
33
- p as MessageType,
35
+ m as MessageType,
34
36
  r as YjsSync,
35
- i as convertV2ToV4,
36
- u as convertV3EdgeTypeToV4,
37
- S as convertV3HandleToV4,
38
- g as convertV3MessagesToV4,
37
+ p as convertV2ToV4,
38
+ g as convertV3EdgeTypeToV4,
39
+ u as convertV3HandleToV4,
40
+ S as convertV3MessagesToV4,
39
41
  T as convertV3NodeTypeToV4,
40
42
  b as convertV3ToV4,
41
- V as createHeadlessSync,
42
- F as isV2Format,
43
- l as isV3Format,
44
- v as isV4Format
43
+ d as createHeadlessSync,
44
+ l as isV2Format,
45
+ F as isV3Format,
46
+ Y as isV4Format
45
47
  };
@@ -1,5 +1,6 @@
1
1
  import { Node, Edge } from '@xyflow/react';
2
2
  import { UMLDiagramType } from './types';
3
+ import { OverlayControlInput, OverlayRegion, OverlaySide } from './overlay/types';
3
4
  import { SendBroadcastMessage } from './sync/yjsSync';
4
5
  import * as Apollon from "./typings";
5
6
  export declare class ApollonEditor {
@@ -11,8 +12,8 @@ export declare class ApollonEditor {
11
12
  private readonly metadataStore;
12
13
  private readonly popoverStore;
13
14
  private readonly assessmentSelectionStore;
14
- private readonly alignmentGuidesStore;
15
- private readonly edgeGeometryStore;
15
+ private readonly overlayStore;
16
+ private readonly hostRegionEls;
16
17
  private subscribers;
17
18
  constructor(element: HTMLElement, options?: Apollon.ApollonOptions);
18
19
  private setReactFlowInstance;
@@ -31,10 +32,62 @@ export declare class ApollonEditor {
31
32
  x: number;
32
33
  y: number;
33
34
  }): import('@xyflow/react').XYPosition | null;
35
+ /**
36
+ * Zoom/pan so the whole diagram fits, capped at `maxZoom: 1.0`. Retries up to
37
+ * 10 animation frames until every node is measured.
38
+ * @param options.padding - Scalar fraction (default `0.15`), or per-side px
39
+ * that adds to a 16px gutter. A per-side object forces the inset-aware path.
40
+ * @param options.duration - Animation duration in ms (default `200`).
41
+ * @param options.respectInsets - Pad the fit by reserved overlay insets
42
+ * (header, rails, …). Default `true`.
43
+ */
34
44
  fitView(options?: {
35
- padding?: number;
45
+ padding?: number | Partial<Record<OverlaySide, number>>;
36
46
  duration?: number;
47
+ /** Pad the fit by the reserved overlay insets (header, rails, …). */
48
+ respectInsets?: boolean;
37
49
  }): void;
50
+ /**
51
+ * Register a floating control, returning a disposer. Re-using an id replaces
52
+ * (idempotent / StrictMode-safe). Pick a façade: a React host →
53
+ * `<ApollonControl>`; a non-React host (or one that needs its own React
54
+ * root/context) → `getRegionElement` + `createPortal`; a one-off imperative
55
+ * widget → `addControl`.
56
+ * @param control - id, target region, a `render` thunk, and optional layout
57
+ * options ({@link OverlayControlInput}).
58
+ * @returns A disposer that unregisters this control; safe to call twice.
59
+ * @throws If `id` is empty or `region` is not a known region — mistakes fail
60
+ * loudly at the edge, not silently in the renderer.
61
+ */
62
+ addControl(control: OverlayControlInput): () => void;
63
+ /**
64
+ * Patch a registered control's options/renderer (a no-op if absent).
65
+ * @param id - The control's immutable id (an `id` in `patch` is ignored).
66
+ * @param patch - Partial options/renderer merged over the existing control.
67
+ */
68
+ updateControl(id: string, patch: Partial<OverlayControlInput>): void;
69
+ /**
70
+ * @param id - A control id.
71
+ * @returns `true` if a control with this id is currently registered.
72
+ */
73
+ hasControl(id: string): boolean;
74
+ /**
75
+ * A stable DOM node anchored in `region`, for hosts that render their own
76
+ * React into it (via `createPortal`) to keep host context. Auto-measured, so
77
+ * the diagram makes room for whatever the host mounts. Lifetime = the editor;
78
+ * `releaseRegionElement` unregisters it. Reserved id: `apollon:host:<region>`.
79
+ * @param region - The region to anchor the node in.
80
+ * @returns The same node for the lifetime of one acquire; `releaseRegionElement`
81
+ * drops it and the next call returns a fresh node.
82
+ * @throws If `region` is not a known region.
83
+ */
84
+ getRegionElement(region: OverlayRegion): HTMLElement;
85
+ /**
86
+ * Release a region acquired via {@link getRegionElement} (unregister + drop
87
+ * the cached node); a later `getRegionElement(region)` creates a fresh node.
88
+ * @param region - The region whose host node to release.
89
+ */
90
+ releaseRegionElement(region: OverlayRegion): void;
38
91
  set diagramType(type: UMLDiagramType);
39
92
  destroy(): void;
40
93
  /** Renders a model to SVG via a hidden, off-screen mount. */
@@ -1 +1 @@
1
- export declare const AlignmentGuides: () => import("react").JSX.Element | null;
1
+ export declare const AlignmentGuides: () => import("react").JSX.Element;
@@ -1 +1,8 @@
1
+ /**
2
+ * Bottom-left control cluster. Every button shares one idiom
3
+ * (`apollon-chrome-iconbtn`): borderless on the glass pane, state shown as a
4
+ * surface-layer fill. Reads left→right as view (zoom-out/in, fit, a clickable
5
+ * %-reset) then, past a hairline, history (undo/redo). React Flow renders its
6
+ * built-in zoom/fit buttons first; our children follow.
7
+ */
1
8
  export declare const CustomControls: () => import("react").JSX.Element;
@@ -3,6 +3,11 @@ import { DropElementConfig } from '../constants';
3
3
  interface DraggableGhostProps {
4
4
  children: React.ReactNode;
5
5
  dropElementConfig: DropElementConfig;
6
+ /**
7
+ * Visual scale of the palette preview.
8
+ * Used to convert pointer offsets into node-placement offsets.
9
+ */
10
+ previewScale?: number;
6
11
  }
7
12
  export declare const DraggableGhost: React.FC<DraggableGhostProps>;
8
13
  export {};
@@ -1,4 +1,4 @@
1
- type NodePopoverType = "class" | "objectName" | "communicationObjectName" | "default" | "Component" | "ComponentSubsystem" | "FlowchartTerminal" | "FlowchartProcess" | "FlowchartDecision" | "FlowchartInputOutput" | "FlowchartFunctionCall" | "DeploymentComponent" | "DeploymentNode" | "SyntaxTreeNonterminal" | "SyntaxTreeTerminal" | "PetriNetPlace" | "BPMNTask" | "BPMNStartEvent" | "BPMNIntermediateEvent" | "BPMNEndEvent" | "BPMNGateway" | "BPMNSubprocess" | "BPMNTransaction" | "BPMNCallActivity" | "BPMNAnnotation" | "BPMNDataObject" | "BPMNDataStore" | "BPMNPool" | "BPMNGroup" | "ReachabilityGraphMarking" | "Sfc" | "SfcActionTable";
1
+ type NodePopoverType = "class" | "objectName" | "communicationObjectName" | "default" | "Component" | "ComponentSubsystem" | "FlowchartTerminal" | "FlowchartProcess" | "FlowchartDecision" | "FlowchartInputOutput" | "FlowchartFunctionCall" | "DeploymentComponent" | "DeploymentNode" | "SyntaxTreeNonterminal" | "SyntaxTreeTerminal" | "PetriNetPlace" | "BPMNTask" | "BPMNStartEvent" | "BPMNIntermediateEvent" | "BPMNEndEvent" | "BPMNGateway" | "BPMNSubprocess" | "BPMNTransaction" | "BPMNCallActivity" | "BPMNAnnotation" | "BPMNDataObject" | "BPMNDataStore" | "BPMNPool" | "BPMNGroup" | "ReachabilityGraphMarking" | "Sfc" | "SfcActionTable" | "ActivitySwimlane";
2
2
  type EdgePopoverType = "ClassAggregation" | "ClassInheritance" | "ClassRealization" | "ClassComposition" | "ClassBidirectional" | "ClassUnidirectional" | "ClassDependency" | "ActivityControlFlow" | "ObjectLink" | "CommunicationLink" | "UseCaseAssociation" | "UseCaseInclude" | "UseCaseExtend" | "UseCaseGeneralization" | "ComponentDependency" | "ComponentProvidedInterface" | "ComponentRequiredInterface" | "ComponentRequiredThreeQuarterInterface" | "ComponentRequiredQuarterInterface" | "BPMNSequenceFlow" | "BPMNMessageFlow" | "BPMNAssociationFlow" | "BPMNDataAssociationFlow" | "DeploymentAssociation" | "DeploymentDependency" | "DeploymentProvidedInterface" | "DeploymentRequiredInterface" | "DeploymentRequiredThreeQuarterInterface" | "DeploymentRequiredQuarterInterface" | "FlowChartFlowline" | "SyntaxTreeLink" | "SfcDiagramEdge" | "ReachabilityGraphArc" | "PetriNetArc";
3
3
  type PopoverType = NodePopoverType | EdgePopoverType;
4
4
  interface PopoverManagerProps {
@@ -0,0 +1,2 @@
1
+ import { PopoverProps } from '../types';
2
+ export declare const ActivitySwimlaneEditPopover: React.FC<PopoverProps>;
@@ -0,0 +1 @@
1
+ export { ActivitySwimlaneEditPopover } from './ActivitySwimlaneEditPopover';
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ import { OverlayControlOptions } from '../../overlay/types';
3
+ /** Props for {@link ApollonControl}: every {@link OverlayControlOptions} field
4
+ * (id, region, inset, order, …) plus the children to render in-canvas. */
5
+ export type ApollonControlProps = OverlayControlOptions & {
6
+ /** Portaled into the control's host node inside the chosen region. Children
7
+ * changes never re-register the control; only real option changes do. */
8
+ children: ReactNode;
9
+ };
10
+ /**
11
+ * Declarative façade over `editor.addControl`. Registers a control that renders
12
+ * a stable host node into the chosen region, and portals `children` into that
13
+ * node — so children render INSIDE the canvas while React owns their
14
+ * reconciliation. Children changes therefore never touch the overlay store; only
15
+ * real option changes (region/inset/order/…) push an update. Returns null in the
16
+ * host tree.
17
+ */
18
+ export declare function ApollonControl({ children, ...options }: ApollonControlProps): ReactNode;
@@ -0,0 +1,26 @@
1
+ import { FC } from 'react';
2
+ import { InterfaceLabelSide } from '../../../utils/geometry/interfaceLabelLayout';
3
+ interface Props {
4
+ name?: string | null;
5
+ /** Interface circle bounds (always square — INTERFACE.SIZE). */
6
+ width: number;
7
+ height: number;
8
+ fill?: string;
9
+ /** Which side of the circle the name sits on. Defaults to "bottom"; the node
10
+ * component flips it away from sides a connecting edge attaches to (including
11
+ * a diagonal corner when every side is taken). */
12
+ side?: InterfaceLabelSide;
13
+ }
14
+ /**
15
+ * The provided/required interface name. Sits centered below the ball/socket by
16
+ * default; the node component moves it to whichever side — or, when all four
17
+ * sides have an edge, whichever diagonal corner — is clear of connecting edges.
18
+ * The name is rendered as real multi-line text (honouring line breaks) and is
19
+ * never auto-wrapped.
20
+ *
21
+ * Renders as SVG <text>/<tspan> in the node's overflow:visible region. The
22
+ * PNG/PDF/SVG export pipeline captures it via its node-text bbox scan
23
+ * (utils/exportUtils.ts), so it exports correctly without enlarging the viewBox.
24
+ */
25
+ export declare const InterfaceLabel: FC<Props>;
26
+ export {};
@@ -0,0 +1,9 @@
1
+ import { SVGComponentProps } from '../../../../types/SVG';
2
+ import { ActivitySwimlaneProps } from '../../../../types';
3
+ /** Thickness of the band that holds the lane name labels. */
4
+ export declare const SWIMLANE_HEADER_SIZE = 30;
5
+ interface ActivitySwimlaneSVGProps extends SVGComponentProps {
6
+ data: ActivitySwimlaneProps;
7
+ }
8
+ export declare const ActivitySwimlaneSVG: React.FC<ActivitySwimlaneSVGProps>;
9
+ export {};
@@ -6,3 +6,4 @@ export * from './ActivityObjectNodeSVG';
6
6
  export * from './ActivityMergeNodeSVG';
7
7
  export * from './ActivityForkNodeSVG';
8
8
  export * from './ActivityForkNodeHorizontalSVG';
9
+ export * from './ActivitySwimlaneSVG';
@@ -1,7 +1,9 @@
1
1
  import { SVGComponentProps } from '../../../../types/SVG';
2
2
  import { DefaultNodeProps } from '../../../../types';
3
+ import { InterfaceLabelSide } from '../../../../utils/geometry/interfaceLabelLayout';
3
4
  interface Props extends SVGComponentProps {
4
5
  data: DefaultNodeProps;
6
+ labelSide?: InterfaceLabelSide;
5
7
  }
6
8
  export declare const ComponentInterfaceNodeSVG: React.FC<Props>;
7
9
  export {};
@@ -1,7 +1,9 @@
1
1
  import { SVGComponentProps } from '../../../../types/SVG';
2
2
  import { DefaultNodeProps } from '../../../../types';
3
+ import { InterfaceLabelSide } from '../../../../utils/geometry/interfaceLabelLayout';
3
4
  interface Props extends SVGComponentProps {
4
5
  data: DefaultNodeProps;
6
+ labelSide?: InterfaceLabelSide;
5
7
  }
6
8
  export declare const DeploymentInterfaceSVG: React.FC<Props>;
7
9
  export {};
@@ -39,6 +39,16 @@ export declare const LAYOUT: Readonly<{
39
39
  readonly STEREOTYPE_LINE_HEIGHT: 15;
40
40
  readonly STEREOTYPE_NAME_GAP: 4;
41
41
  }>;
42
+ /**
43
+ * Treat narrow portrait viewports and short phone-landscape viewports as
44
+ * mobile. The portrait bound stops just below 768px so iPads (768px portrait)
45
+ * keep the regular desktop layout; the second clause catches phones in
46
+ * landscape, where the short height distinguishes them from tablets.
47
+ *
48
+ * NOTE: mirrored in standalone/webapp/src/constants/responsive.ts (the webapp
49
+ * can't import the library's curated public surface). Keep both in sync.
50
+ */
51
+ export declare const MOBILE_VIEW_QUERY = "(max-width: 767.95px), (max-width: 950px) and (max-height: 500px)";
42
52
  export declare const INTERFACE: Readonly<{
43
53
  readonly SIZE: 30;
44
54
  readonly RADIUS: number;
@@ -97,6 +107,19 @@ export declare const EDGES: Readonly<{
97
107
  * U-turn is never a deliberate edit, so this stays at the visual-merge
98
108
  * threshold rather than the per-step threshold. */
99
109
  readonly ORTHOGONAL_ARM_OVERLAP_PX: 10;
110
+ /** Perpendicular gap (flow px) between an edge's mid-segment line and the
111
+ * near edge of its relationship/stereotype label. The label sits this far
112
+ * off the line on whichever side is clearer. */
113
+ readonly LABEL_GAP: 14;
114
+ /** Nominal label line height (flow px) used as the across-text depth of the
115
+ * candidate box when scoring which side a label sits on. Constant by
116
+ * construction — placement never measures the rendered text (mirrors the
117
+ * messageLayout.ts invariant). */
118
+ readonly LABEL_LINE_HEIGHT: 14;
119
+ /** Nominal half-width (flow px) of a label along its text axis, used only to
120
+ * build the candidate box for side scoring. Coarse on purpose: we choose a
121
+ * side, not a pixel-perfect fit. */
122
+ readonly LABEL_NOMINAL_HALF_EXTENT: 40;
100
123
  }>;
101
124
  export declare const ZINDEX: Readonly<{
102
125
  readonly BASE: 0;
@@ -225,6 +248,11 @@ export type DropElementConfig = {
225
248
  readonly type: DiagramNodeType;
226
249
  readonly width: number;
227
250
  readonly height: number;
251
+ /** Size of the node when dropped on the canvas, if it should differ from the
252
+ * sidebar preview size (`width`/`height`). Lets a large element preview small
253
+ * in the palette without shrinking the rest of the picker. */
254
+ readonly dropWidth?: number;
255
+ readonly dropHeight?: number;
228
256
  readonly defaultData?: Record<string, unknown>;
229
257
  readonly svg: React.FC<any>;
230
258
  readonly marginTop?: number;
@@ -1,13 +1,10 @@
1
1
  import { IPoint } from '../Connection';
2
2
  interface EdgeIncludeExtendLabelsProps {
3
- label?: string | null;
4
- pathMiddlePosition: IPoint;
5
3
  sourcePoint?: IPoint;
6
4
  targetPoint?: IPoint;
7
- isUseCasePath?: boolean;
8
5
  showRelationshipLabels?: boolean;
9
6
  relationshipType?: "include" | "extend";
10
7
  textColor?: string;
11
8
  }
12
- export declare const EdgeIncludeExtendLabel: ({ sourcePoint, targetPoint, showRelationshipLabels, relationshipType, textColor, }: EdgeIncludeExtendLabelsProps) => import("react").JSX.Element;
9
+ export declare const EdgeIncludeExtendLabel: ({ sourcePoint, targetPoint, showRelationshipLabels, relationshipType, textColor, }: EdgeIncludeExtendLabelsProps) => import("react").JSX.Element | null;
13
10
  export {};
@@ -1,15 +1,18 @@
1
1
  import { IPoint } from '../Connection';
2
+ import { Rect } from '../../utils/geometry/edgeLabelLayout';
2
3
  interface EdgeMiddleLabelsProps {
3
4
  label?: string | null;
4
- pathMiddlePosition: IPoint;
5
- isMiddlePathHorizontal: boolean;
5
+ /** The edge's full rendered polyline (orthogonal edges). */
6
+ activePoints?: IPoint[];
6
7
  sourcePoint?: IPoint;
7
8
  targetPoint?: IPoint;
9
+ /** Every node the edge routes near, so the label avoids all of them. */
10
+ nodeRects?: Rect[];
11
+ neighborGeometry?: IPoint[][];
8
12
  showRelationshipLabels?: boolean;
9
13
  isUseCasePath?: boolean;
10
14
  isPetriNet?: boolean;
11
- avoidToolbarOverlap?: boolean;
12
15
  textColor: string;
13
16
  }
14
- export declare const EdgeMiddleLabels: ({ label, pathMiddlePosition, isMiddlePathHorizontal, sourcePoint, targetPoint, showRelationshipLabels, isUseCasePath, isPetriNet, avoidToolbarOverlap, textColor, }: EdgeMiddleLabelsProps) => import("react").JSX.Element | null;
17
+ export declare const EdgeMiddleLabels: ({ label, activePoints, sourcePoint, targetPoint, nodeRects, neighborGeometry, showRelationshipLabels, isUseCasePath, isPetriNet, textColor, }: EdgeMiddleLabelsProps) => import("react").JSX.Element | null;
15
18
  export {};
@@ -7,9 +7,8 @@ interface EdgeMultipleLabelsProps {
7
7
  isReconnecting?: boolean;
8
8
  sourcePosition: IPoint;
9
9
  targetPosition: IPoint;
10
- edgePoints?: IPoint[];
11
- isHorizontalEdge?: boolean;
10
+ isHorizontalEdge: boolean;
12
11
  textColor: string;
13
12
  }
14
- export declare const EdgeMultipleLabels: ({ messages, pathMiddlePosition, showRelationshipLabels, isReconnecting, sourcePosition, targetPosition, edgePoints, isHorizontalEdge, textColor, }: EdgeMultipleLabelsProps) => import("react").JSX.Element | null;
13
+ export declare const EdgeMultipleLabels: ({ messages, pathMiddlePosition, showRelationshipLabels, isReconnecting, sourcePosition, targetPosition, isHorizontalEdge, textColor, }: EdgeMultipleLabelsProps) => import("react").JSX.Element | null;
15
14
  export {};
@@ -0,0 +1,51 @@
1
+ import { MessageData } from '../EdgeProps';
2
+ import { IPoint } from '../Connection';
3
+ /**
4
+ * Geometry for communication-edge message labels, relative to the middle-segment
5
+ * midpoint (position-independent, hence unit-testable on its own).
6
+ *
7
+ * Invariant: a group's SIDE is fixed by the segment orientation; flow direction
8
+ * is encoded ONLY by the arrow rotation, never by the side. Gaps are constant by
9
+ * construction — nothing measures text width.
10
+ */
11
+ export interface MessageHostSegment {
12
+ /** Rounded midpoint of the chosen arm (flow space). */
13
+ point: IPoint;
14
+ /** Orientation of the chosen arm. */
15
+ isHorizontal: boolean;
16
+ /** The arm's endpoints in source->target order (for arrow direction). */
17
+ start: IPoint;
18
+ end: IPoint;
19
+ }
20
+ /**
21
+ * The arm of a (possibly stepped) communication edge that should HOST the
22
+ * message stack. The naive mid-segment can land on a tiny jog between two long
23
+ * arms — classifying a dominantly-vertical edge as "horizontal" so the labels
24
+ * centre on the jog and straddle BOTH neighbouring arms. Instead we host on the
25
+ * LONGEST arm (tie-broken toward the edge's middle), so the stack sits beside a
26
+ * long straight run: it never crosses the line and, in vertical mode, all
27
+ * same-direction messages share a leading edge. Pure geometry — identical in the
28
+ * editor and in headless export.
29
+ */
30
+ export declare const getMessageHostSegment: (renderPoints: IPoint[], fallbackSource: IPoint, fallbackTarget: IPoint) => MessageHostSegment;
31
+ export type LabelTextAnchor = "start" | "middle" | "end";
32
+ export declare const ARROW_SIZE = 16;
33
+ export declare const ARROW_TEXT_GAP = 4;
34
+ export declare const LINE_GAP = 8;
35
+ export declare const STACK_SPACING = 22;
36
+ export interface MessageGroupLayout {
37
+ messages: MessageData[];
38
+ arrowRotation: number;
39
+ textAnchor: LabelTextAnchor;
40
+ /** Top-left of the arrow icon for the first message, relative to the segment midpoint. */
41
+ arrowOrigin: IPoint;
42
+ /** Text anchor point for the first message, relative to the segment midpoint. */
43
+ textOrigin: IPoint;
44
+ /** Offset added per stacked message (multiplied by the message index). */
45
+ stackStep: IPoint;
46
+ }
47
+ export interface MessageLayout {
48
+ forward: MessageGroupLayout;
49
+ backward: MessageGroupLayout;
50
+ }
51
+ export declare const computeMessageLayout: (messages: MessageData[], sourcePosition: IPoint, targetPosition: IPoint, isHorizontalEdge: boolean) => MessageLayout;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Typed error for the raster (PNG) export path so callers can show an
3
+ * actionable "diagram too large" message instead of the silent 0-byte file the
4
+ * old canvas path produced (#667).
5
+ */
6
+ export declare class RasterTooLargeError extends Error {
7
+ readonly canvasWidth: number;
8
+ readonly canvasHeight: number;
9
+ constructor(message: string, canvasWidth: number, canvasHeight: number);
10
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@tumaet/apollon/export` — reliable browser raster/vector export of a
3
+ * compat-mode Apollon SVG. PNG via resvg (wasm); PDF via svg2pdf.js on jsPDF.
4
+ * Both fix the canvas-area-cap silent failure of the old client path (#667).
5
+ *
6
+ * The heavy renderers (`@resvg/resvg-wasm`, `jspdf`, `svg2pdf.js`) are optional
7
+ * dependencies, loaded lazily, so importing the editor never pulls them in.
8
+ */
9
+ export { svgToPng, computeAppliedScale } from './svgToPng';
10
+ export type { SvgToPngOptions, SvgToPngResult } from './svgToPng';
11
+ export { svgToPdf } from './svgToPdf';
12
+ export type { SvgToPdfOptions } from './svgToPdf';
13
+ export { RasterTooLargeError } from './exportErrors';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Drop `font-style="italic"` from every element of an export SVG.
3
+ *
4
+ * Apollon emits italic for abstract-class headers, but the editor itself ships
5
+ * only upright Inter (Regular + Bold) — on screen the slant is the browser's
6
+ * *synthetic* oblique, and text width is measured with the upright metrics.
7
+ * The raster (resvg) and PDF (jsPDF) paths carry the same two upright faces, so
8
+ * a real italic face would change advance widths and break the editor↔export
9
+ * determinism guarantee (and risk overflowing label boxes). We therefore render
10
+ * abstract names upright by design — matching the server export — and strip the
11
+ * attribute so the SVG no longer *claims* an italic it doesn't deliver. Abstract
12
+ * classes stay distinguishable by their «Abstract» stereotype label.
13
+ *
14
+ * Mutates `root` in place.
15
+ */
16
+ export declare function normalizeExportSvg(root: Element): void;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Adapt a compat-mode Apollon SVG to the three svg2pdf.js limitations that the
3
+ * library's own compat passes (in exportUtils.ts) do NOT already cover.
4
+ * Relative font-sizes, `tspan` `dy` and `dominant-baseline` are resolved
5
+ * upstream by `getSVG`, so they are intentionally absent here.
6
+ *
7
+ * 1. Nested `<svg>` clips its content despite `overflow="visible"` — svg2pdf
8
+ * treats the intrinsic size as a clip rect. Apollon wraps each node in
9
+ * `<svg width viewBox>`; replace those with `<g>` (layout-equivalent since
10
+ * the viewBox is always `0 0 w h`), translating any non-zero origin.
11
+ * 2. A `<text>` with 2+ `<tspan>` children renders with wrong glyph widths in
12
+ * svg2pdf; split it into independent `<text>` elements. Each tspan already
13
+ * carries an absolute `y` (resolved by `resolveTspanDy`).
14
+ * 3. svg2pdf looks up `font-family` as a literal string, so the CSS list
15
+ * `"Inter, system-ui, …"` misses the registered "Inter" and falls back to a
16
+ * standard font; collapse any Inter-led list to plain `"Inter"`.
17
+ *
18
+ * Mutates `root` in place and returns it.
19
+ */
20
+ export declare function preProcessSvgForPdf(root: Element): Element;
@@ -0,0 +1,17 @@
1
+ export type SvgToPdfOptions = {
2
+ /** Document title metadata; does not affect layout. */
3
+ title?: string;
4
+ /**
5
+ * Inter Regular + Bold as ttf/otf bytes, for hosts without `fetch` of the
6
+ * bundled `?url` font asset (e.g. tests, headless Node). Defaults to the
7
+ * bundled Inter.
8
+ */
9
+ fonts?: {
10
+ regular: Uint8Array;
11
+ bold: Uint8Array;
12
+ };
13
+ };
14
+ export declare function svgToPdf(svg: string, clip: {
15
+ width: number;
16
+ height: number;
17
+ }, opts?: SvgToPdfOptions): Promise<Blob>;