@graph-artifact/core 0.1.9 → 0.1.10

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.
@@ -2,6 +2,8 @@ import type { ReactNode } from 'react';
2
2
  import type { ThemeOverride } from './types.js';
3
3
  import type { ThemeTokens } from './theme/index.js';
4
4
  export interface Theme {
5
+ /** Theme mode used for host integration (React Flow colorMode, etc.). */
6
+ mode: 'dark' | 'light';
5
7
  color: Record<string, string>;
6
8
  font: {
7
9
  family: string;
@@ -39,9 +41,11 @@ export interface Theme {
39
41
  }
40
42
  export declare const defaultTheme: Theme;
41
43
  interface ThemeProviderProps {
44
+ /** Override the base theme used ('dark' | 'light' | custom registered theme). */
45
+ baseTheme?: string;
42
46
  overrides?: ThemeOverride;
43
47
  children: ReactNode;
44
48
  }
45
- export declare function ThemeProvider({ overrides, children }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
49
+ export declare function ThemeProvider({ baseTheme, overrides, children }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
46
50
  export declare function useTheme(): Theme;
47
51
  export {};
@@ -5,6 +5,8 @@ import { getThemeTokens } from './theme/index.js';
5
5
  // ─── Build Theme from Tokens ──────────────────────────────────────────────────
6
6
  function buildTheme(tokens) {
7
7
  return {
8
+ // Most consumers only ship dark + light. Treat unknown themes as dark by default.
9
+ mode: tokens.color.darkBg2 === '#ffffff' ? 'light' : 'dark',
8
10
  color: { ...tokens.color },
9
11
  font: {
10
12
  family: tokens.font.family,
@@ -72,8 +74,21 @@ function resolveTheme(instanceOverrides) {
72
74
  }
73
75
  // ─── Context & Provider ───────────────────────────────────────────────────────
74
76
  const ThemeContext = createContext(defaultTheme);
75
- export function ThemeProvider({ overrides, children }) {
76
- const theme = useMemo(() => resolveTheme(overrides), [overrides]);
77
+ export function ThemeProvider({ baseTheme, overrides, children }) {
78
+ const theme = useMemo(() => {
79
+ if (!baseTheme)
80
+ return resolveTheme(overrides);
81
+ const tokens = getThemeTokens(baseTheme);
82
+ let resolved = buildTheme(tokens);
83
+ const { theme: configOverrides } = getConfig();
84
+ if (configOverrides && Object.keys(configOverrides).length > 0) {
85
+ resolved = deepMerge(resolved, configOverrides);
86
+ }
87
+ if (overrides && Object.keys(overrides).length > 0) {
88
+ resolved = deepMerge(resolved, overrides);
89
+ }
90
+ return resolved;
91
+ }, [baseTheme, overrides]);
77
92
  return _jsx(ThemeContext.Provider, { value: theme, children: children });
78
93
  }
79
94
  export function useTheme() {
@@ -278,7 +278,24 @@ function GraphCanvasInner({ parsed, metadata, onNodeClick, canvasOverrides, hide
278
278
  textAlign: 'center',
279
279
  }, children: `Diagram syntax/layout error: ${layoutError}` }));
280
280
  }
281
- return (_jsx("div", { style: { width: '100%', height: '100%' }, children: _jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: resolvedNodeTypes, edgeTypes: edgeTypes, onNodeClick: handleNodeClick, fitView: canvas.fitView, minZoom: canvas.minZoom, maxZoom: canvas.maxZoom, defaultEdgeOptions: defaultEdgeOptions, zIndexMode: "manual", proOptions: hideAttribution ? { hideAttribution: true } : undefined, children: [canvas.showBackground && (_jsx(Background, { color: theme.color.gray2, gap: canvas.backgroundGap, size: canvas.backgroundSize })), canvas.showControls && _jsx(Controls, {}), canvas.showMiniMap && (_jsx(MiniMap, { nodeColor: miniMapNodeColor, maskColor: miniMapMaskColor }))] }) }));
281
+ return (_jsxs("div", { style: { width: '100%', height: '100%' }, children: [_jsx("style", { children: `
282
+ /* Theme React Flow chrome (controls/minimap) to match our tokens. */
283
+ .react-flow__controls-button {
284
+ background: ${theme.mode === 'dark' ? theme.color.darkBg3 : theme.color.white};
285
+ color: ${theme.mode === 'dark' ? theme.color.gray4 : theme.color.gray4};
286
+ border: 1px solid ${theme.color.gray2};
287
+ }
288
+ .react-flow__controls-button:hover {
289
+ background: ${theme.mode === 'dark' ? theme.color.gray1 : theme.color.gray1};
290
+ }
291
+ .react-flow__controls-button svg {
292
+ fill: currentColor;
293
+ }
294
+ .react-flow__minimap {
295
+ background: ${theme.mode === 'dark' ? theme.color.darkBg2 : theme.color.white};
296
+ border: 1px solid ${theme.color.gray2};
297
+ }
298
+ ` }), _jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: resolvedNodeTypes, edgeTypes: edgeTypes, onNodeClick: handleNodeClick, fitView: canvas.fitView, minZoom: canvas.minZoom, maxZoom: canvas.maxZoom, defaultEdgeOptions: defaultEdgeOptions, zIndexMode: "manual", colorMode: theme.mode, proOptions: hideAttribution ? { hideAttribution: true } : undefined, children: [canvas.showBackground && (_jsx(Background, { color: theme.color.gray2, gap: canvas.backgroundGap, size: canvas.backgroundSize })), canvas.showControls && _jsx(Controls, {}), canvas.showMiniMap && (_jsx(MiniMap, { nodeColor: miniMapNodeColor, maskColor: miniMapMaskColor }))] })] }));
282
299
  }
283
300
  export function GraphCanvas(props) {
284
301
  return (_jsx(ReactFlowProvider, { children: _jsx(GraphCanvasInner, { ...props }) }));
@@ -80,7 +80,7 @@ export const SequenceNoteNode = memo(function SequenceNoteNode({ data }) {
80
80
  const ns = theme.nodeStyles.sequence;
81
81
  return (_jsx("div", { style: {
82
82
  width: w,
83
- backgroundColor: theme.color.darkBg3,
83
+ backgroundColor: ns.note.backgroundColor,
84
84
  border: `${ns.note.borderWidth} solid ${theme.color.gray2}`,
85
85
  borderRadius: theme.radius.sm,
86
86
  display: 'flex',
@@ -90,7 +90,7 @@ export const SequenceNoteNode = memo(function SequenceNoteNode({ data }) {
90
90
  pointerEvents: 'none',
91
91
  boxSizing: 'border-box',
92
92
  }, children: _jsx("span", { style: {
93
- color: theme.color.gray4,
93
+ color: ns.note.textColor,
94
94
  fontSize: theme.font.size.sm,
95
95
  fontFamily: theme.font.family,
96
96
  textAlign: 'center',
@@ -38,7 +38,7 @@ export const StateNode = memo(function StateNode({ data }) {
38
38
  if (shape === 'circle') {
39
39
  return (_jsx("div", { style: {
40
40
  width: layoutWidth, height: layoutHeight,
41
- backgroundColor: theme.color.gray5, borderRadius: theme.radius.full,
41
+ backgroundColor: theme.color.orange1, borderRadius: theme.radius.full,
42
42
  }, children: _jsx(StateHandles, { fwIn: fwIn, fwOut: fwOut }) }));
43
43
  }
44
44
  // End state: bullseye (outer ring + inner filled dot).
@@ -49,23 +49,26 @@ export const StateNode = memo(function StateNode({ data }) {
49
49
  width: layoutWidth, height: layoutHeight,
50
50
  boxSizing: 'border-box',
51
51
  borderRadius: theme.radius.full,
52
- border: `${theme.borderWidth.md} solid ${theme.color.gray5}`,
52
+ border: `${theme.borderWidth.md} solid ${theme.color.orange1}`,
53
53
  display: 'flex', alignItems: 'center', justifyContent: 'center',
54
54
  }, children: [_jsx("div", { style: {
55
55
  width: innerSize, height: innerSize,
56
- backgroundColor: theme.color.gray5, borderRadius: theme.radius.full,
56
+ // Inner fill: dark in dark mode, dark gray in light mode.
57
+ backgroundColor: theme.mode === 'dark' ? theme.color.darkBg2 : theme.color.gray5,
58
+ borderRadius: theme.radius.full,
57
59
  } }), _jsx(StateHandles, { fwIn: fwIn, fwOut: fwOut })] }));
58
60
  }
59
61
  return (_jsxs("div", { style: {
60
- backgroundColor: theme.color.darkBg2, border: `${theme.borderWidth.sm} solid ${theme.color.gray2}`,
61
- borderLeft: `${ns.rect.accentWidth} solid ${theme.color.orange1}`, borderRadius: theme.radius.pill,
62
+ backgroundColor: theme.color.orange1,
63
+ border: `${theme.borderWidth.sm} solid ${theme.color.orange3}`,
64
+ borderRadius: theme.radius.pill,
62
65
  padding: `${theme.space[3]} ${theme.space[6]}`,
63
66
  width: layoutWidth, height: layoutHeight,
64
67
  boxSizing: 'border-box',
65
68
  fontFamily: theme.font.family, cursor: 'pointer',
66
69
  display: 'flex', alignItems: 'center', justifyContent: 'center',
67
70
  }, children: [_jsx("div", { style: {
68
- color: theme.color.gray5, fontSize: theme.font.size.lg, fontWeight: theme.font.weight.semibold,
71
+ color: theme.color.white, fontSize: theme.font.size.lg, fontWeight: theme.font.weight.semibold,
69
72
  textAlign: 'center', whiteSpace: 'nowrap',
70
73
  }, children: label }), _jsx(StateHandles, { fwIn: fwIn, fwOut: fwOut })] }));
71
74
  });
package/dist/config.js CHANGED
@@ -69,7 +69,9 @@ function createDefaultConfig() {
69
69
  fitView: true,
70
70
  showBackground: true,
71
71
  showControls: true,
72
- showMiniMap: true,
72
+ // MiniMap is useful in dedicated viewers but noisy in embedded contexts (chat, docs).
73
+ // Host apps can enable it via `canvasOverrides`.
74
+ showMiniMap: false,
73
75
  backgroundGap: 20,
74
76
  backgroundSize: 1,
75
77
  },
@@ -400,6 +400,7 @@ export function layoutSequenceDiagram(sequence, theme, metadata) {
400
400
  function buildSequenceEdge(id, source, target, arrowType, theme, showArrow) {
401
401
  const isDashed = arrowType === 'dashed' || arrowType === 'dashed_open';
402
402
  const isOpen = arrowType === 'solid_open' || arrowType === 'dashed_open';
403
+ const markerSize = theme.edgeDefaults.markerSize;
403
404
  return {
404
405
  id,
405
406
  source,
@@ -414,8 +415,8 @@ function buildSequenceEdge(id, source, target, arrowType, theme, showArrow) {
414
415
  markerEnd: {
415
416
  type: MarkerType.ArrowClosed,
416
417
  color: theme.color.gray3,
417
- width: 10,
418
- height: 10,
418
+ width: markerSize,
419
+ height: markerSize,
419
420
  },
420
421
  } : {}),
421
422
  };
@@ -424,6 +425,7 @@ function buildSequenceRoutedEdge(id, source, target, arrowType, theme, showArrow
424
425
  const isDashed = arrowType === 'dashed' || arrowType === 'dashed_open';
425
426
  const isOpen = arrowType === 'solid_open' || arrowType === 'dashed_open';
426
427
  const svgPath = generateRoundedPath(points, 14);
428
+ const markerSize = theme.edgeDefaults.markerSize;
427
429
  return {
428
430
  id,
429
431
  source,
@@ -439,8 +441,8 @@ function buildSequenceRoutedEdge(id, source, target, arrowType, theme, showArrow
439
441
  markerEnd: {
440
442
  type: MarkerType.ArrowClosed,
441
443
  color: theme.color.gray3,
442
- width: 10,
443
- height: 10,
444
+ width: markerSize,
445
+ height: markerSize,
444
446
  },
445
447
  } : {}),
446
448
  };
@@ -107,7 +107,7 @@ const edgeDefaults = {
107
107
  labelBgBorderRadius: 4,
108
108
  dashedPattern: '5,5',
109
109
  thickWidth: 3,
110
- markerSize: 12,
110
+ markerSize: 14,
111
111
  };
112
112
  // ─── Shared Node Base Styles ────────────────────────────────────────────────
113
113
  const nodeBase = {
@@ -160,7 +160,7 @@ const nodeStyles = {
160
160
  sequence: {
161
161
  number: { size: 22, fontSize: font.size.xs, borderWidth: '1.5px' },
162
162
  block: { borderWidth: borderWidth.sm, dividerWidth: borderWidth.sm, labelCharWidth: 8, labelOffsetBase: 24 },
163
- note: { borderWidth: borderWidth.sm, paddingY: space[1] },
163
+ note: { borderWidth: borderWidth.sm, paddingY: space[1], backgroundColor: color.darkBg3, textColor: color.gray4 },
164
164
  },
165
165
  subgraph: {
166
166
  pointerEvents: 'none',
@@ -107,7 +107,7 @@ const edgeDefaults = {
107
107
  labelBgBorderRadius: 4,
108
108
  dashedPattern: '5,5',
109
109
  thickWidth: 3,
110
- markerSize: 12,
110
+ markerSize: 14,
111
111
  };
112
112
  // ─── Shared Node Base Styles ────────────────────────────────────────────────
113
113
  const nodeBase = {
@@ -160,7 +160,8 @@ const nodeStyles = {
160
160
  sequence: {
161
161
  number: { size: 22, fontSize: font.size.xs, borderWidth: '1.5px' },
162
162
  block: { borderWidth: borderWidth.sm, dividerWidth: borderWidth.sm, labelCharWidth: 8, labelOffsetBase: 24 },
163
- note: { borderWidth: borderWidth.sm, paddingY: space[1] },
163
+ // Light mode notes: use a darker neutral so they don't disappear on white backgrounds.
164
+ note: { borderWidth: borderWidth.sm, paddingY: space[1], backgroundColor: color.gray2, textColor: color.gray5 },
164
165
  },
165
166
  subgraph: {
166
167
  pointerEvents: 'none',
@@ -81,6 +81,8 @@ export interface ThemeTokens {
81
81
  note: {
82
82
  borderWidth: string;
83
83
  paddingY: string;
84
+ backgroundColor: string;
85
+ textColor: string;
84
86
  };
85
87
  };
86
88
  subgraph: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graph-artifact/core",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Composable Mermaid-like parser, layout engine, and React renderer for interactive diagram artifacts.",
5
5
  "license": "MIT",
6
6
  "repository": {