@mindlogic-ai/logician-ui 4.0.0-alpha.6 → 4.0.0-alpha.8

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.
@@ -7,7 +7,11 @@ export declare const Tree: {
7
7
  BranchIndicator: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewBranchIndicatorProps & import("react").RefAttributes<HTMLDivElement>>;
8
8
  BranchText: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewBranchTextProps & import("react").RefAttributes<HTMLSpanElement>>;
9
9
  BranchContent: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewBranchContentProps & import("react").RefAttributes<HTMLDivElement>>;
10
- BranchIndentGuide: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewBranchIndentGuideProps & import("react").RefAttributes<HTMLDivElement>>;
10
+ BranchIndentGuide: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewBranchIndentGuideProps & {
11
+ elbow?: boolean;
12
+ footLength?: string | number;
13
+ guideColor?: string;
14
+ } & import("react").RefAttributes<HTMLDivElement>>;
11
15
  Item: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewItemProps & import("react").RefAttributes<HTMLDivElement>>;
12
16
  ItemText: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewItemTextProps & import("react").RefAttributes<HTMLSpanElement>>;
13
17
  ItemIndicator: import("react").ForwardRefExoticComponent<import("@chakra-ui/react").TreeViewItemIndicatorProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"Tree.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/Tree.tsx"],"names":[],"mappings":"AAkBA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;CAkBhB,CAAC"}
1
+ {"version":3,"file":"Tree.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/Tree.tsx"],"names":[],"mappings":"AAkBA,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;CAkBhB,CAAC"}
@@ -7,7 +7,35 @@ export type TreeBranchTriggerProps = TreeViewBranchTriggerProps;
7
7
  export type TreeBranchIndicatorProps = TreeViewBranchIndicatorProps;
8
8
  export type TreeBranchTextProps = TreeViewBranchTextProps;
9
9
  export type TreeBranchContentProps = TreeViewBranchContentProps;
10
- export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps;
10
+ export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps & {
11
+ /**
12
+ * Draw `├` / `└` guide lines instead of a plain vertical rail: every
13
+ * row gets a horizontal foot joining it to its parent rail, middle
14
+ * rows keep the vertical (`├`), and the last row of each group stops
15
+ * at its centre (`└`). Off by default to preserve the existing
16
+ * plain-vertical-rail look. See `TreeBranchIndentGuide`.
17
+ * @default false
18
+ */
19
+ elbow?: boolean;
20
+ /**
21
+ * Length of the elbow foot — the horizontal cross-stroke that joins
22
+ * the rail to the row content, drawn from the parent rail column
23
+ * toward the text. In elbow mode each level indents 4px further than
24
+ * the plain-rail default, and since the default foot length doesn't
25
+ * grow with it, the foot ends 8px short of the row text. Any Chakra
26
+ * width value. Only applies when `elbow` is set.
27
+ * @default 'calc(var(--tree-indentation) - 4px)'
28
+ */
29
+ footLength?: string | number;
30
+ /**
31
+ * Color of the guide lines (rail, elbow foot and vertical segments).
32
+ * Any Chakra color token. The default is a mode-flipping semantic
33
+ * token, so it darkens in light mode and lightens in dark mode
34
+ * automatically.
35
+ * @default 'border.default'
36
+ */
37
+ guideColor?: string;
38
+ };
11
39
  export type TreeItemProps = TreeViewItemProps;
12
40
  export type TreeItemTextProps = TreeViewItemTextProps;
13
41
  export type TreeItemIndicatorProps = TreeViewItemIndicatorProps;
@@ -1 +1 @@
1
- {"version":3,"file":"Tree.types.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/Tree.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,8BAA8B,EAC9B,4BAA4B,EAC5B,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,EAC1B,0BAA0B,EAC1B,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAClD,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,wBAAwB,GAAG,4BAA4B,CAAC;AACpE,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAC1D,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,0BAA0B,GAAG,8BAA8B,CAAC;AACxE,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AACtD,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,kBAAkB,CAAC;AAIhD,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAChD,WAAW,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC,CACnD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,0BAA0B,GAAG,UAAU,CACjD,WAAW,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CACpD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,wBAAwB,GAAG,UAAU,CAC/C,WAAW,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAClD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAC7C,WAAW,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAChD,CAAC,CAAC,CAAC,CAAC;AAEL,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"Tree.types.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/Tree.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,8BAA8B,EAC9B,4BAA4B,EAC5B,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,EAC1B,0BAA0B,EAC1B,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAClD,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,wBAAwB,GAAG,4BAA4B,CAAC;AACpE,MAAM,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAC1D,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,0BAA0B,GAAG,8BAA8B,GAAG;IACxE;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AACF,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AACtD,MAAM,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAChE,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,kBAAkB,CAAC;AAIhD,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAChD,WAAW,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC,CACnD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,0BAA0B,GAAG,UAAU,CACjD,WAAW,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CACpD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,wBAAwB,GAAG,UAAU,CAC/C,WAAW,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAClD,CAAC,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAC7C,WAAW,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAChD,CAAC,CAAC,CAAC,CAAC;AAEL,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,3 +1,7 @@
1
1
  import { TreeView as ChakraTreeView } from '@chakra-ui/react';
2
- export declare const TreeBranchIndentGuide: import("react").ForwardRefExoticComponent<ChakraTreeView.BranchIndentGuideProps & import("react").RefAttributes<HTMLDivElement>>;
2
+ export declare const TreeBranchIndentGuide: import("react").ForwardRefExoticComponent<ChakraTreeView.BranchIndentGuideProps & {
3
+ elbow?: boolean;
4
+ footLength?: string | number;
5
+ guideColor?: string;
6
+ } & import("react").RefAttributes<HTMLDivElement>>;
3
7
  //# sourceMappingURL=TreeBranchIndentGuide.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TreeBranchIndentGuide.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAI9D,eAAO,MAAM,qBAAqB,kIAahC,CAAC"}
1
+ {"version":3,"file":"TreeBranchIndentGuide.d.ts","sourceRoot":"","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AA4C9D,eAAO,MAAM,qBAAqB;;;;kDAmGjC,CAAC"}
@@ -5,14 +5,113 @@ var jsxRuntime = require('react/jsx-runtime');
5
5
  var React = require('react');
6
6
  var react = require('@chakra-ui/react');
7
7
 
8
- const TreeBranchIndentGuide = React.forwardRef((props, ref) => {
9
- // Chakra v3's `branchIndentGuide` slot already renders the vertical
10
- // line via `position: absolute`, `width: 1px`, `bg: border`, with
11
- // `insetInlineStart` auto-calculated from tree depth. Don't add
12
- // `ms`/`ps` (breaks the depth math, pushes the line over content)
13
- // or `borderInlineStartWidth` (stacks a second 1px stroke on top of
14
- // the slot's own bg-painted 1px). Only override `bg` to lighten.
15
- return (jsxRuntime.jsx(react.TreeView.BranchIndentGuide, { ref: ref, bg: "border.subtle", ...props }));
8
+ // Extra per-level indentation in elbow mode. Guide-line trees read better
9
+ // with a little more horizontal air between the elbow and the row text,
10
+ // so each level indents this much further than the recipe default. The
11
+ // foot length does NOT grow with it (its default is derived from the
12
+ // original `--tree-indentation`), so the foot-to-text gap widens by the
13
+ // same amount.
14
+ const ELBOW_EXTRA_INDENT = '4px';
15
+ // The widened per-level indentation, defined on the guide's sibling rows
16
+ // so both the recipe's offset math and our pseudo-element math can share
17
+ // it. Root rows (depth 1) have no guide sibling, but their offset
18
+ // multiplies indentation by zero, so every level stays consistent.
19
+ const ELBOW_INDENT_VARS = {
20
+ '--tree-elbow-indentation': `calc(var(--tree-indentation) + ${ELBOW_EXTRA_INDENT})`,
21
+ };
22
+ // Re-derive the recipe's `--tree-indentation-offset` (which feeds
23
+ // `--tree-offset`, the row's padding-start) from the widened indentation.
24
+ // Must be set on the same elements the recipe defines it on
25
+ // (item / branch-control) — inheritance can't override those.
26
+ const ELBOW_OFFSET_VARS = {
27
+ '--tree-indentation-offset': 'calc(var(--tree-elbow-indentation) * var(--tree-depth))',
28
+ };
29
+ // The parent rail column, measured from a child row (item / branch-control).
30
+ // A row's content starts at its own `--tree-offset`, and the parent rail
31
+ // sits exactly one (widened) indentation to its left (verified against the
32
+ // recipe's offset math for both the guide and the row slots).
33
+ const ROW_RAIL_COLUMN = 'calc(var(--tree-offset) - var(--tree-elbow-indentation))';
34
+ // The same column, computed from the `branch` element itself. A branch
35
+ // only carries Ark's inline `--depth` (the recipe's `--tree-offset` is
36
+ // defined on item/branch-control, not here), so expand the recipe math:
37
+ // rail(x) = padding + indentation * (depth - 2) + icon * 0.5 * (depth - 1)
38
+ // — algebraically identical to ROW_RAIL_COLUMN for a row at `--depth`.
39
+ const BRANCH_RAIL_COLUMN = 'calc(var(--tree-padding-inline) + var(--tree-elbow-indentation) * (var(--depth) - 2) + var(--tree-icon-size) * 0.5 * (var(--depth) - 1))';
40
+ const TreeBranchIndentGuide = React.forwardRef(({ elbow = false, footLength = 'calc(var(--tree-indentation) - 4px)', guideColor = 'border.default', ...props }, ref) => {
41
+ // Chakra v3's `branchIndentGuide` slot renders ONE absolutely-positioned
42
+ // vertical line per `branchContent` (its first child), `height: 100%`,
43
+ // with `insetInlineStart` auto-calculated from tree depth. Don't add
44
+ // `ms`/`ps` (breaks the depth math) or `borderInlineStartWidth` (stacks
45
+ // a second 1px stroke on the slot's own bg-painted 1px) — only `bg`.
46
+ //
47
+ // That single full-height rail cannot form a `└` on the last row: it
48
+ // always paints past the last row's centre down to the content bottom.
49
+ // So in `elbow` mode the slot's own rail is hidden (`bg: transparent`,
50
+ // the element stays as the CSS sibling anchor) and the guide lines are
51
+ // re-drawn per row on the guide's sibling rows (`[data-part="item"]`
52
+ // leaves and `[data-part="branch"]` subtrees):
53
+ //
54
+ // - every row gets a `::before` foot — the horizontal cross-stroke
55
+ // from the rail column toward the row content, pinned to the row's
56
+ // vertical centre;
57
+ // - non-last rows get a full-height vertical segment (`├`); for a
58
+ // branch it is drawn on the `[data-part="branch"]` element so it
59
+ // passes through the whole expanded subtree, like the original rail
60
+ // (zIndex 1 for the same paint-above-row-hover reason);
61
+ // - the LAST row's segment stops at the row's vertical centre where
62
+ // the foot meets it (`└`); for a branch that means half the
63
+ // branch-control only, so an expanded last branch hangs below the
64
+ // elbow with no rail running past it.
65
+ const foot = {
66
+ content: '""',
67
+ position: 'absolute',
68
+ insetBlockStart: '50%',
69
+ insetInlineStart: ROW_RAIL_COLUMN,
70
+ width: footLength,
71
+ height: '1px',
72
+ bg: guideColor,
73
+ };
74
+ const rail = {
75
+ content: '""',
76
+ position: 'absolute',
77
+ insetBlockStart: '0',
78
+ insetInlineStart: ROW_RAIL_COLUMN,
79
+ width: '1px',
80
+ bg: guideColor,
81
+ };
82
+ const elbowCss = elbow
83
+ ? {
84
+ '& ~ [data-part="item"]': {
85
+ ...ELBOW_INDENT_VARS,
86
+ ...ELBOW_OFFSET_VARS,
87
+ },
88
+ // branch-control reads --tree-elbow-indentation by inheritance,
89
+ // but the offset override must sit on the control itself.
90
+ '& ~ [data-part="branch"]': ELBOW_INDENT_VARS,
91
+ '& ~ [data-part="branch"] > [data-part="branch-control"]': ELBOW_OFFSET_VARS,
92
+ '& ~ [data-part="item"]::before': foot,
93
+ '& ~ [data-part="branch"] > [data-part="branch-control"]::before': foot,
94
+ '& ~ [data-part="item"]:not(:last-child)::after': {
95
+ ...rail,
96
+ insetBlockEnd: '0',
97
+ },
98
+ '& ~ [data-part="item"]:last-child::after': {
99
+ ...rail,
100
+ height: '50%',
101
+ },
102
+ '& ~ [data-part="branch"]:not(:last-child)::before': {
103
+ ...rail,
104
+ insetBlockEnd: '0',
105
+ insetInlineStart: BRANCH_RAIL_COLUMN,
106
+ zIndex: 1,
107
+ },
108
+ '& ~ [data-part="branch"]:last-child > [data-part="branch-control"]::after': {
109
+ ...rail,
110
+ height: '50%',
111
+ },
112
+ }
113
+ : undefined;
114
+ return (jsxRuntime.jsx(react.TreeView.BranchIndentGuide, { ref: ref, bg: elbow ? 'transparent' : guideColor, css: elbowCss, ...props }));
16
115
  });
17
116
  TreeBranchIndentGuide.displayName = 'TreeBranchIndentGuide';
18
117
 
@@ -1 +1 @@
1
- {"version":3,"file":"TreeBranchIndentGuide.js","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"sourcesContent":[null],"names":["forwardRef","_jsx","ChakraTreeView"],"mappings":";;;;;;;AAKO,MAAM,qBAAqB,GAAGA,gBAAU,CAG7C,CAAC,KAAK,EAAE,GAAG,KAAI;;;;;;;AAOf,IAAA,QACEC,cAAA,CAACC,cAAc,CAAC,iBAAiB,IAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAC,eAAe,KAAK,KAAK,EAAA,CAAI;AAEhF,CAAC;AACD,qBAAqB,CAAC,WAAW,GAAG,uBAAuB;;;;"}
1
+ {"version":3,"file":"TreeBranchIndentGuide.js","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"sourcesContent":[null],"names":["forwardRef","_jsx","ChakraTreeView"],"mappings":";;;;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GAAG,KAAK;AAEhC;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG;IACxB,0BAA0B,EAAE,CAAA,+BAAA,EAAkC,kBAAkB,CAAA,CAAA,CAAG;CAC3E;AAEV;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG;AACxB,IAAA,2BAA2B,EACzB,yDAAyD;CACnD;AAEV;AACA;AACA;AACA;AACA,MAAM,eAAe,GACnB,0DAA0D;AAE5D;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GACtB,0IAA0I;AAErI,MAAM,qBAAqB,GAAGA,gBAAU,CAI7C,CACE,EACE,KAAK,GAAG,KAAK,EACb,UAAU,GAAG,qCAAqC,EAClD,UAAU,GAAG,gBAAgB,EAC7B,GAAG,KAAK,EACT,EACD,GAAG,KACD;;;;;;;;;;;;;;;;;;;;;;;;;AAyBF,IAAA,MAAM,IAAI,GAAG;AACX,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,eAAe,EAAE,KAAK;AACtB,QAAA,gBAAgB,EAAE,eAAe;AACjC,QAAA,KAAK,EAAE,UAAU;AACjB,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,EAAE,EAAE,UAAU;KACN;AACV,IAAA,MAAM,IAAI,GAAG;AACX,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,eAAe,EAAE,GAAG;AACpB,QAAA,gBAAgB,EAAE,eAAe;AACjC,QAAA,KAAK,EAAE,KAAK;AACZ,QAAA,EAAE,EAAE,UAAU;KACN;IACV,MAAM,QAAQ,GAAG;AACf,UAAE;AACE,YAAA,wBAAwB,EAAE;AACxB,gBAAA,GAAG,iBAAiB;AACpB,gBAAA,GAAG,iBAAiB;AACrB,aAAA;;;AAGD,YAAA,0BAA0B,EAAE,iBAAiB;AAC7C,YAAA,yDAAyD,EACvD,iBAAiB;AACnB,YAAA,gCAAgC,EAAE,IAAI;AACtC,YAAA,iEAAiE,EAC/D,IAAI;AACN,YAAA,gDAAgD,EAAE;AAChD,gBAAA,GAAG,IAAI;AACP,gBAAA,aAAa,EAAE,GAAG;AACnB,aAAA;AACD,YAAA,0CAA0C,EAAE;AAC1C,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,KAAK;AACd,aAAA;AACD,YAAA,mDAAmD,EAAE;AACnD,gBAAA,GAAG,IAAI;AACP,gBAAA,aAAa,EAAE,GAAG;AAClB,gBAAA,gBAAgB,EAAE,kBAAkB;AACpC,gBAAA,MAAM,EAAE,CAAC;AACV,aAAA;AACD,YAAA,2EAA2E,EACzE;AACE,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,KAAK;AACd,aAAA;AACJ;UACD,SAAS;AAEb,IAAA,QACEC,cAAA,CAACC,cAAc,CAAC,iBAAiB,EAAA,EAC/B,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,KAAK,GAAG,aAAa,GAAG,UAAU,EACtC,GAAG,EAAE,QAAQ,EAAA,GACT,KAAK,EAAA,CACT;AAEN,CAAC;AAEH,qBAAqB,CAAC,WAAW,GAAG,uBAAuB;;;;"}
@@ -3,14 +3,113 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef } from 'react';
4
4
  import { TreeView } from '@chakra-ui/react';
5
5
 
6
- const TreeBranchIndentGuide = forwardRef((props, ref) => {
7
- // Chakra v3's `branchIndentGuide` slot already renders the vertical
8
- // line via `position: absolute`, `width: 1px`, `bg: border`, with
9
- // `insetInlineStart` auto-calculated from tree depth. Don't add
10
- // `ms`/`ps` (breaks the depth math, pushes the line over content)
11
- // or `borderInlineStartWidth` (stacks a second 1px stroke on top of
12
- // the slot's own bg-painted 1px). Only override `bg` to lighten.
13
- return (jsx(TreeView.BranchIndentGuide, { ref: ref, bg: "border.subtle", ...props }));
6
+ // Extra per-level indentation in elbow mode. Guide-line trees read better
7
+ // with a little more horizontal air between the elbow and the row text,
8
+ // so each level indents this much further than the recipe default. The
9
+ // foot length does NOT grow with it (its default is derived from the
10
+ // original `--tree-indentation`), so the foot-to-text gap widens by the
11
+ // same amount.
12
+ const ELBOW_EXTRA_INDENT = '4px';
13
+ // The widened per-level indentation, defined on the guide's sibling rows
14
+ // so both the recipe's offset math and our pseudo-element math can share
15
+ // it. Root rows (depth 1) have no guide sibling, but their offset
16
+ // multiplies indentation by zero, so every level stays consistent.
17
+ const ELBOW_INDENT_VARS = {
18
+ '--tree-elbow-indentation': `calc(var(--tree-indentation) + ${ELBOW_EXTRA_INDENT})`,
19
+ };
20
+ // Re-derive the recipe's `--tree-indentation-offset` (which feeds
21
+ // `--tree-offset`, the row's padding-start) from the widened indentation.
22
+ // Must be set on the same elements the recipe defines it on
23
+ // (item / branch-control) — inheritance can't override those.
24
+ const ELBOW_OFFSET_VARS = {
25
+ '--tree-indentation-offset': 'calc(var(--tree-elbow-indentation) * var(--tree-depth))',
26
+ };
27
+ // The parent rail column, measured from a child row (item / branch-control).
28
+ // A row's content starts at its own `--tree-offset`, and the parent rail
29
+ // sits exactly one (widened) indentation to its left (verified against the
30
+ // recipe's offset math for both the guide and the row slots).
31
+ const ROW_RAIL_COLUMN = 'calc(var(--tree-offset) - var(--tree-elbow-indentation))';
32
+ // The same column, computed from the `branch` element itself. A branch
33
+ // only carries Ark's inline `--depth` (the recipe's `--tree-offset` is
34
+ // defined on item/branch-control, not here), so expand the recipe math:
35
+ // rail(x) = padding + indentation * (depth - 2) + icon * 0.5 * (depth - 1)
36
+ // — algebraically identical to ROW_RAIL_COLUMN for a row at `--depth`.
37
+ const BRANCH_RAIL_COLUMN = 'calc(var(--tree-padding-inline) + var(--tree-elbow-indentation) * (var(--depth) - 2) + var(--tree-icon-size) * 0.5 * (var(--depth) - 1))';
38
+ const TreeBranchIndentGuide = forwardRef(({ elbow = false, footLength = 'calc(var(--tree-indentation) - 4px)', guideColor = 'border.default', ...props }, ref) => {
39
+ // Chakra v3's `branchIndentGuide` slot renders ONE absolutely-positioned
40
+ // vertical line per `branchContent` (its first child), `height: 100%`,
41
+ // with `insetInlineStart` auto-calculated from tree depth. Don't add
42
+ // `ms`/`ps` (breaks the depth math) or `borderInlineStartWidth` (stacks
43
+ // a second 1px stroke on the slot's own bg-painted 1px) — only `bg`.
44
+ //
45
+ // That single full-height rail cannot form a `└` on the last row: it
46
+ // always paints past the last row's centre down to the content bottom.
47
+ // So in `elbow` mode the slot's own rail is hidden (`bg: transparent`,
48
+ // the element stays as the CSS sibling anchor) and the guide lines are
49
+ // re-drawn per row on the guide's sibling rows (`[data-part="item"]`
50
+ // leaves and `[data-part="branch"]` subtrees):
51
+ //
52
+ // - every row gets a `::before` foot — the horizontal cross-stroke
53
+ // from the rail column toward the row content, pinned to the row's
54
+ // vertical centre;
55
+ // - non-last rows get a full-height vertical segment (`├`); for a
56
+ // branch it is drawn on the `[data-part="branch"]` element so it
57
+ // passes through the whole expanded subtree, like the original rail
58
+ // (zIndex 1 for the same paint-above-row-hover reason);
59
+ // - the LAST row's segment stops at the row's vertical centre where
60
+ // the foot meets it (`└`); for a branch that means half the
61
+ // branch-control only, so an expanded last branch hangs below the
62
+ // elbow with no rail running past it.
63
+ const foot = {
64
+ content: '""',
65
+ position: 'absolute',
66
+ insetBlockStart: '50%',
67
+ insetInlineStart: ROW_RAIL_COLUMN,
68
+ width: footLength,
69
+ height: '1px',
70
+ bg: guideColor,
71
+ };
72
+ const rail = {
73
+ content: '""',
74
+ position: 'absolute',
75
+ insetBlockStart: '0',
76
+ insetInlineStart: ROW_RAIL_COLUMN,
77
+ width: '1px',
78
+ bg: guideColor,
79
+ };
80
+ const elbowCss = elbow
81
+ ? {
82
+ '& ~ [data-part="item"]': {
83
+ ...ELBOW_INDENT_VARS,
84
+ ...ELBOW_OFFSET_VARS,
85
+ },
86
+ // branch-control reads --tree-elbow-indentation by inheritance,
87
+ // but the offset override must sit on the control itself.
88
+ '& ~ [data-part="branch"]': ELBOW_INDENT_VARS,
89
+ '& ~ [data-part="branch"] > [data-part="branch-control"]': ELBOW_OFFSET_VARS,
90
+ '& ~ [data-part="item"]::before': foot,
91
+ '& ~ [data-part="branch"] > [data-part="branch-control"]::before': foot,
92
+ '& ~ [data-part="item"]:not(:last-child)::after': {
93
+ ...rail,
94
+ insetBlockEnd: '0',
95
+ },
96
+ '& ~ [data-part="item"]:last-child::after': {
97
+ ...rail,
98
+ height: '50%',
99
+ },
100
+ '& ~ [data-part="branch"]:not(:last-child)::before': {
101
+ ...rail,
102
+ insetBlockEnd: '0',
103
+ insetInlineStart: BRANCH_RAIL_COLUMN,
104
+ zIndex: 1,
105
+ },
106
+ '& ~ [data-part="branch"]:last-child > [data-part="branch-control"]::after': {
107
+ ...rail,
108
+ height: '50%',
109
+ },
110
+ }
111
+ : undefined;
112
+ return (jsx(TreeView.BranchIndentGuide, { ref: ref, bg: elbow ? 'transparent' : guideColor, css: elbowCss, ...props }));
14
113
  });
15
114
  TreeBranchIndentGuide.displayName = 'TreeBranchIndentGuide';
16
115
 
@@ -1 +1 @@
1
- {"version":3,"file":"TreeBranchIndentGuide.mjs","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"sourcesContent":[null],"names":["_jsx","ChakraTreeView"],"mappings":";;;;;AAKO,MAAM,qBAAqB,GAAG,UAAU,CAG7C,CAAC,KAAK,EAAE,GAAG,KAAI;;;;;;;AAOf,IAAA,QACEA,GAAA,CAACC,QAAc,CAAC,iBAAiB,IAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAC,eAAe,KAAK,KAAK,EAAA,CAAI;AAEhF,CAAC;AACD,qBAAqB,CAAC,WAAW,GAAG,uBAAuB;;;;"}
1
+ {"version":3,"file":"TreeBranchIndentGuide.mjs","sources":["../../../src/components/Tree/TreeBranchIndentGuide.tsx"],"sourcesContent":[null],"names":["_jsx","ChakraTreeView"],"mappings":";;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GAAG,KAAK;AAEhC;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG;IACxB,0BAA0B,EAAE,CAAA,+BAAA,EAAkC,kBAAkB,CAAA,CAAA,CAAG;CAC3E;AAEV;AACA;AACA;AACA;AACA,MAAM,iBAAiB,GAAG;AACxB,IAAA,2BAA2B,EACzB,yDAAyD;CACnD;AAEV;AACA;AACA;AACA;AACA,MAAM,eAAe,GACnB,0DAA0D;AAE5D;AACA;AACA;AACA;AACA;AACA,MAAM,kBAAkB,GACtB,0IAA0I;AAErI,MAAM,qBAAqB,GAAG,UAAU,CAI7C,CACE,EACE,KAAK,GAAG,KAAK,EACb,UAAU,GAAG,qCAAqC,EAClD,UAAU,GAAG,gBAAgB,EAC7B,GAAG,KAAK,EACT,EACD,GAAG,KACD;;;;;;;;;;;;;;;;;;;;;;;;;AAyBF,IAAA,MAAM,IAAI,GAAG;AACX,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,eAAe,EAAE,KAAK;AACtB,QAAA,gBAAgB,EAAE,eAAe;AACjC,QAAA,KAAK,EAAE,UAAU;AACjB,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,EAAE,EAAE,UAAU;KACN;AACV,IAAA,MAAM,IAAI,GAAG;AACX,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,UAAU;AACpB,QAAA,eAAe,EAAE,GAAG;AACpB,QAAA,gBAAgB,EAAE,eAAe;AACjC,QAAA,KAAK,EAAE,KAAK;AACZ,QAAA,EAAE,EAAE,UAAU;KACN;IACV,MAAM,QAAQ,GAAG;AACf,UAAE;AACE,YAAA,wBAAwB,EAAE;AACxB,gBAAA,GAAG,iBAAiB;AACpB,gBAAA,GAAG,iBAAiB;AACrB,aAAA;;;AAGD,YAAA,0BAA0B,EAAE,iBAAiB;AAC7C,YAAA,yDAAyD,EACvD,iBAAiB;AACnB,YAAA,gCAAgC,EAAE,IAAI;AACtC,YAAA,iEAAiE,EAC/D,IAAI;AACN,YAAA,gDAAgD,EAAE;AAChD,gBAAA,GAAG,IAAI;AACP,gBAAA,aAAa,EAAE,GAAG;AACnB,aAAA;AACD,YAAA,0CAA0C,EAAE;AAC1C,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,KAAK;AACd,aAAA;AACD,YAAA,mDAAmD,EAAE;AACnD,gBAAA,GAAG,IAAI;AACP,gBAAA,aAAa,EAAE,GAAG;AAClB,gBAAA,gBAAgB,EAAE,kBAAkB;AACpC,gBAAA,MAAM,EAAE,CAAC;AACV,aAAA;AACD,YAAA,2EAA2E,EACzE;AACE,gBAAA,GAAG,IAAI;AACP,gBAAA,MAAM,EAAE,KAAK;AACd,aAAA;AACJ;UACD,SAAS;AAEb,IAAA,QACEA,GAAA,CAACC,QAAc,CAAC,iBAAiB,EAAA,EAC/B,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,KAAK,GAAG,aAAa,GAAG,UAAU,EACtC,GAAG,EAAE,QAAQ,EAAA,GACT,KAAK,EAAA,CACT;AAEN,CAAC;AAEH,qBAAqB,CAAC,WAAW,GAAG,uBAAuB;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/theme/global.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,8CAoEpB,CAAC;AAGH,eAAO,MAAM,MAAM,8CAAY,CAAC"}
1
+ {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/theme/global.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,8CAuEpB,CAAC;AAGH,eAAO,MAAM,MAAM,8CAAY,CAAC"}
@@ -26,7 +26,10 @@ const globalCss = react.defineGlobalStyles({
26
26
  },
27
27
  html: {
28
28
  height: 'var(--chakra-vh)',
29
- fontSize: 16,
29
+ // Global base font size. The whole type/spacing scale is em-relative, so
30
+ // this single value drives the app-wide baseline; surfaces that need a
31
+ // different base (e.g. FactChat chat at 16px) opt in via <ScaledContext>.
32
+ fontSize: 14,
30
33
  fontFamily: [
31
34
  font.pretendard.style.fontFamily,
32
35
  font.inter.style.fontFamily,
@@ -1 +1 @@
1
- {"version":3,"file":"global.js","sources":["../../src/theme/global.ts"],"sourcesContent":[null],"names":["defineGlobalStyles","pretendard","inter","noto"],"mappings":";;;;;;AAIA;;;;;;;AAOG;AACI,MAAM,SAAS,GAAGA,wBAAkB,CAAC;;AAE1C,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;;;AAID,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;AAED,IAAA,IAAI,EAAE;AACJ,QAAA,MAAM,EAAE,kBAAkB;AAC1B,QAAA,QAAQ,EAAE,EAAE;AACZ,QAAA,UAAU,EAAE;YACVC,eAAU,CAAC,KAAK,CAAC,UAAU;YAC3BC,UAAK,CAAC,KAAK,CAAC,UAAU;YACtBC,SAAI,CAAC,KAAK,CAAC,UAAU;SACtB,CAAC,IAAI,CAAC,GAAG,CAAC;AACX,QAAA,QAAQ,EAAE,MAAM;AAChB,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AAED,IAAA,YAAY,EAAE;;;;AAIZ,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;AAED,IAAA,SAAS,EAAE;AACT,QAAA,MAAM,EAAE,kBAAkB;AAC3B,KAAA;AACD,IAAA,sBAAsB,EAAE;AACtB,QAAA,UAAU,EAAED,UAAK,CAAC,KAAK,CAAC,UAAU;AACnC,KAAA;;;;;;AAOD,IAAA,GAAG,EAAE;AACH,QAAA,cAAc,EAAE,MAAM;AACtB,QAAA,cAAc,EAAE,4CAA4C;AAC7D,KAAA;AACD,IAAA,qBAAqB,EAAE;AACrB,QAAA,KAAK,EAAE,MAAM;AACb,QAAA,MAAM,EAAE,MAAM;AACf,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,eAAe,EAAE,gCAAgC;AACjD,QAAA,YAAY,EAAE,QAAQ;AACtB,QAAA,MAAM,EAAE,uBAAuB;AAC/B,QAAA,cAAc,EAAE,aAAa;AAC9B,KAAA;AACD,IAAA,iCAAiC,EAAE;AACjC,QAAA,eAAe,EAAE,gCAAgC;AAClD,KAAA;AACD,IAAA,4BAA4B,EAAE;AAC5B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"global.js","sources":["../../src/theme/global.ts"],"sourcesContent":[null],"names":["defineGlobalStyles","pretendard","inter","noto"],"mappings":";;;;;;AAIA;;;;;;;AAOG;AACI,MAAM,SAAS,GAAGA,wBAAkB,CAAC;;AAE1C,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;;;AAID,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;AAED,IAAA,IAAI,EAAE;AACJ,QAAA,MAAM,EAAE,kBAAkB;;;;AAI1B,QAAA,QAAQ,EAAE,EAAE;AACZ,QAAA,UAAU,EAAE;YACVC,eAAU,CAAC,KAAK,CAAC,UAAU;YAC3BC,UAAK,CAAC,KAAK,CAAC,UAAU;YACtBC,SAAI,CAAC,KAAK,CAAC,UAAU;SACtB,CAAC,IAAI,CAAC,GAAG,CAAC;AACX,QAAA,QAAQ,EAAE,MAAM;AAChB,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AAED,IAAA,YAAY,EAAE;;;;AAIZ,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;AAED,IAAA,SAAS,EAAE;AACT,QAAA,MAAM,EAAE,kBAAkB;AAC3B,KAAA;AACD,IAAA,sBAAsB,EAAE;AACtB,QAAA,UAAU,EAAED,UAAK,CAAC,KAAK,CAAC,UAAU;AACnC,KAAA;;;;;;AAOD,IAAA,GAAG,EAAE;AACH,QAAA,cAAc,EAAE,MAAM;AACtB,QAAA,cAAc,EAAE,4CAA4C;AAC7D,KAAA;AACD,IAAA,qBAAqB,EAAE;AACrB,QAAA,KAAK,EAAE,MAAM;AACb,QAAA,MAAM,EAAE,MAAM;AACf,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,eAAe,EAAE,gCAAgC;AACjD,QAAA,YAAY,EAAE,QAAQ;AACtB,QAAA,MAAM,EAAE,uBAAuB;AAC/B,QAAA,cAAc,EAAE,aAAa;AAC9B,KAAA;AACD,IAAA,iCAAiC,EAAE;AACjC,QAAA,eAAe,EAAE,gCAAgC;AAClD,KAAA;AACD,IAAA,4BAA4B,EAAE;AAC5B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACF,CAAA;;;;"}
@@ -24,7 +24,10 @@ const globalCss = defineGlobalStyles({
24
24
  },
25
25
  html: {
26
26
  height: 'var(--chakra-vh)',
27
- fontSize: 16,
27
+ // Global base font size. The whole type/spacing scale is em-relative, so
28
+ // this single value drives the app-wide baseline; surfaces that need a
29
+ // different base (e.g. FactChat chat at 16px) opt in via <ScaledContext>.
30
+ fontSize: 14,
28
31
  fontFamily: [
29
32
  pretendard.style.fontFamily,
30
33
  inter.style.fontFamily,
@@ -1 +1 @@
1
- {"version":3,"file":"global.mjs","sources":["../../src/theme/global.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAIA;;;;;;;AAOG;AACI,MAAM,SAAS,GAAG,kBAAkB,CAAC;;AAE1C,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;;;AAID,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;AAED,IAAA,IAAI,EAAE;AACJ,QAAA,MAAM,EAAE,kBAAkB;AAC1B,QAAA,QAAQ,EAAE,EAAE;AACZ,QAAA,UAAU,EAAE;YACV,UAAU,CAAC,KAAK,CAAC,UAAU;YAC3B,KAAK,CAAC,KAAK,CAAC,UAAU;YACtB,IAAI,CAAC,KAAK,CAAC,UAAU;SACtB,CAAC,IAAI,CAAC,GAAG,CAAC;AACX,QAAA,QAAQ,EAAE,MAAM;AAChB,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AAED,IAAA,YAAY,EAAE;;;;AAIZ,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;AAED,IAAA,SAAS,EAAE;AACT,QAAA,MAAM,EAAE,kBAAkB;AAC3B,KAAA;AACD,IAAA,sBAAsB,EAAE;AACtB,QAAA,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;AACnC,KAAA;;;;;;AAOD,IAAA,GAAG,EAAE;AACH,QAAA,cAAc,EAAE,MAAM;AACtB,QAAA,cAAc,EAAE,4CAA4C;AAC7D,KAAA;AACD,IAAA,qBAAqB,EAAE;AACrB,QAAA,KAAK,EAAE,MAAM;AACb,QAAA,MAAM,EAAE,MAAM;AACf,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,eAAe,EAAE,gCAAgC;AACjD,QAAA,YAAY,EAAE,QAAQ;AACtB,QAAA,MAAM,EAAE,uBAAuB;AAC/B,QAAA,cAAc,EAAE,aAAa;AAC9B,KAAA;AACD,IAAA,iCAAiC,EAAE;AACjC,QAAA,eAAe,EAAE,gCAAgC;AAClD,KAAA;AACD,IAAA,4BAA4B,EAAE;AAC5B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"global.mjs","sources":["../../src/theme/global.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAIA;;;;;;;AAOG;AACI,MAAM,SAAS,GAAG,kBAAkB,CAAC;;AAE1C,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;;;AAID,IAAA,OAAO,EAAE;QACP,kCAAkC,EAAE,SAAS;QAC7C,gCAAgC,EAAE,SAAS;AAC5C,KAAA;AAED,IAAA,IAAI,EAAE;AACJ,QAAA,MAAM,EAAE,kBAAkB;;;;AAI1B,QAAA,QAAQ,EAAE,EAAE;AACZ,QAAA,UAAU,EAAE;YACV,UAAU,CAAC,KAAK,CAAC,UAAU;YAC3B,KAAK,CAAC,KAAK,CAAC,UAAU;YACtB,IAAI,CAAC,KAAK,CAAC,UAAU;SACtB,CAAC,IAAI,CAAC,GAAG,CAAC;AACX,QAAA,QAAQ,EAAE,MAAM;AAChB,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AAED,IAAA,YAAY,EAAE;;;;AAIZ,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;AAED,IAAA,SAAS,EAAE;AACT,QAAA,MAAM,EAAE,kBAAkB;AAC3B,KAAA;AACD,IAAA,sBAAsB,EAAE;AACtB,QAAA,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU;AACnC,KAAA;;;;;;AAOD,IAAA,GAAG,EAAE;AACH,QAAA,cAAc,EAAE,MAAM;AACtB,QAAA,cAAc,EAAE,4CAA4C;AAC7D,KAAA;AACD,IAAA,qBAAqB,EAAE;AACrB,QAAA,KAAK,EAAE,MAAM;AACb,QAAA,MAAM,EAAE,MAAM;AACf,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACD,IAAA,2BAA2B,EAAE;AAC3B,QAAA,eAAe,EAAE,gCAAgC;AACjD,QAAA,YAAY,EAAE,QAAQ;AACtB,QAAA,MAAM,EAAE,uBAAuB;AAC/B,QAAA,cAAc,EAAE,aAAa;AAC9B,KAAA;AACD,IAAA,iCAAiC,EAAE;AACjC,QAAA,eAAe,EAAE,gCAAgC;AAClD,KAAA;AACD,IAAA,4BAA4B,EAAE;AAC5B,QAAA,UAAU,EAAE,aAAa;AAC1B,KAAA;AACF,CAAA;;;;"}
@@ -2,7 +2,7 @@
2
2
  * Text styles for consistent typography across the application
3
3
  * Names match fontSize tokens for easy migration: fontStyle="h1" → textStyle="h1"
4
4
  *
5
- * Base font size: 16px (set in global.ts)
5
+ * Base font size: 14px (set in global.ts)
6
6
  * Responsive scaling using em units (relative to nearest ancestor font-size):
7
7
  * - Mobile (base): Slightly smaller for better readability on small screens
8
8
  * - Desktop (md+): Original design sizes
@@ -72,7 +72,7 @@ const SHARED_TEXT_STYLE_VALUES = {
72
72
  * Text styles for consistent typography across the application
73
73
  * Names match fontSize tokens for easy migration: fontStyle="h1" → textStyle="h1"
74
74
  *
75
- * Base font size: 16px (set in global.ts)
75
+ * Base font size: 14px (set in global.ts)
76
76
  * Responsive scaling using em units (relative to nearest ancestor font-size):
77
77
  * - Mobile (base): Slightly smaller for better readability on small screens
78
78
  * - Desktop (md+): Original design sizes
@@ -68,7 +68,7 @@ const SHARED_TEXT_STYLE_VALUES = {
68
68
  * Text styles for consistent typography across the application
69
69
  * Names match fontSize tokens for easy migration: fontStyle="h1" → textStyle="h1"
70
70
  *
71
- * Base font size: 16px (set in global.ts)
71
+ * Base font size: 14px (set in global.ts)
72
72
  * Responsive scaling using em units (relative to nearest ancestor font-size):
73
73
  * - Mobile (base): Slightly smaller for better readability on small screens
74
74
  * - Desktop (md+): Original design sizes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindlogic-ai/logician-ui",
3
- "version": "4.0.0-alpha.6",
3
+ "version": "4.0.0-alpha.8",
4
4
  "description": "A comprehensive React design system built on Chakra UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -0,0 +1,220 @@
1
+ import { Box, HStack, Stack, Text } from '@chakra-ui/react';
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+
4
+ import { Tree } from './Tree';
5
+ import { createTreeCollection } from './index';
6
+
7
+ /**
8
+ * Stories for the `elbow` option on `Tree.BranchIndentGuide`.
9
+ *
10
+ * `elbow` turns the plain vertical rails into `├` / `└` guide lines:
11
+ * every row gets a horizontal foot joining it to its parent rail, and
12
+ * the last row of each group stops at its centre (`└`). `guideColor`
13
+ * customizes the line color (default `border.default`, which flips
14
+ * with color mode). Pass both through the `indentGuide` element:
15
+ *
16
+ * <Tree.Node indentGuide={<Tree.BranchIndentGuide elbow />} ... />
17
+ */
18
+
19
+ interface Node {
20
+ id: string;
21
+ name: string;
22
+ children?: Node[];
23
+ }
24
+
25
+ const orgSample: Node = {
26
+ id: 'ROOT',
27
+ name: '',
28
+ children: [
29
+ {
30
+ id: 'hq',
31
+ name: '본사',
32
+ children: [
33
+ {
34
+ id: 'hq-dev',
35
+ name: '개발본부',
36
+ children: [
37
+ {
38
+ id: 'hq-dev-backend',
39
+ name: '백엔드그룹',
40
+ children: [
41
+ { id: 'hq-dev-backend-api', name: 'API팀' },
42
+ { id: 'hq-dev-backend-db', name: 'DB팀' },
43
+ { id: 'hq-dev-backend-platform', name: '플랫폼팀' },
44
+ ],
45
+ },
46
+ {
47
+ id: 'hq-dev-frontend',
48
+ name: '프론트엔드그룹',
49
+ children: [
50
+ { id: 'hq-dev-frontend-web', name: '웹팀' },
51
+ { id: 'hq-dev-frontend-mobile', name: '모바일팀' },
52
+ ],
53
+ },
54
+ ],
55
+ },
56
+ {
57
+ id: 'hq-sales',
58
+ name: '영업본부',
59
+ children: [
60
+ { id: 'hq-sales-domestic', name: '국내영업팀' },
61
+ { id: 'hq-sales-global', name: '해외영업팀' },
62
+ ],
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ id: 'rnd',
68
+ name: 'R&D센터',
69
+ children: [
70
+ { id: 'rnd-ml', name: '머신러닝팀' },
71
+ { id: 'rnd-data', name: '데이터팀' },
72
+ ],
73
+ },
74
+ ],
75
+ };
76
+
77
+ const orgCollection = createTreeCollection<Node>({
78
+ rootNode: orgSample,
79
+ nodeToValue: (node) => node.id,
80
+ nodeToString: (node) => node.name,
81
+ });
82
+
83
+ const allBranchIds = (function collect(n: Node): string[] {
84
+ if (!n.children) return [];
85
+ return n.children.flatMap((c) => (c.children ? [c.id, ...collect(c)] : []));
86
+ })(orgSample);
87
+
88
+ const renderNode = ({
89
+ node,
90
+ nodeState,
91
+ }: {
92
+ node: Node;
93
+ nodeState: { isBranch: boolean };
94
+ }) =>
95
+ nodeState.isBranch ? (
96
+ <Tree.BranchControl>
97
+ <Tree.BranchIndicator />
98
+ <Tree.BranchText>{node.name}</Tree.BranchText>
99
+ </Tree.BranchControl>
100
+ ) : (
101
+ <Tree.Item>
102
+ <Tree.ItemText>{node.name}</Tree.ItemText>
103
+ </Tree.Item>
104
+ );
105
+
106
+ const meta = {
107
+ title: 'Components/Tree',
108
+ component: Tree.Root,
109
+ parameters: { layout: 'padded' },
110
+ } satisfies Meta<typeof Tree.Root>;
111
+
112
+ export default meta;
113
+
114
+ type Story = StoryObj<typeof meta>;
115
+
116
+ /**
117
+ * `elbow` guides — the innermost rail grows a horizontal foot so each
118
+ * row connects to its parent column like a classic file-tree.
119
+ */
120
+ export const WithElbowGuides: Story = {
121
+ args: {
122
+ collection: orgCollection,
123
+ 'aria-label': '조직',
124
+ defaultExpandedValue: allBranchIds,
125
+ defaultSelectedValue: ['hq-dev-frontend-web'],
126
+ },
127
+ render: (args) => (
128
+ <Box maxWidth="360px">
129
+ <Tree.Root {...args}>
130
+ <Tree.Tree>
131
+ <Tree.Node
132
+ indentGuide={<Tree.BranchIndentGuide elbow />}
133
+ render={renderNode}
134
+ />
135
+ </Tree.Tree>
136
+ </Tree.Root>
137
+ </Box>
138
+ ),
139
+ };
140
+
141
+ /**
142
+ * `guideColor` — customize the guide line color with any Chakra color
143
+ * token. The default `border.default` flips with color mode; pick a
144
+ * stronger token (or any palette color) for more contrast.
145
+ */
146
+ export const GuideColor: Story = {
147
+ args: {
148
+ collection: orgCollection,
149
+ 'aria-label': '조직',
150
+ defaultExpandedValue: allBranchIds,
151
+ },
152
+ render: (args) => (
153
+ <HStack align="flex-start" gap={8}>
154
+ {(
155
+ [
156
+ ['border.default (기본)', undefined],
157
+ ['border.strong', 'border.strong'],
158
+ ['primary.light', 'primary.light'],
159
+ ] as const
160
+ ).map(([label, guideColor]) => (
161
+ <Stack key={label} gap={2} width="280px">
162
+ <Text fontSize="sm" fontWeight="semibold" color="fg.muted">
163
+ {label}
164
+ </Text>
165
+ <Tree.Root {...args}>
166
+ <Tree.Tree>
167
+ <Tree.Node
168
+ indentGuide={
169
+ <Tree.BranchIndentGuide elbow guideColor={guideColor} />
170
+ }
171
+ render={renderNode}
172
+ />
173
+ </Tree.Tree>
174
+ </Tree.Root>
175
+ </Stack>
176
+ ))}
177
+ </HStack>
178
+ ),
179
+ };
180
+
181
+ /**
182
+ * Plain vertical rails (default) vs. `elbow` guides, side by side.
183
+ */
184
+ export const ElbowGuideComparison: Story = {
185
+ args: {
186
+ collection: orgCollection,
187
+ 'aria-label': '조직',
188
+ defaultExpandedValue: allBranchIds,
189
+ },
190
+ render: (args) => (
191
+ <HStack align="flex-start" gap={8}>
192
+ <Stack gap={2} width="320px">
193
+ <Text fontSize="sm" fontWeight="semibold" color="fg.muted">
194
+ 기본 — 수직 레일
195
+ </Text>
196
+ <Tree.Root {...args}>
197
+ <Tree.Tree>
198
+ <Tree.Node
199
+ indentGuide={<Tree.BranchIndentGuide />}
200
+ render={renderNode}
201
+ />
202
+ </Tree.Tree>
203
+ </Tree.Root>
204
+ </Stack>
205
+ <Stack gap={2} width="320px">
206
+ <Text fontSize="sm" fontWeight="semibold" color="fg.muted">
207
+ elbow — ├ / └ 가이드 라인
208
+ </Text>
209
+ <Tree.Root {...args}>
210
+ <Tree.Tree>
211
+ <Tree.Node
212
+ indentGuide={<Tree.BranchIndentGuide elbow />}
213
+ render={renderNode}
214
+ />
215
+ </Tree.Tree>
216
+ </Tree.Root>
217
+ </Stack>
218
+ </HStack>
219
+ ),
220
+ };
@@ -23,7 +23,35 @@ export type TreeBranchTriggerProps = TreeViewBranchTriggerProps;
23
23
  export type TreeBranchIndicatorProps = TreeViewBranchIndicatorProps;
24
24
  export type TreeBranchTextProps = TreeViewBranchTextProps;
25
25
  export type TreeBranchContentProps = TreeViewBranchContentProps;
26
- export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps;
26
+ export type TreeBranchIndentGuideProps = TreeViewBranchIndentGuideProps & {
27
+ /**
28
+ * Draw `├` / `└` guide lines instead of a plain vertical rail: every
29
+ * row gets a horizontal foot joining it to its parent rail, middle
30
+ * rows keep the vertical (`├`), and the last row of each group stops
31
+ * at its centre (`└`). Off by default to preserve the existing
32
+ * plain-vertical-rail look. See `TreeBranchIndentGuide`.
33
+ * @default false
34
+ */
35
+ elbow?: boolean;
36
+ /**
37
+ * Length of the elbow foot — the horizontal cross-stroke that joins
38
+ * the rail to the row content, drawn from the parent rail column
39
+ * toward the text. In elbow mode each level indents 4px further than
40
+ * the plain-rail default, and since the default foot length doesn't
41
+ * grow with it, the foot ends 8px short of the row text. Any Chakra
42
+ * width value. Only applies when `elbow` is set.
43
+ * @default 'calc(var(--tree-indentation) - 4px)'
44
+ */
45
+ footLength?: string | number;
46
+ /**
47
+ * Color of the guide lines (rail, elbow foot and vertical segments).
48
+ * Any Chakra color token. The default is a mode-flipping semantic
49
+ * token, so it darkens in light mode and lightens in dark mode
50
+ * automatically.
51
+ * @default 'border.default'
52
+ */
53
+ guideColor?: string;
54
+ };
27
55
  export type TreeItemProps = TreeViewItemProps;
28
56
  export type TreeItemTextProps = TreeViewItemTextProps;
29
57
  export type TreeItemIndicatorProps = TreeViewItemIndicatorProps;
@@ -0,0 +1,213 @@
1
+ import { ChakraProvider } from '@chakra-ui/react';
2
+ import { render } from '@testing-library/react';
3
+
4
+ import { system } from '../../theme';
5
+ import { createTreeCollection } from './index';
6
+ import { Tree } from './Tree';
7
+
8
+ interface N {
9
+ id: string;
10
+ name: string;
11
+ children?: N[];
12
+ }
13
+
14
+ const sample: N = {
15
+ id: 'ROOT',
16
+ name: '',
17
+ children: [{ id: 'a', name: 'A', children: [{ id: 'a1', name: 'A1' }] }],
18
+ };
19
+
20
+ const collection = createTreeCollection<N>({
21
+ rootNode: sample,
22
+ nodeToValue: (n) => n.id,
23
+ nodeToString: (n) => n.name,
24
+ });
25
+
26
+ const renderNode = ({
27
+ node,
28
+ nodeState,
29
+ }: {
30
+ node: N;
31
+ nodeState: { isBranch: boolean };
32
+ }) =>
33
+ nodeState.isBranch ? (
34
+ <Tree.BranchControl>
35
+ <Tree.BranchText>{node.name}</Tree.BranchText>
36
+ </Tree.BranchControl>
37
+ ) : (
38
+ <Tree.Item>
39
+ <Tree.ItemText>{node.name}</Tree.ItemText>
40
+ </Tree.Item>
41
+ );
42
+
43
+ function renderGuide(elbow: boolean, extraProps: { guideColor?: string } = {}) {
44
+ const { container } = render(
45
+ <ChakraProvider value={system}>
46
+ <Tree.Root
47
+ collection={collection}
48
+ aria-label="t"
49
+ defaultExpandedValue={['a']}
50
+ >
51
+ <Tree.Tree>
52
+ <Tree.Node
53
+ indentGuide={
54
+ elbow ? (
55
+ <Tree.BranchIndentGuide elbow {...extraProps} />
56
+ ) : (
57
+ <Tree.BranchIndentGuide {...extraProps} />
58
+ )
59
+ }
60
+ render={renderNode}
61
+ />
62
+ </Tree.Tree>
63
+ </Tree.Root>
64
+ </ChakraProvider>
65
+ );
66
+ const guide = container.querySelector<HTMLElement>(
67
+ '[data-part="branch-indent-guide"]'
68
+ );
69
+ if (!guide) throw new Error('no indent guide rendered');
70
+ return guide.className;
71
+ }
72
+
73
+ // All Chakra/emotion-injected CSS, concatenated.
74
+ const sheetText = () =>
75
+ Array.from(document.querySelectorAll('style'))
76
+ .map((s) => s.textContent ?? '')
77
+ .join('\n');
78
+
79
+ // The foot rule is anchored on the emotion-generated `css-*` class
80
+ // (not the static `chakra-*` slot class), so target that one.
81
+ const styleAnchor = (className: string) => {
82
+ const emotion = className
83
+ .trim()
84
+ .split(/\s+/)
85
+ .find((c) => /^css-/.test(c));
86
+ if (!emotion) throw new Error(`no emotion class in: ${className}`);
87
+ return '\\.' + emotion;
88
+ };
89
+
90
+ /**
91
+ * Regression guard for the `elbow` foot. Chakra renders ONE indent guide
92
+ * per `branchContent` (its first child), followed by that branch's rows
93
+ * as siblings — so the foot must be drawn on those sibling rows, scoped
94
+ * to the elbow guide's own class. An earlier `:last-of-type::after`
95
+ * attempt silently matched nothing (the guide is never the last sibling
96
+ * of its type), which is exactly what these tests pin down.
97
+ */
98
+ describe('Tree.BranchIndentGuide elbow', () => {
99
+ it('draws a ::before foot on the guide’s sibling rows when elbow is set', () => {
100
+ const cls = renderGuide(true);
101
+ const anchor = styleAnchor(cls);
102
+ const css = sheetText();
103
+
104
+ // Foot rule is scoped to THIS guide's class via the sibling combinator,
105
+ // for both leaf rows and branch rows — never applied globally.
106
+ expect(css).toMatch(
107
+ new RegExp(`${anchor}\\s*~\\s*\\[data-part="item"\\]::before`)
108
+ );
109
+ expect(css).toMatch(
110
+ new RegExp(
111
+ `${anchor}\\s*~\\s*\\[data-part="branch"\\]\\s*>\\s*\\[data-part="branch-control"\\]::before`
112
+ )
113
+ );
114
+
115
+ // The foot paints a 1px cross-stroke pinned to the row's vertical
116
+ // centre, starting at the parent rail column.
117
+ const footRule =
118
+ css.match(
119
+ new RegExp(
120
+ `${anchor}\\s*~\\s*\\[data-part="item"\\]::before\\s*\\{[^}]*\\}`
121
+ )
122
+ )?.[0] ?? '';
123
+ expect(footRule).toContain('content:""');
124
+ expect(footRule).toContain('position:absolute');
125
+ expect(footRule).toContain('inset-block-start:50%');
126
+ expect(footRule).toContain('height:1px');
127
+ expect(footRule).toMatch(
128
+ /inset-inline-start:calc\(var\(--tree-offset\) - var\(--tree-elbow-indentation\)\)/
129
+ );
130
+
131
+ // Elbow mode widens per-level indentation: the sibling rows redefine
132
+ // the recipe's indentation-offset from the widened value, pushing the
133
+ // row text right, away from the elbow foot.
134
+ const rowVarsRule =
135
+ css.match(
136
+ new RegExp(`${anchor}\\s*~\\s*\\[data-part="item"\\]\\s*\\{[^}]*\\}`)
137
+ )?.[0] ?? '';
138
+ expect(rowVarsRule).toContain(
139
+ '--tree-elbow-indentation:calc(var(--tree-indentation) + 4px)'
140
+ );
141
+ expect(rowVarsRule).toContain(
142
+ '--tree-indentation-offset:calc(var(--tree-elbow-indentation) * var(--tree-depth))'
143
+ );
144
+ });
145
+
146
+ it('draws no foot for a plain guide (no elbow)', () => {
147
+ const cls = renderGuide(false);
148
+ const anchor = styleAnchor(cls);
149
+ // This specific guide's class must not anchor any ::before foot rule.
150
+ expect(sheetText()).not.toMatch(new RegExp(`${anchor}\\s*~[^{]*::before`));
151
+ });
152
+
153
+ it('stops the vertical segment at the centre of the last row (└)', () => {
154
+ const cls = renderGuide(true);
155
+ const anchor = styleAnchor(cls);
156
+ const css = sheetText();
157
+
158
+ // Middle rows: full-height vertical (├) on the row itself.
159
+ const middleRule =
160
+ css.match(
161
+ new RegExp(
162
+ `${anchor}\\s*~\\s*\\[data-part="item"\\]:not\\(:last-child\\)::after\\s*\\{[^}]*\\}`
163
+ )
164
+ )?.[0] ?? '';
165
+ expect(middleRule).toContain('inset-block-start:0');
166
+ expect(middleRule).toContain('inset-block-end:0');
167
+ expect(middleRule).toContain('width:1px');
168
+
169
+ // Last leaf row: segment ends at 50% so the foot forms a └.
170
+ const lastItemRule =
171
+ css.match(
172
+ new RegExp(
173
+ `${anchor}\\s*~\\s*\\[data-part="item"\\]:last-child::after\\s*\\{[^}]*\\}`
174
+ )
175
+ )?.[0] ?? '';
176
+ expect(lastItemRule).toContain('inset-block-start:0');
177
+ expect(lastItemRule).toContain('height:50%');
178
+
179
+ // Last branch row: half-height segment on its control only, so an
180
+ // expanded last branch has no rail running past its elbow.
181
+ const lastBranchRule =
182
+ css.match(
183
+ new RegExp(
184
+ `${anchor}\\s*~\\s*\\[data-part="branch"\\]:last-child\\s*>\\s*\\[data-part="branch-control"\\]::after\\s*\\{[^}]*\\}`
185
+ )
186
+ )?.[0] ?? '';
187
+ expect(lastBranchRule).toContain('height:50%');
188
+
189
+ // Non-last branch: pass-through rail spans the whole expanded subtree,
190
+ // painted above row hover backgrounds like the original slot rail.
191
+ const passThroughRule =
192
+ css.match(
193
+ new RegExp(
194
+ `${anchor}\\s*~\\s*\\[data-part="branch"\\]:not\\(:last-child\\)::before\\s*\\{[^}]*\\}`
195
+ )
196
+ )?.[0] ?? '';
197
+ expect(passThroughRule).toContain('inset-block-end:0');
198
+ expect(passThroughRule).toContain('z-index:1');
199
+ });
200
+
201
+ it('paints the guide with the custom guideColor token', () => {
202
+ const cls = renderGuide(true, { guideColor: 'red.500' });
203
+ const anchor = styleAnchor(cls);
204
+ const css = sheetText();
205
+ const footRule =
206
+ css.match(
207
+ new RegExp(
208
+ `${anchor}\\s*~\\s*\\[data-part="item"\\]::before\\s*\\{[^}]*\\}`
209
+ )
210
+ )?.[0] ?? '';
211
+ expect(footRule).toMatch(/--chakra-colors-red-500|red\.500/);
212
+ });
213
+ });
@@ -3,18 +3,144 @@ import { TreeView as ChakraTreeView } from '@chakra-ui/react';
3
3
 
4
4
  import { TreeBranchIndentGuideProps } from './Tree.types';
5
5
 
6
+ // Extra per-level indentation in elbow mode. Guide-line trees read better
7
+ // with a little more horizontal air between the elbow and the row text,
8
+ // so each level indents this much further than the recipe default. The
9
+ // foot length does NOT grow with it (its default is derived from the
10
+ // original `--tree-indentation`), so the foot-to-text gap widens by the
11
+ // same amount.
12
+ const ELBOW_EXTRA_INDENT = '4px';
13
+
14
+ // The widened per-level indentation, defined on the guide's sibling rows
15
+ // so both the recipe's offset math and our pseudo-element math can share
16
+ // it. Root rows (depth 1) have no guide sibling, but their offset
17
+ // multiplies indentation by zero, so every level stays consistent.
18
+ const ELBOW_INDENT_VARS = {
19
+ '--tree-elbow-indentation': `calc(var(--tree-indentation) + ${ELBOW_EXTRA_INDENT})`,
20
+ } as const;
21
+
22
+ // Re-derive the recipe's `--tree-indentation-offset` (which feeds
23
+ // `--tree-offset`, the row's padding-start) from the widened indentation.
24
+ // Must be set on the same elements the recipe defines it on
25
+ // (item / branch-control) — inheritance can't override those.
26
+ const ELBOW_OFFSET_VARS = {
27
+ '--tree-indentation-offset':
28
+ 'calc(var(--tree-elbow-indentation) * var(--tree-depth))',
29
+ } as const;
30
+
31
+ // The parent rail column, measured from a child row (item / branch-control).
32
+ // A row's content starts at its own `--tree-offset`, and the parent rail
33
+ // sits exactly one (widened) indentation to its left (verified against the
34
+ // recipe's offset math for both the guide and the row slots).
35
+ const ROW_RAIL_COLUMN =
36
+ 'calc(var(--tree-offset) - var(--tree-elbow-indentation))';
37
+
38
+ // The same column, computed from the `branch` element itself. A branch
39
+ // only carries Ark's inline `--depth` (the recipe's `--tree-offset` is
40
+ // defined on item/branch-control, not here), so expand the recipe math:
41
+ // rail(x) = padding + indentation * (depth - 2) + icon * 0.5 * (depth - 1)
42
+ // — algebraically identical to ROW_RAIL_COLUMN for a row at `--depth`.
43
+ const BRANCH_RAIL_COLUMN =
44
+ 'calc(var(--tree-padding-inline) + var(--tree-elbow-indentation) * (var(--depth) - 2) + var(--tree-icon-size) * 0.5 * (var(--depth) - 1))';
45
+
6
46
  export const TreeBranchIndentGuide = forwardRef<
7
47
  HTMLDivElement,
8
48
  TreeBranchIndentGuideProps
9
- >((props, ref) => {
10
- // Chakra v3's `branchIndentGuide` slot already renders the vertical
11
- // line via `position: absolute`, `width: 1px`, `bg: border`, with
12
- // `insetInlineStart` auto-calculated from tree depth. Don't add
13
- // `ms`/`ps` (breaks the depth math, pushes the line over content)
14
- // or `borderInlineStartWidth` (stacks a second 1px stroke on top of
15
- // the slot's own bg-painted 1px). Only override `bg` to lighten.
16
- return (
17
- <ChakraTreeView.BranchIndentGuide ref={ref} bg="border.subtle" {...props} />
18
- );
19
- });
49
+ >(
50
+ (
51
+ {
52
+ elbow = false,
53
+ footLength = 'calc(var(--tree-indentation) - 4px)',
54
+ guideColor = 'border.default',
55
+ ...props
56
+ },
57
+ ref
58
+ ) => {
59
+ // Chakra v3's `branchIndentGuide` slot renders ONE absolutely-positioned
60
+ // vertical line per `branchContent` (its first child), `height: 100%`,
61
+ // with `insetInlineStart` auto-calculated from tree depth. Don't add
62
+ // `ms`/`ps` (breaks the depth math) or `borderInlineStartWidth` (stacks
63
+ // a second 1px stroke on the slot's own bg-painted 1px) — only `bg`.
64
+ //
65
+ // That single full-height rail cannot form a `└` on the last row: it
66
+ // always paints past the last row's centre down to the content bottom.
67
+ // So in `elbow` mode the slot's own rail is hidden (`bg: transparent`,
68
+ // the element stays as the CSS sibling anchor) and the guide lines are
69
+ // re-drawn per row on the guide's sibling rows (`[data-part="item"]`
70
+ // leaves and `[data-part="branch"]` subtrees):
71
+ //
72
+ // - every row gets a `::before` foot — the horizontal cross-stroke
73
+ // from the rail column toward the row content, pinned to the row's
74
+ // vertical centre;
75
+ // - non-last rows get a full-height vertical segment (`├`); for a
76
+ // branch it is drawn on the `[data-part="branch"]` element so it
77
+ // passes through the whole expanded subtree, like the original rail
78
+ // (zIndex 1 for the same paint-above-row-hover reason);
79
+ // - the LAST row's segment stops at the row's vertical centre where
80
+ // the foot meets it (`└`); for a branch that means half the
81
+ // branch-control only, so an expanded last branch hangs below the
82
+ // elbow with no rail running past it.
83
+ const foot = {
84
+ content: '""',
85
+ position: 'absolute',
86
+ insetBlockStart: '50%',
87
+ insetInlineStart: ROW_RAIL_COLUMN,
88
+ width: footLength,
89
+ height: '1px',
90
+ bg: guideColor,
91
+ } as const;
92
+ const rail = {
93
+ content: '""',
94
+ position: 'absolute',
95
+ insetBlockStart: '0',
96
+ insetInlineStart: ROW_RAIL_COLUMN,
97
+ width: '1px',
98
+ bg: guideColor,
99
+ } as const;
100
+ const elbowCss = elbow
101
+ ? {
102
+ '& ~ [data-part="item"]': {
103
+ ...ELBOW_INDENT_VARS,
104
+ ...ELBOW_OFFSET_VARS,
105
+ },
106
+ // branch-control reads --tree-elbow-indentation by inheritance,
107
+ // but the offset override must sit on the control itself.
108
+ '& ~ [data-part="branch"]': ELBOW_INDENT_VARS,
109
+ '& ~ [data-part="branch"] > [data-part="branch-control"]':
110
+ ELBOW_OFFSET_VARS,
111
+ '& ~ [data-part="item"]::before': foot,
112
+ '& ~ [data-part="branch"] > [data-part="branch-control"]::before':
113
+ foot,
114
+ '& ~ [data-part="item"]:not(:last-child)::after': {
115
+ ...rail,
116
+ insetBlockEnd: '0',
117
+ },
118
+ '& ~ [data-part="item"]:last-child::after': {
119
+ ...rail,
120
+ height: '50%',
121
+ },
122
+ '& ~ [data-part="branch"]:not(:last-child)::before': {
123
+ ...rail,
124
+ insetBlockEnd: '0',
125
+ insetInlineStart: BRANCH_RAIL_COLUMN,
126
+ zIndex: 1,
127
+ },
128
+ '& ~ [data-part="branch"]:last-child > [data-part="branch-control"]::after':
129
+ {
130
+ ...rail,
131
+ height: '50%',
132
+ },
133
+ }
134
+ : undefined;
135
+
136
+ return (
137
+ <ChakraTreeView.BranchIndentGuide
138
+ ref={ref}
139
+ bg={elbow ? 'transparent' : guideColor}
140
+ css={elbowCss}
141
+ {...props}
142
+ />
143
+ );
144
+ }
145
+ );
20
146
  TreeBranchIndentGuide.displayName = 'TreeBranchIndentGuide';
@@ -26,7 +26,10 @@ export const globalCss = defineGlobalStyles({
26
26
 
27
27
  html: {
28
28
  height: 'var(--chakra-vh)',
29
- fontSize: 16,
29
+ // Global base font size. The whole type/spacing scale is em-relative, so
30
+ // this single value drives the app-wide baseline; surfaces that need a
31
+ // different base (e.g. FactChat chat at 16px) opt in via <ScaledContext>.
32
+ fontSize: 14,
30
33
  fontFamily: [
31
34
  pretendard.style.fontFamily,
32
35
  inter.style.fontFamily,
@@ -74,7 +74,7 @@ const SHARED_TEXT_STYLE_VALUES = {
74
74
  * Text styles for consistent typography across the application
75
75
  * Names match fontSize tokens for easy migration: fontStyle="h1" → textStyle="h1"
76
76
  *
77
- * Base font size: 16px (set in global.ts)
77
+ * Base font size: 14px (set in global.ts)
78
78
  * Responsive scaling using em units (relative to nearest ancestor font-size):
79
79
  * - Mobile (base): Slightly smaller for better readability on small screens
80
80
  * - Desktop (md+): Original design sizes