@nowline/layout 0.0.0-dev.20260601071750.g04bdff9

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 (193) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +103 -0
  3. package/dist/band-scale.d.ts +56 -0
  4. package/dist/band-scale.d.ts.map +1 -0
  5. package/dist/band-scale.js +86 -0
  6. package/dist/band-scale.js.map +1 -0
  7. package/dist/calendar.d.ts +79 -0
  8. package/dist/calendar.d.ts.map +1 -0
  9. package/dist/calendar.js +210 -0
  10. package/dist/calendar.js.map +1 -0
  11. package/dist/capacity.d.ts +72 -0
  12. package/dist/capacity.d.ts.map +1 -0
  13. package/dist/capacity.js +163 -0
  14. package/dist/capacity.js.map +1 -0
  15. package/dist/dsl-utils.d.ts +5 -0
  16. package/dist/dsl-utils.d.ts.map +1 -0
  17. package/dist/dsl-utils.js +28 -0
  18. package/dist/dsl-utils.js.map +1 -0
  19. package/dist/edge-routing.d.ts +89 -0
  20. package/dist/edge-routing.d.ts.map +1 -0
  21. package/dist/edge-routing.js +435 -0
  22. package/dist/edge-routing.js.map +1 -0
  23. package/dist/frame-tab-geometry.d.ts +78 -0
  24. package/dist/frame-tab-geometry.d.ts.map +1 -0
  25. package/dist/frame-tab-geometry.js +115 -0
  26. package/dist/frame-tab-geometry.js.map +1 -0
  27. package/dist/header-card-geometry.d.ts +29 -0
  28. package/dist/header-card-geometry.d.ts.map +1 -0
  29. package/dist/header-card-geometry.js +41 -0
  30. package/dist/header-card-geometry.js.map +1 -0
  31. package/dist/i18n.d.ts +48 -0
  32. package/dist/i18n.d.ts.map +1 -0
  33. package/dist/i18n.js +114 -0
  34. package/dist/i18n.js.map +1 -0
  35. package/dist/include-chrome-geometry.d.ts +86 -0
  36. package/dist/include-chrome-geometry.d.ts.map +1 -0
  37. package/dist/include-chrome-geometry.js +104 -0
  38. package/dist/include-chrome-geometry.js.map +1 -0
  39. package/dist/index.d.ts +11 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +10 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/inline-date-pin-geometry.d.ts +46 -0
  44. package/dist/inline-date-pin-geometry.d.ts.map +1 -0
  45. package/dist/inline-date-pin-geometry.js +149 -0
  46. package/dist/inline-date-pin-geometry.js.map +1 -0
  47. package/dist/item-bar-geometry.d.ts +157 -0
  48. package/dist/item-bar-geometry.d.ts.map +1 -0
  49. package/dist/item-bar-geometry.js +214 -0
  50. package/dist/item-bar-geometry.js.map +1 -0
  51. package/dist/lane-utilization.d.ts +90 -0
  52. package/dist/lane-utilization.d.ts.map +1 -0
  53. package/dist/lane-utilization.js +206 -0
  54. package/dist/lane-utilization.js.map +1 -0
  55. package/dist/layout-context.d.ts +143 -0
  56. package/dist/layout-context.d.ts.map +1 -0
  57. package/dist/layout-context.js +8 -0
  58. package/dist/layout-context.js.map +1 -0
  59. package/dist/layout.d.ts +18 -0
  60. package/dist/layout.d.ts.map +1 -0
  61. package/dist/layout.js +1298 -0
  62. package/dist/layout.js.map +1 -0
  63. package/dist/nodes/anchor-node.d.ts +16 -0
  64. package/dist/nodes/anchor-node.d.ts.map +1 -0
  65. package/dist/nodes/anchor-node.js +68 -0
  66. package/dist/nodes/anchor-node.js.map +1 -0
  67. package/dist/nodes/footnote-node.d.ts +10 -0
  68. package/dist/nodes/footnote-node.d.ts.map +1 -0
  69. package/dist/nodes/footnote-node.js +41 -0
  70. package/dist/nodes/footnote-node.js.map +1 -0
  71. package/dist/nodes/group-node.d.ts +29 -0
  72. package/dist/nodes/group-node.d.ts.map +1 -0
  73. package/dist/nodes/group-node.js +195 -0
  74. package/dist/nodes/group-node.js.map +1 -0
  75. package/dist/nodes/include-node.d.ts +16 -0
  76. package/dist/nodes/include-node.d.ts.map +1 -0
  77. package/dist/nodes/include-node.js +117 -0
  78. package/dist/nodes/include-node.js.map +1 -0
  79. package/dist/nodes/item-node.d.ts +51 -0
  80. package/dist/nodes/item-node.d.ts.map +1 -0
  81. package/dist/nodes/item-node.js +108 -0
  82. package/dist/nodes/item-node.js.map +1 -0
  83. package/dist/nodes/marker-geometry.d.ts +22 -0
  84. package/dist/nodes/marker-geometry.d.ts.map +1 -0
  85. package/dist/nodes/marker-geometry.js +38 -0
  86. package/dist/nodes/marker-geometry.js.map +1 -0
  87. package/dist/nodes/milestone-node.d.ts +48 -0
  88. package/dist/nodes/milestone-node.d.ts.map +1 -0
  89. package/dist/nodes/milestone-node.js +210 -0
  90. package/dist/nodes/milestone-node.js.map +1 -0
  91. package/dist/nodes/parallel-node.d.ts +21 -0
  92. package/dist/nodes/parallel-node.d.ts.map +1 -0
  93. package/dist/nodes/parallel-node.js +88 -0
  94. package/dist/nodes/parallel-node.js.map +1 -0
  95. package/dist/nodes/roadmap-node.d.ts +76 -0
  96. package/dist/nodes/roadmap-node.d.ts.map +1 -0
  97. package/dist/nodes/roadmap-node.js +788 -0
  98. package/dist/nodes/roadmap-node.js.map +1 -0
  99. package/dist/nodes/swimlane-node.d.ts +38 -0
  100. package/dist/nodes/swimlane-node.d.ts.map +1 -0
  101. package/dist/nodes/swimlane-node.js +308 -0
  102. package/dist/nodes/swimlane-node.js.map +1 -0
  103. package/dist/renderable.d.ts +44 -0
  104. package/dist/renderable.d.ts.map +1 -0
  105. package/dist/renderable.js +21 -0
  106. package/dist/renderable.js.map +1 -0
  107. package/dist/row-packer.d.ts +125 -0
  108. package/dist/row-packer.d.ts.map +1 -0
  109. package/dist/row-packer.js +169 -0
  110. package/dist/row-packer.js.map +1 -0
  111. package/dist/style-resolution.d.ts +14 -0
  112. package/dist/style-resolution.d.ts.map +1 -0
  113. package/dist/style-resolution.js +191 -0
  114. package/dist/style-resolution.js.map +1 -0
  115. package/dist/themes/dark.d.ts +4 -0
  116. package/dist/themes/dark.d.ts.map +1 -0
  117. package/dist/themes/dark.js +241 -0
  118. package/dist/themes/dark.js.map +1 -0
  119. package/dist/themes/grayscale.d.ts +4 -0
  120. package/dist/themes/grayscale.d.ts.map +1 -0
  121. package/dist/themes/grayscale.js +237 -0
  122. package/dist/themes/grayscale.js.map +1 -0
  123. package/dist/themes/index.d.ts +19 -0
  124. package/dist/themes/index.d.ts.map +1 -0
  125. package/dist/themes/index.js +57 -0
  126. package/dist/themes/index.js.map +1 -0
  127. package/dist/themes/light.d.ts +4 -0
  128. package/dist/themes/light.d.ts.map +1 -0
  129. package/dist/themes/light.js +248 -0
  130. package/dist/themes/light.js.map +1 -0
  131. package/dist/themes/shape.d.ts +194 -0
  132. package/dist/themes/shape.d.ts.map +1 -0
  133. package/dist/themes/shape.js +6 -0
  134. package/dist/themes/shape.js.map +1 -0
  135. package/dist/themes/shared.d.ts +145 -0
  136. package/dist/themes/shared.d.ts.map +1 -0
  137. package/dist/themes/shared.js +310 -0
  138. package/dist/themes/shared.js.map +1 -0
  139. package/dist/time-scale.d.ts +39 -0
  140. package/dist/time-scale.d.ts.map +1 -0
  141. package/dist/time-scale.js +62 -0
  142. package/dist/time-scale.js.map +1 -0
  143. package/dist/types.d.ts +516 -0
  144. package/dist/types.d.ts.map +1 -0
  145. package/dist/types.js +6 -0
  146. package/dist/types.js.map +1 -0
  147. package/dist/view-preset.d.ts +23 -0
  148. package/dist/view-preset.d.ts.map +1 -0
  149. package/dist/view-preset.js +146 -0
  150. package/dist/view-preset.js.map +1 -0
  151. package/dist/working-calendar.d.ts +14 -0
  152. package/dist/working-calendar.d.ts.map +1 -0
  153. package/dist/working-calendar.js +74 -0
  154. package/dist/working-calendar.js.map +1 -0
  155. package/package.json +43 -0
  156. package/src/band-scale.ts +115 -0
  157. package/src/calendar.ts +244 -0
  158. package/src/capacity.ts +191 -0
  159. package/src/dsl-utils.ts +30 -0
  160. package/src/edge-routing.ts +550 -0
  161. package/src/frame-tab-geometry.ts +165 -0
  162. package/src/header-card-geometry.ts +48 -0
  163. package/src/i18n.ts +124 -0
  164. package/src/include-chrome-geometry.ts +156 -0
  165. package/src/index.ts +118 -0
  166. package/src/inline-date-pin-geometry.ts +196 -0
  167. package/src/item-bar-geometry.ts +271 -0
  168. package/src/lane-utilization.ts +259 -0
  169. package/src/layout-context.ts +182 -0
  170. package/src/layout.ts +1530 -0
  171. package/src/nodes/anchor-node.ts +77 -0
  172. package/src/nodes/footnote-node.ts +60 -0
  173. package/src/nodes/group-node.ts +260 -0
  174. package/src/nodes/include-node.ts +168 -0
  175. package/src/nodes/item-node.ts +171 -0
  176. package/src/nodes/marker-geometry.ts +43 -0
  177. package/src/nodes/milestone-node.ts +263 -0
  178. package/src/nodes/parallel-node.ts +110 -0
  179. package/src/nodes/roadmap-node.ts +957 -0
  180. package/src/nodes/swimlane-node.ts +423 -0
  181. package/src/renderable.ts +68 -0
  182. package/src/row-packer.ts +271 -0
  183. package/src/style-resolution.ts +243 -0
  184. package/src/themes/dark.ts +244 -0
  185. package/src/themes/grayscale.ts +240 -0
  186. package/src/themes/index.ts +66 -0
  187. package/src/themes/light.ts +251 -0
  188. package/src/themes/shape.ts +230 -0
  189. package/src/themes/shared.ts +369 -0
  190. package/src/time-scale.ts +78 -0
  191. package/src/types.ts +641 -0
  192. package/src/view-preset.ts +180 -0
  193. package/src/working-calendar.ts +91 -0
@@ -0,0 +1,244 @@
1
+ import type { EntityStyle, NamedColors, Theme } from './shape.js';
2
+
3
+ // Named-color mapping for dark theme. Tailwind's slate-on-near-black tints.
4
+ export const darkNamed: NamedColors = {
5
+ red: '#f87171',
6
+ blue: '#60a5fa',
7
+ yellow: '#facc15',
8
+ green: '#34d399',
9
+ orange: '#fb923c',
10
+ purple: '#a78bfa',
11
+ gray: '#94a3b8',
12
+ navy: '#818cf8',
13
+ white: '#e2e8f0',
14
+ };
15
+
16
+ const baseEntity: EntityStyle = {
17
+ bg: 'none',
18
+ fg: '#e2e8f0',
19
+ text: '#e2e8f0',
20
+ border: 'solid',
21
+ icon: 'none',
22
+ shadow: 'none',
23
+ font: 'sans',
24
+ weight: 'normal',
25
+ italic: false,
26
+ textSize: 'md',
27
+ padding: 'sm',
28
+ spacing: 'sm',
29
+ headerHeight: 'sm',
30
+ cornerRadius: 'sm',
31
+ bracket: 'none',
32
+ capacityIcon: 'multiplier',
33
+ };
34
+
35
+ export const darkTheme: Theme = {
36
+ name: 'dark',
37
+ surface: {
38
+ page: '#0b1220',
39
+ chart: '#111827',
40
+ headerBox: '#1e293b',
41
+ },
42
+ entities: {
43
+ roadmap: {
44
+ ...baseEntity,
45
+ headerHeight: 'md',
46
+ padding: 'md',
47
+ },
48
+ swimlane: {
49
+ ...baseEntity,
50
+ fg: '#e2e8f0',
51
+ text: '#e2e8f0',
52
+ padding: 'sm',
53
+ spacing: 'none',
54
+ textSize: 'sm',
55
+ },
56
+ item: {
57
+ ...baseEntity,
58
+ // Status-aware tint applied during layout when bg stays #0f172a.
59
+ bg: '#0f172a',
60
+ fg: '#94a3b8',
61
+ text: '#e2e8f0',
62
+ shadow: 'subtle',
63
+ cornerRadius: 'sm',
64
+ },
65
+ parallel: {
66
+ ...baseEntity,
67
+ bracket: 'none',
68
+ padding: 'xs',
69
+ },
70
+ group: {
71
+ ...baseEntity,
72
+ bracket: 'solid',
73
+ padding: 'xs',
74
+ fg: '#94a3b8',
75
+ },
76
+ anchor: {
77
+ ...baseEntity,
78
+ bg: '#0b1220',
79
+ fg: '#cbd5e1',
80
+ text: '#cbd5e1',
81
+ cornerRadius: 'sm',
82
+ },
83
+ milestone: {
84
+ ...baseEntity,
85
+ bg: '#e2e8f0',
86
+ fg: '#e2e8f0',
87
+ text: '#0b1220',
88
+ border: 'solid',
89
+ },
90
+ footnote: {
91
+ ...baseEntity,
92
+ bg: 'none',
93
+ fg: '#94a3b8',
94
+ text: '#e2e8f0',
95
+ textSize: 'sm',
96
+ },
97
+ label: {
98
+ ...baseEntity,
99
+ bg: '#1e293b',
100
+ fg: '#94a3b8',
101
+ text: '#e2e8f0',
102
+ textSize: 'xs',
103
+ padding: 'xs',
104
+ cornerRadius: 'full',
105
+ },
106
+ },
107
+ swimlane: {
108
+ bandEven: '#111827',
109
+ bandOdd: '#0f172a',
110
+ separator: '#1f2937',
111
+ frameTabText: '#e2e8f0',
112
+ frameTabMuted: '#94a3b8',
113
+ border: '#334155',
114
+ tabFill: '#1e293b',
115
+ tabStroke: '#475569',
116
+ tabText: '#e2e8f0',
117
+ ownerText: '#94a3b8',
118
+ footnoteIndicator: '#f87171',
119
+ rowTintEven: '#0f172a',
120
+ rowTintOdd: '#1e293b',
121
+ utilizationOk: '#34d399',
122
+ utilizationWarn: '#fbbf24',
123
+ utilizationOver: '#f87171',
124
+ },
125
+ timeline: {
126
+ gridLine: '#475569',
127
+ minorGridLine: '#334155',
128
+ tickMark: '#334155',
129
+ labelText: '#cbd5e1',
130
+ panelFill: '#0f172a',
131
+ border: '#334155',
132
+ },
133
+ header: {
134
+ cardBorder: '#475569',
135
+ author: '#94a3b8',
136
+ },
137
+ item: {
138
+ overflowX: '#f87171',
139
+ linkIconFg: '#94a3b8',
140
+ overflowTailFill: '#7f1d1d',
141
+ overflowTailStroke: '#f87171',
142
+ overflowCaption: '#fecaca',
143
+ },
144
+ parallel: {
145
+ bracketStroke: '#cbd5e1',
146
+ },
147
+ anchorDiamond: {
148
+ fill: '#0f172a',
149
+ stroke: '#cbd5e1',
150
+ label: '#cbd5e1',
151
+ cutLine: '#94a3b8',
152
+ },
153
+ milestoneDiamond: {
154
+ fill: '#e2e8f0',
155
+ label: '#e2e8f0',
156
+ cutLineNormal: '#a5b4fc',
157
+ cutLineOverrun: '#ef4444',
158
+ slack: '#cbd5e1',
159
+ },
160
+ footnotePanel: {
161
+ fill: '#0f172a',
162
+ border: '#334155',
163
+ header: '#e2e8f0',
164
+ title: '#e2e8f0',
165
+ description: '#94a3b8',
166
+ number: '#f87171',
167
+ },
168
+ nowline: {
169
+ stroke: '#f87171',
170
+ labelText: '#0b1220',
171
+ labelBg: '#f87171',
172
+ },
173
+ milestone: {
174
+ dashedInk: '#a5b4fc',
175
+ overrun: '#ef4444',
176
+ },
177
+ anchor: {
178
+ predecessorLine: '#94a3b8',
179
+ },
180
+ dependency: {
181
+ edgeStroke: '#cbd5e1',
182
+ overflowStroke: '#ef5350',
183
+ },
184
+ footnote: {
185
+ indicatorText: '#f87171',
186
+ descriptionMuted: '#94a3b8',
187
+ },
188
+ includeRegion: {
189
+ border: '#475569',
190
+ label: '#cbd5e1',
191
+ badge: '#94a3b8',
192
+ fill: '#0b1220',
193
+ tabFill: '#111827',
194
+ tabStroke: '#475569',
195
+ tabText: '#e2e8f0',
196
+ badgeFill: '#1e293b',
197
+ badgeStroke: '#475569',
198
+ badgeText: '#94a3b8',
199
+ },
200
+ arrowhead: {
201
+ neutral: '#94a3b8',
202
+ light: '#64748b',
203
+ dark: '#e2e8f0',
204
+ },
205
+ status: {
206
+ done: '#34d399',
207
+ inProgress: '#60a5fa',
208
+ atRisk: '#facc15',
209
+ blocked: '#ef4444',
210
+ planned: '#94a3b8',
211
+ neutral: '#94a3b8',
212
+ },
213
+ // Status-dot palettes — see `light.ts` for the contract. Same
214
+ // two palettes both themes; dark-theme bars come in BOTH dark
215
+ // (status-tint) and bright (label-driven) flavors, so a single
216
+ // palette can never satisfy both. The renderer picks per-bar
217
+ // based on the bar bg's relative luminance.
218
+ statusDot: {
219
+ onLight: {
220
+ done: '#065f46',
221
+ inProgress: '#1e3a8a',
222
+ atRisk: '#92400e',
223
+ blocked: '#991b1b',
224
+ planned: '#334155',
225
+ neutral: '#334155',
226
+ },
227
+ onDark: {
228
+ done: '#d1fae5',
229
+ inProgress: '#dbeafe',
230
+ atRisk: '#fef3c7',
231
+ blocked: '#fee2e2',
232
+ planned: '#e2e8f0',
233
+ neutral: '#e2e8f0',
234
+ },
235
+ },
236
+ attribution: {
237
+ mark: '#cbd5e1',
238
+ link: '#f87171',
239
+ },
240
+ diagnostic: {
241
+ overlayBg: '#7f1d1d',
242
+ errorText: '#fecaca',
243
+ },
244
+ };
@@ -0,0 +1,240 @@
1
+ import type { EntityStyle, NamedColors, Theme } from './shape.js';
2
+
3
+ // Named-color mapping for grayscale theme. Each named color maps to a
4
+ // distinct grey by lightness so `bg:blue` etc. remain differentiable
5
+ // in black-and-white print without relying on automatic colour conversion.
6
+ export const grayscaleNamed: NamedColors = {
7
+ red: '#4a4a4a',
8
+ blue: '#6b6b6b',
9
+ green: '#8a8a8a',
10
+ yellow: '#c8c8c8',
11
+ orange: '#5c5c5c',
12
+ purple: '#3a3a3a',
13
+ gray: '#9e9e9e',
14
+ navy: '#2a2a2a',
15
+ white: '#ffffff',
16
+ };
17
+
18
+ const baseEntity: EntityStyle = {
19
+ bg: 'none',
20
+ fg: '#1a1a1a',
21
+ text: '#1a1a1a',
22
+ border: 'solid',
23
+ icon: 'none',
24
+ shadow: 'none',
25
+ font: 'sans',
26
+ weight: 'normal',
27
+ italic: false,
28
+ textSize: 'md',
29
+ padding: 'sm',
30
+ spacing: 'sm',
31
+ headerHeight: 'sm',
32
+ cornerRadius: 'sm',
33
+ bracket: 'none',
34
+ capacityIcon: 'multiplier',
35
+ };
36
+
37
+ export const grayscaleTheme: Theme = {
38
+ name: 'grayscale',
39
+ surface: {
40
+ page: '#f5f5f5',
41
+ chart: '#ffffff',
42
+ headerBox: '#ffffff',
43
+ },
44
+ entities: {
45
+ roadmap: {
46
+ ...baseEntity,
47
+ headerHeight: 'md',
48
+ padding: 'md',
49
+ },
50
+ swimlane: {
51
+ ...baseEntity,
52
+ fg: '#3a3a3a',
53
+ text: '#3a3a3a',
54
+ padding: 'sm',
55
+ spacing: 'none',
56
+ textSize: 'sm',
57
+ },
58
+ item: {
59
+ ...baseEntity,
60
+ bg: '#ffffff',
61
+ fg: '#9e9e9e',
62
+ text: '#1a1a1a',
63
+ shadow: 'subtle',
64
+ cornerRadius: 'sm',
65
+ },
66
+ parallel: {
67
+ ...baseEntity,
68
+ bracket: 'none',
69
+ padding: 'xs',
70
+ },
71
+ group: {
72
+ ...baseEntity,
73
+ bracket: 'solid',
74
+ padding: 'xs',
75
+ fg: '#595959',
76
+ },
77
+ anchor: {
78
+ ...baseEntity,
79
+ bg: '#1a1a1a',
80
+ fg: '#1a1a1a',
81
+ text: '#1a1a1a',
82
+ cornerRadius: 'sm',
83
+ },
84
+ milestone: {
85
+ ...baseEntity,
86
+ bg: '#2a2a2a',
87
+ fg: '#2a2a2a',
88
+ text: '#ffffff',
89
+ border: 'solid',
90
+ },
91
+ footnote: {
92
+ ...baseEntity,
93
+ bg: 'none',
94
+ fg: '#595959',
95
+ text: '#595959',
96
+ textSize: 'sm',
97
+ },
98
+ label: {
99
+ ...baseEntity,
100
+ bg: '#e8e8e8',
101
+ fg: '#595959',
102
+ text: '#595959',
103
+ textSize: 'xs',
104
+ padding: 'xs',
105
+ cornerRadius: 'full',
106
+ },
107
+ },
108
+ swimlane: {
109
+ bandEven: '#ffffff',
110
+ bandOdd: '#f5f5f5',
111
+ separator: '#d4d4d4',
112
+ frameTabText: '#3a3a3a',
113
+ frameTabMuted: '#737373',
114
+ border: '#d4d4d4',
115
+ tabFill: '#e8e8e8',
116
+ tabStroke: '#bdbdbd',
117
+ tabText: '#3a3a3a',
118
+ ownerText: '#737373',
119
+ footnoteIndicator: '#4a4a4a',
120
+ rowTintEven: '#ffffff',
121
+ rowTintOdd: '#f5f5f5',
122
+ utilizationOk: '#8a8a8a',
123
+ utilizationWarn: '#595959',
124
+ utilizationOver: '#1a1a1a',
125
+ },
126
+ timeline: {
127
+ gridLine: '#bdbdbd',
128
+ minorGridLine: '#d4d4d4',
129
+ tickMark: '#bdbdbd',
130
+ labelText: '#737373',
131
+ panelFill: '#ffffff',
132
+ border: '#d4d4d4',
133
+ },
134
+ header: {
135
+ cardBorder: '#d4d4d4',
136
+ author: '#737373',
137
+ },
138
+ item: {
139
+ overflowX: '#1a1a1a',
140
+ linkIconFg: '#1a1a1a',
141
+ overflowTailFill: '#e8e8e8',
142
+ overflowTailStroke: '#4a4a4a',
143
+ overflowCaption: '#3a3a3a',
144
+ },
145
+ parallel: {
146
+ bracketStroke: '#3a3a3a',
147
+ },
148
+ anchorDiamond: {
149
+ fill: '#ffffff',
150
+ stroke: '#3a3a3a',
151
+ label: '#3a3a3a',
152
+ cutLine: '#737373',
153
+ },
154
+ milestoneDiamond: {
155
+ fill: '#1a1a1a',
156
+ label: '#1a1a1a',
157
+ cutLineNormal: '#2a2a2a',
158
+ cutLineOverrun: '#4a4a4a',
159
+ slack: '#1a1a1a',
160
+ },
161
+ footnotePanel: {
162
+ fill: '#ffffff',
163
+ border: '#d4d4d4',
164
+ header: '#1a1a1a',
165
+ title: '#1a1a1a',
166
+ description: '#737373',
167
+ number: '#4a4a4a',
168
+ },
169
+ nowline: {
170
+ stroke: '#1a1a1a',
171
+ labelText: '#ffffff',
172
+ labelBg: '#1a1a1a',
173
+ },
174
+ milestone: {
175
+ dashedInk: '#9e9e9e',
176
+ overrun: '#4a4a4a',
177
+ },
178
+ anchor: {
179
+ predecessorLine: '#9e9e9e',
180
+ },
181
+ dependency: {
182
+ edgeStroke: '#595959',
183
+ overflowStroke: '#1a1a1a',
184
+ },
185
+ footnote: {
186
+ indicatorText: '#4a4a4a',
187
+ descriptionMuted: '#737373',
188
+ },
189
+ includeRegion: {
190
+ border: '#9e9e9e',
191
+ label: '#3a3a3a',
192
+ badge: '#737373',
193
+ fill: '#f5f5f5',
194
+ tabFill: '#ffffff',
195
+ tabStroke: '#bdbdbd',
196
+ tabText: '#1a1a1a',
197
+ badgeFill: '#e8e8e8',
198
+ badgeStroke: '#bdbdbd',
199
+ badgeText: '#595959',
200
+ },
201
+ arrowhead: {
202
+ neutral: '#595959',
203
+ light: '#9e9e9e',
204
+ dark: '#1a1a1a',
205
+ },
206
+ status: {
207
+ done: '#8a8a8a',
208
+ inProgress: '#595959',
209
+ atRisk: '#737373',
210
+ blocked: '#3a3a3a',
211
+ planned: '#bdbdbd',
212
+ neutral: '#bdbdbd',
213
+ },
214
+ statusDot: {
215
+ onLight: {
216
+ done: '#3a3a3a',
217
+ inProgress: '#2a2a2a',
218
+ atRisk: '#4a4a4a',
219
+ blocked: '#1a1a1a',
220
+ planned: '#595959',
221
+ neutral: '#595959',
222
+ },
223
+ onDark: {
224
+ done: '#d4d4d4',
225
+ inProgress: '#e8e8e8',
226
+ atRisk: '#c8c8c8',
227
+ blocked: '#f0f0f0',
228
+ planned: '#d4d4d4',
229
+ neutral: '#d4d4d4',
230
+ },
231
+ },
232
+ attribution: {
233
+ mark: '#9e9e9e',
234
+ link: '#1a1a1a',
235
+ },
236
+ diagnostic: {
237
+ overlayBg: '#e8e8e8',
238
+ errorText: '#1a1a1a',
239
+ },
240
+ };
@@ -0,0 +1,66 @@
1
+ export { darkNamed, darkTheme } from './dark.js';
2
+ export { grayscaleNamed, grayscaleTheme } from './grayscale.js';
3
+ export { lightNamed, lightTheme } from './light.js';
4
+ export type { EntityStyle, NamedColors, Theme } from './shape.js';
5
+
6
+ import { darkNamed, darkTheme } from './dark.js';
7
+ import { grayscaleNamed, grayscaleTheme } from './grayscale.js';
8
+ import { lightNamed, lightTheme } from './light.js';
9
+ import type { NamedColors, Theme } from './shape.js';
10
+
11
+ export type ThemeName = 'light' | 'dark' | 'grayscale';
12
+
13
+ export const themes: { light: Theme; dark: Theme; grayscale: Theme } = {
14
+ light: lightTheme,
15
+ dark: darkTheme,
16
+ grayscale: grayscaleTheme,
17
+ };
18
+
19
+ export const namedColors: { light: NamedColors; dark: NamedColors; grayscale: NamedColors } = {
20
+ light: lightNamed,
21
+ dark: darkNamed,
22
+ grayscale: grayscaleNamed,
23
+ };
24
+
25
+ // Theme-name aliases mirror the COLOR_ALIASES policy below: the US spelling
26
+ // `grayscale` is canonical (matching the `gray` color token), and the UK
27
+ // `greyscale` is accepted as input. Every surface that turns user-typed text
28
+ // into a ThemeName (CLI `--theme`, embed config) runs it through this so the
29
+ // canonical token stays single while both spellings resolve.
30
+ const THEME_ALIASES: Record<string, ThemeName> = {
31
+ greyscale: 'grayscale',
32
+ };
33
+
34
+ // Normalize a user-supplied theme token to its canonical ThemeName, or
35
+ // `undefined` when it is not a recognized theme (callers decide how to
36
+ // report the error). Lowercases first so `Grayscale` / `GREYSCALE` resolve.
37
+ export function normalizeThemeName(raw: string): ThemeName | undefined {
38
+ const lower = raw.toLowerCase();
39
+ const canonical = THEME_ALIASES[lower] ?? lower;
40
+ if (canonical === 'light' || canonical === 'dark' || canonical === 'grayscale') {
41
+ return canonical;
42
+ }
43
+ return undefined;
44
+ }
45
+
46
+ // Aliases collapse internationally-friendlier spellings onto the canonical
47
+ // keys before lookup so themes only need to define each color once.
48
+ const COLOR_ALIASES: Record<string, string> = {
49
+ grey: 'gray',
50
+ violet: 'purple',
51
+ };
52
+
53
+ // Resolve a DSL color token (`blue`, `#ff00aa`, or `none`) against a theme.
54
+ export function resolveColor(token: string, theme: Theme): string {
55
+ if (token === 'none') return 'none';
56
+ if (token.startsWith('#')) return token;
57
+ const named =
58
+ theme.name === 'dark'
59
+ ? darkNamed
60
+ : theme.name === 'grayscale'
61
+ ? grayscaleNamed
62
+ : lightNamed;
63
+ const canonical = COLOR_ALIASES[token] ?? token;
64
+ const hit = (named as unknown as Record<string, string>)[canonical];
65
+ return typeof hit === 'string' ? hit : token;
66
+ }