f1ow 1.0.0 → 1.1.1

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.
@@ -0,0 +1,2 @@
1
+ export declare function isTextEditingTarget(target: EventTarget | null): boolean;
2
+ export declare function blurTextEditingTarget(target: EventTarget | null): boolean;
@@ -7,6 +7,7 @@ interface BBox {
7
7
  width: number;
8
8
  height: number;
9
9
  }
10
+ type BoxLike = Pick<BBox, 'x' | 'y' | 'width' | 'height'>;
10
11
  /**
11
12
  * Determine the preferred exit direction for elbow routing from a
12
13
  * center (imprecise) binding.
@@ -21,8 +22,11 @@ interface BBox {
21
22
  * shapes at their vertical center — visually too close to objects and
22
23
  * often requiring more bends.
23
24
  *
24
- * Only uses horizontal exits when the target is strongly horizontally
25
- * aligned (>3× horizontal dominance after normalizing by shape size).
25
+ * When both endpoint shapes are known, prefer the axis with the smaller
26
+ * inter-shape gap (or the orthogonal axis with less overlap). This makes
27
+ * diagonal routes look more intentional than a pure center-to-center
28
+ * heuristic. If only a target point is known, fall back to the normalized
29
+ * center-delta heuristic with a slight vertical bias.
26
30
  *
27
31
  * This function should be used by BOTH:
28
32
  * - The binding system (connection.ts) to compute edge points for
@@ -31,12 +35,7 @@ interface BBox {
31
35
  *
32
36
  * For PRECISE bindings, always use directionFromFixedPoint instead.
33
37
  */
34
- export declare function getElbowPreferredDirection(shape: {
35
- x: number;
36
- y: number;
37
- width: number;
38
- height: number;
39
- }, targetPoint: Point): Direction;
38
+ export declare function getElbowPreferredDirection(shape: BoxLike, targetPoint: Point, targetShape?: BoxLike | null, targetBinding?: Binding | null): Direction;
40
39
  /**
41
40
  * Determine the exit/entry direction from a binding's fixedPoint.
42
41
  * fixedPoint is [fx, fy] in [0-1, 0-1] on the target shape's bbox.
@@ -67,6 +66,34 @@ export declare function directionFromShapeToPoint(shape: BBox, targetPoint: Poin
67
66
  * computed edge point from the binding system.
68
67
  */
69
68
  export declare function directionFromEdgePoint(shape: BBox, edgePoint: Point): Direction;
69
+ export declare function selectElbowDirectionPair(args: {
70
+ startWorld: Point;
71
+ endWorld: Point;
72
+ startBinding: Binding | null;
73
+ endBinding: Binding | null;
74
+ startShape?: {
75
+ x: number;
76
+ y: number;
77
+ width: number;
78
+ height: number;
79
+ } | null;
80
+ endShape?: {
81
+ x: number;
82
+ y: number;
83
+ width: number;
84
+ height: number;
85
+ } | null;
86
+ intermediateObstacles?: Array<{
87
+ x: number;
88
+ y: number;
89
+ width: number;
90
+ height: number;
91
+ }>;
92
+ minStubLength?: number;
93
+ }): {
94
+ startDir: Direction;
95
+ endDir: Direction;
96
+ };
70
97
  /**
71
98
  * Compute an orthogonal route between two points using a multi-candidate
72
99
  * grid-based shortest-path algorithm.
@@ -99,6 +126,12 @@ export declare function directionFromEdgePoint(shape: BBox, edgePoint: Point): D
99
126
  export declare function computeElbowRoute(start: Point, end: Point, startDir: Direction, endDir: Direction, startBBox?: BBox | null, endBBox?: BBox | null, minStubLength?: number,
100
127
  /** Additional obstacles (intermediate shapes) to avoid — already as BBox */
101
128
  intermediateObstacles?: BBox[]): Point[];
129
+ export declare function collectElbowIntermediateObstacles(startWorld: Point, endWorld: Point, allElements: CanvasElement[], excludeIds: Set<string>): Array<{
130
+ x: number;
131
+ y: number;
132
+ width: number;
133
+ height: number;
134
+ }>;
102
135
  /** Clear the route cache (call when elements change structurally) */
103
136
  export declare function clearElbowRouteCache(): void;
104
137
  export declare function computeElbowPoints(startWorld: Point, endWorld: Point, startBinding: Binding | null, endBinding: Binding | null, allElements: CanvasElement[], minStubLength?: number): number[];
@@ -36,6 +36,27 @@ export declare function measureLabelText(text: string, fontSize: number, fontFam
36
36
  width: number;
37
37
  height: number;
38
38
  };
39
+ /**
40
+ * Compute the CSS half-leading offset for a given font.
41
+ *
42
+ * Konva renders text with `textBaseline='top'` — the glyph top aligns
43
+ * with the node's y position (no leading above).
44
+ * CSS distributes leading equally above and below the content area
45
+ * within each line box. The half-leading is the space ABOVE the glyphs
46
+ * that CSS adds but Konva does not.
47
+ *
48
+ * To align a DOM textarea's visible text with Konva's rendered text,
49
+ * shift the textarea UP by this amount (in canvas-space pixels).
50
+ *
51
+ * Uses `fontBoundingBoxAscent/Descent` for accurate per-font measurement
52
+ * with a safe fallback for older browsers.
53
+ *
54
+ * @param fontSize - Font size in canvas-space pixels
55
+ * @param fontFamily - CSS font-family string
56
+ * @param lineHeight - Line-height multiplier (default: LABEL_LINE_HEIGHT)
57
+ * @returns Half-leading in canvas-space pixels (≥ 0)
58
+ */
59
+ export declare function computeHalfLeading(fontSize: number, fontFamily: string, lineHeight?: number): number;
39
60
  /**
40
61
  * Compute the full pill (background rect) dimensions for a connector label.
41
62
  *
@@ -0,0 +1,14 @@
1
+ export declare function htmlToPlainText(html: string): string;
2
+ /**
3
+ * Render markdown text to inline-styled HTML.
4
+ * Safe for use with `innerHTML` — raw HTML in the source is escaped.
5
+ */
6
+ export declare function renderMarkdown(text: string): string;
7
+ export declare function markdownToPlainText(text: string): string;
8
+ /** CSS class name for the markdown overlay container */
9
+ export declare const MD_CLASS = "fc-md";
10
+ /**
11
+ * CSS rules for markdown-rendered content.
12
+ * Injected once via a <style> tag by the overlay component.
13
+ */
14
+ export declare const MD_STYLES = "\n.fc-md { line-height: inherit; }\n.fc-md p { margin: 0; }\n.fc-md h1 { margin: 0; font-weight: bold; font-size: 1.5em; line-height: 1.3; }\n.fc-md h2 { margin: 0; font-weight: bold; font-size: 1.3em; line-height: 1.3; }\n.fc-md h3 { margin: 0; font-weight: bold; font-size: 1.15em; line-height: 1.3; }\n.fc-md h4, .fc-md h5, .fc-md h6 { margin: 0; font-weight: bold; font-size: 1em; }\n.fc-md strong { font-weight: bold; }\n.fc-md em { font-style: italic; }\n.fc-md del { text-decoration: line-through; }\n.fc-md code {\n background: rgba(0,0,0,0.06);\n padding: 0.1em 0.3em;\n border-radius: 3px;\n font-size: 0.9em;\n font-family: 'SF Mono', Monaco, Menlo, Consolas, monospace;\n}\n.fc-md pre {\n background: rgba(0,0,0,0.06);\n padding: 0.4em 0.6em;\n border-radius: 4px;\n font-size: 0.85em;\n overflow-x: auto;\n margin: 0.2em 0;\n}\n.fc-md pre code { background: none; padding: 0; font-size: inherit; }\n.fc-md ul, .fc-md ol { padding-left: 1.5em; margin: 0.15em 0; }\n.fc-md li { margin: 0; }\n.fc-md blockquote {\n border-left: 3px solid rgba(0,0,0,0.15);\n padding-left: 0.8em;\n margin: 0.2em 0;\n color: rgba(0,0,0,0.55);\n}\n.fc-md a { color: #4f8df7; text-decoration: underline; }\n.fc-md hr { border: none; border-top: 1px solid rgba(0,0,0,0.12); margin: 0.3em 0; }\n.fc-md img { max-width: 100%; }\n";
@@ -0,0 +1 @@
1
+ export declare function serializeEditableHtmlToMarkdown(html: string): string;
@@ -0,0 +1,18 @@
1
+ import { CanvasElement, ElementStyle, Point, TextElement } from '../types';
2
+ export type TextContainerElement = Extract<CanvasElement, {
3
+ type: 'rectangle' | 'ellipse' | 'diamond' | 'image';
4
+ }>;
5
+ export declare function isTextContainerElement(element: CanvasElement): element is TextContainerElement;
6
+ export declare function isPointInsideTextContainer(element: TextContainerElement, point: Point): boolean;
7
+ export declare function findTopmostTextContainerAtPoint(elements: CanvasElement[], point: Point): TextContainerElement | null;
8
+ /**
9
+ * Reorder elements so every bound text element is placed immediately
10
+ * after its container. This keeps a shape and its label at the same
11
+ * logical z-index — shapes stacked above the container occlude the
12
+ * label, and the label never leaks above unrelated foreground elements.
13
+ *
14
+ * Pure function — returns the input reference unchanged when no
15
+ * reordering is required so downstream memoization stays stable.
16
+ */
17
+ export declare function orderBoundTextWithContainers(elements: CanvasElement[]): CanvasElement[];
18
+ export declare function createBoundTextElement(id: string, container: TextContainerElement, style: ElementStyle): TextElement;
@@ -0,0 +1,6 @@
1
+ import { CanvasElement } from '../types';
2
+ /**
3
+ * Resolve text element ids that should receive font style updates.
4
+ * Includes directly selected text elements and bound text from selected containers.
5
+ */
6
+ export declare function collectTextStyleTargetIds(selectedIds: string[], elements: CanvasElement[]): string[];
@@ -0,0 +1,72 @@
1
+ import * as c from "yjs";
2
+ import { WebsocketProvider as i } from "y-websocket";
3
+ let n = null, e = null, u = null, l = /* @__PURE__ */ new Set();
4
+ function w(t) {
5
+ return e && d(), u = t, n = new c.Doc(), e = new i(
6
+ t.serverUrl,
7
+ t.roomName,
8
+ n,
9
+ {
10
+ connect: !0,
11
+ params: t.authToken ? { token: t.authToken } : void 0
12
+ }
13
+ ), e.awareness.setLocalState({
14
+ user: t.user,
15
+ cursor: null,
16
+ selectedIds: []
17
+ }), e.on("status", (r) => {
18
+ const o = r.status;
19
+ for (const s of l)
20
+ s(o);
21
+ }), { doc: n, provider: e };
22
+ }
23
+ function d() {
24
+ e && (e.awareness.setLocalState(null), e.disconnect(), e.destroy(), e = null), n && (n.destroy(), n = null), u = null;
25
+ }
26
+ function m() {
27
+ return n;
28
+ }
29
+ function p() {
30
+ return e;
31
+ }
32
+ function v() {
33
+ return n?.getMap("elements");
34
+ }
35
+ function S() {
36
+ return u;
37
+ }
38
+ function g() {
39
+ return e !== null && e.wsconnected;
40
+ }
41
+ function C(t) {
42
+ return l.add(t), () => {
43
+ l.delete(t);
44
+ };
45
+ }
46
+ function b(t) {
47
+ if (!e) return;
48
+ const r = e.awareness.getLocalState();
49
+ e.awareness.setLocalState({
50
+ ...r,
51
+ ...t
52
+ });
53
+ }
54
+ function L() {
55
+ if (!e) return /* @__PURE__ */ new Map();
56
+ const t = e.awareness.getStates(), r = e.awareness.clientID, o = /* @__PURE__ */ new Map();
57
+ for (const [s, a] of t)
58
+ s !== r && a && a.user && o.set(s, a);
59
+ return o;
60
+ }
61
+ export {
62
+ w as createCollaborationProvider,
63
+ d as destroyCollaborationProvider,
64
+ S as getCollaborationConfig,
65
+ L as getRemoteAwareness,
66
+ m as getYDoc,
67
+ v as getYElements,
68
+ p as getYProvider,
69
+ g as isCollaborationActive,
70
+ C as onStatusChange,
71
+ b as updateAwareness
72
+ };
package/package.json CHANGED
@@ -1,107 +1,121 @@
1
- {
2
- "name": "f1ow",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "description": "Interactive canvas drawing toolkit built on KonvaJS — drop-in React component for diagrams, sketches & whiteboards",
6
- "author": "Nuumz <info@nuumz.com>",
7
- "homepage": "https://github.com/nuumz/f1ow-canvas#readme",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/nuumz/f1ow-canvas.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/nuumz/f1ow-canvas/issues"
14
- },
15
- "main": "./dist/f1ow.umd.cjs",
16
- "module": "./dist/f1ow.js",
17
- "types": "./dist/lib/index.d.ts",
18
- "exports": {
19
- ".": {
20
- "types": "./dist/lib/index.d.ts",
21
- "import": "./dist/f1ow.js",
22
- "require": "./dist/f1ow.umd.cjs"
23
- }
24
- },
25
- "files": [
26
- "dist",
27
- "LICENSE",
28
- "README.md"
29
- ],
30
- "sideEffects": false,
31
- "scripts": {
32
- "dev": "vite",
33
- "build": "vite build",
34
- "build:lib": "vite build --mode lib",
35
- "preview": "vite preview",
36
- "typecheck": "tsc --noEmit",
37
- "prepublishOnly": "npm run typecheck && npm run build:lib"
38
- },
39
- "peerDependencies": {
40
- "react": ">=17.0.0",
41
- "react-dom": ">=17.0.0",
42
- "konva": ">=9.0.0",
43
- "react-konva": ">=18.0.0",
44
- "zustand": ">=5.0.0",
45
- "yjs": ">=13.0.0",
46
- "y-websocket": ">=2.0.0"
47
- },
48
- "peerDependenciesMeta": {
49
- "konva": {
50
- "optional": false
51
- },
52
- "react-konva": {
53
- "optional": false
54
- },
55
- "zustand": {
56
- "optional": false
57
- },
58
- "yjs": {
59
- "optional": true
60
- },
61
- "y-websocket": {
62
- "optional": true
63
- }
64
- },
65
- "devDependencies": {
66
- "@types/node": "^25.2.1",
67
- "@types/rbush": "^4.0.0",
68
- "@types/react": "^18.3.18",
69
- "@types/react-dom": "^18.3.5",
70
- "@vitejs/plugin-react": "^4.3.4",
71
- "konva": "^9.3.18",
72
- "lucide-react": "^0.468.0",
73
- "nanoid": "^5.0.9",
74
- "rbush": "^4.0.1",
75
- "react": "^18.3.1",
76
- "react-dom": "^18.3.1",
77
- "react-konva": "^18.2.10",
78
- "typescript": "^5.7.3",
79
- "vite": "^6.0.7",
80
- "vite-plugin-dts": "^4.3.0",
81
- "y-websocket": "^3.0.0",
82
- "yjs": "^13.6.29",
83
- "zustand": "^5.0.3"
84
- },
85
- "keywords": [
86
- "canvas",
87
- "drawing",
88
- "konvajs",
89
- "react",
90
- "whiteboard",
91
- "diagram",
92
- "flowchart",
93
- "react-component",
94
- "konva",
95
- "collaborative",
96
- "vector",
97
- "sketch"
98
- ],
99
- "license": "MIT",
100
- "pnpm": {
101
- "overrides": {
102
- "minimatch@9": "9.0.7",
103
- "minimatch@10": "10.2.1",
104
- "ajv": "8.18.0"
105
- }
106
- }
107
- }
1
+ {
2
+ "name": "f1ow",
3
+ "version": "1.1.1",
4
+ "type": "module",
5
+ "description": "Interactive canvas drawing toolkit built on KonvaJS — drop-in React component for diagrams, sketches & whiteboards",
6
+ "author": "Nuumz <info@nuumz.com>",
7
+ "homepage": "https://github.com/nuumz/f1ow-canvas#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/nuumz/f1ow-canvas.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/nuumz/f1ow-canvas/issues"
14
+ },
15
+ "main": "./dist/f1ow.umd.cjs",
16
+ "module": "./dist/f1ow.js",
17
+ "types": "./dist/lib/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/lib/index.d.ts",
21
+ "import": "./dist/f1ow.js",
22
+ "require": "./dist/f1ow.umd.cjs"
23
+ },
24
+ "./collaboration": {
25
+ "types": "./dist/lib/collaboration.d.ts",
26
+ "import": "./dist/f1ow-collaboration.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "LICENSE",
32
+ "README.md"
33
+ ],
34
+ "sideEffects": false,
35
+ "scripts": {
36
+ "dev": "vite",
37
+ "build": "vite build",
38
+ "build:lib": "vite build --mode lib && vite build --mode lib-collab",
39
+ "preview": "vite preview",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "test:coverage": "vitest run --coverage",
44
+ "prepublishOnly": "pnpm typecheck && pnpm build:lib"
45
+ },
46
+ "peerDependencies": {
47
+ "konva": ">=9.0.0",
48
+ "react": ">=19.0.0",
49
+ "react-dom": ">=19.0.0",
50
+ "react-konva": ">=19.0.0",
51
+ "y-websocket": ">=2.0.0",
52
+ "yjs": ">=13.0.0",
53
+ "zustand": ">=5.0.0"
54
+ },
55
+ "peerDependenciesMeta": {
56
+ "konva": {
57
+ "optional": false
58
+ },
59
+ "react-konva": {
60
+ "optional": false
61
+ },
62
+ "zustand": {
63
+ "optional": false
64
+ },
65
+ "yjs": {
66
+ "optional": true
67
+ },
68
+ "y-websocket": {
69
+ "optional": true
70
+ }
71
+ },
72
+ "devDependencies": {
73
+ "@types/node": "^25.6.0",
74
+ "@types/rbush": "^4.0.0",
75
+ "@types/react": "^19.2.2",
76
+ "@types/react-dom": "^19.2.2",
77
+ "@vitejs/plugin-react": "^4.7.0",
78
+ "@vitest/coverage-v8": "^4.1.4",
79
+ "happy-dom": "^20.9.0",
80
+ "konva": "^10.2.5",
81
+ "lucide-react": "^0.468.0",
82
+ "nanoid": "^5.1.9",
83
+ "rbush": "^4.0.1",
84
+ "react": "^19.2.5",
85
+ "react-dom": "^19.2.5",
86
+ "react-konva": "^19.2.3",
87
+ "typescript": "^5.9.3",
88
+ "vite": "^6.4.2",
89
+ "vite-plugin-dts": "^4.5.4",
90
+ "vitest": "^4.1.4",
91
+ "y-websocket": "^3.0.0",
92
+ "yjs": "^13.6.30",
93
+ "zustand": "^5.0.12"
94
+ },
95
+ "keywords": [
96
+ "canvas",
97
+ "drawing",
98
+ "konvajs",
99
+ "react",
100
+ "whiteboard",
101
+ "diagram",
102
+ "flowchart",
103
+ "react-component",
104
+ "konva",
105
+ "collaborative",
106
+ "vector",
107
+ "sketch"
108
+ ],
109
+ "license": "MIT",
110
+ "pnpm": {
111
+ "overrides": {
112
+ "minimatch@9": "9.0.7",
113
+ "minimatch@10": "10.2.3",
114
+ "ajv": "8.18.0",
115
+ "rollup@4": "4.59.0"
116
+ }
117
+ },
118
+ "dependencies": {
119
+ "marked": "^18.0.2"
120
+ }
121
+ }