@dfosco/storyboard-core 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfosco/storyboard-core",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -550,11 +550,11 @@
550
550
  display: flex;
551
551
  flex-direction: column;
552
552
  gap: var(--base-size-8);
553
+ }
553
554
 
554
- & .listItem:only-child {
555
- border: 1px solid var(--borderColor-muted, #30363d);
556
- border-radius: var(--base-size-6);
557
- }
555
+ .protoGroup > .listItem {
556
+ border: 1px solid var(--borderColor-muted, #30363d);
557
+ border-radius: var(--base-size-6);
558
558
  }
559
559
 
560
560
  .folderGroup {
@@ -644,7 +644,7 @@
644
644
 
645
645
  .protoHeader[aria-expanded="true"] .cardBody {
646
646
  background-color: var(--bgColor-muted);
647
- border-radius: var(--base-size-8);
647
+ border-radius: var(--base-size-6);
648
648
  }
649
649
 
650
650
  .cardBody {
@@ -653,7 +653,7 @@
653
653
 
654
654
  .cardBody:hover {
655
655
  background-color: var(--bgColor-muted);
656
- border-radius: var(--base-size-8);
656
+ border-radius: var(--base-size-6);
657
657
  }
658
658
 
659
659
  .protoName {
@@ -736,7 +736,6 @@
736
736
  flex-direction: column;
737
737
  }
738
738
 
739
- .folderGroup .listItem,
740
739
  .flowItem {
741
740
  border: 1px solid var(--borderColor-muted);
742
741
  padding: 0;
@@ -750,14 +749,12 @@
750
749
  border-top-left-radius: var(--base-size-6);
751
750
  border-top-right-radius: var(--base-size-6);
752
751
  }
753
-
754
- .folderGroup .listItem:last-child,
752
+
755
753
  .flowItem:last-child {
756
754
  border-bottom-left-radius: var(--base-size-6);
757
755
  border-bottom-right-radius: var(--base-size-6);
758
756
  }
759
757
 
760
- .folderGroup .listItem:only-child,
761
758
  .flowItem:only-child {
762
759
  border-radius: var(--base-size-6);
763
760
  }
package/src/viewfinder.js CHANGED
@@ -16,13 +16,17 @@ export function hash(str) {
16
16
  /**
17
17
  * Resolve the target route path for a flow.
18
18
  *
19
+ * Priority:
19
20
  * 1. If flow name matches a known route (case-insensitive), use that route
20
- * 2. If flow data has a top-level `route`, or `meta.route` / `sceneMeta.route`, use that
21
- * 3. Fall back to root "/"
21
+ * 2. If flow data has an explicit top-level `route`, or `meta.route` / `flowMeta.route`, use that
22
+ * 3. If flow data has `_route` (inferred from file path by Vite plugin), use that
23
+ * 4. Fall back to root "/"
24
+ *
25
+ * Flows with `meta.default: true` targeting a route omit the `?flow=` param.
22
26
  *
23
27
  * @param {string} flowName
24
28
  * @param {string[]} knownRoutes - Array of route names (e.g. ["Dashboard", "Repositories"])
25
- * @returns {string} Full path with ?flow= param
29
+ * @returns {string} Full path with optional ?flow= param
26
30
  */
27
31
  export function resolveFlowRoute(flowName, knownRoutes = []) {
28
32
  // Case-insensitive match against known routes
@@ -34,14 +38,22 @@ export function resolveFlowRoute(flowName, knownRoutes = []) {
34
38
  }
35
39
  }
36
40
 
37
- // Check for explicit route: top-level `route`, then meta.route, then legacy sceneMeta.route
38
41
  try {
39
42
  const data = loadFlow(flowName)
40
- const route = data?.route || data?.meta?.route || data?.flowMeta?.route || data?.sceneMeta?.route
41
- if (route) {
42
- const normalized = route.startsWith('/') ? route : `/${route}`
43
+
44
+ // Check for explicit route: top-level `route`, then meta.route, then legacy sceneMeta.route
45
+ const explicitRoute = data?.route || data?.meta?.route || data?.flowMeta?.route || data?.sceneMeta?.route
46
+ if (explicitRoute) {
47
+ const normalized = explicitRoute.startsWith('/') ? explicitRoute : `/${explicitRoute}`
48
+ if (data?.meta?.default === true) return normalized
43
49
  return `${normalized}?flow=${encodeURIComponent(flowName)}`
44
50
  }
51
+
52
+ // Use inferred route from file path (injected by Vite data plugin)
53
+ if (data?._route) {
54
+ if (data?.meta?.default === true) return data._route
55
+ return `${data._route}?flow=${encodeURIComponent(flowName)}`
56
+ }
45
57
  } catch {
46
58
  // ignore load errors
47
59
  }
@@ -101,7 +113,7 @@ export function buildPrototypeIndex(knownRoutes = []) {
101
113
  icon: meta.icon || null,
102
114
  team: meta.team || null,
103
115
  tags: meta.tags || null,
104
- hideFlows: meta.hideFlows || false,
116
+ hideFlows: meta.hideFlows ?? raw?.hideFlows ?? false,
105
117
  folder: raw?.folder || null,
106
118
  flows: [],
107
119
  }
@@ -12,6 +12,9 @@ const makeIndex = () => ({
12
12
  'meta-author': { flowMeta: { author: 'dfosco' }, title: 'With Author' },
13
13
  'meta-authors': { flowMeta: { author: ['dfosco', 'heyamie', 'branonconor'] }, title: 'Multi Author' },
14
14
  'meta-both': { flowMeta: { route: '/Overview', author: 'octocat' }, title: 'Both' },
15
+ 'inferred-route': { _route: '/Dashboard', title: 'Inferred' },
16
+ 'inferred-default': { _route: '/Settings', meta: { default: true }, title: 'Default Flow' },
17
+ 'explicit-wins': { route: '/Forms', _route: '/Dashboard', title: 'Explicit Wins' },
15
18
  },
16
19
  objects: {},
17
20
  records: {},
@@ -105,6 +108,31 @@ describe('resolveFlowRoute', () => {
105
108
  })
106
109
  expect(resolveFlowRoute('conflict', [])).toBe('/Forms?flow=conflict')
107
110
  })
111
+
112
+ it('uses _route when no explicit route exists', () => {
113
+ expect(resolveFlowRoute('inferred-route', routes)).toBe('/Dashboard?flow=inferred-route')
114
+ })
115
+
116
+ it('prefers explicit route over _route', () => {
117
+ expect(resolveFlowRoute('explicit-wins', routes)).toBe('/Forms?flow=explicit-wins')
118
+ })
119
+
120
+ it('omits ?flow= when meta.default is true (inferred route)', () => {
121
+ expect(resolveFlowRoute('inferred-default', routes)).toBe('/Settings')
122
+ })
123
+
124
+ it('omits ?flow= when meta.default is true (explicit route)', () => {
125
+ init({
126
+ flows: { 'default-explicit': { route: '/Overview', meta: { default: true } } },
127
+ objects: {},
128
+ records: {},
129
+ })
130
+ expect(resolveFlowRoute('default-explicit', [])).toBe('/Overview')
131
+ })
132
+
133
+ it('still appends ?flow= when meta.default is absent even with _route', () => {
134
+ expect(resolveFlowRoute('inferred-route', [])).toBe('/Dashboard?flow=inferred-route')
135
+ })
108
136
  })
109
137
 
110
138
  describe('getFlowMeta', () => {
@@ -183,6 +211,20 @@ describe('buildPrototypeIndex', () => {
183
211
  expect(proto.hideFlows).toBe(false)
184
212
  })
185
213
 
214
+ it('reads hideFlows from top-level prototype metadata (outside meta key)', () => {
215
+ init({
216
+ flows: { 'TopLevel/only-flow': { meta: { title: 'Only Flow' } } },
217
+ objects: {},
218
+ records: {},
219
+ prototypes: {
220
+ TopLevel: { meta: { title: 'Top Level' }, hideFlows: true },
221
+ },
222
+ })
223
+ const { prototypes } = buildPrototypeIndex([])
224
+ const proto = prototypes.find(p => p.dirName === 'TopLevel')
225
+ expect(proto.hideFlows).toBe(true)
226
+ })
227
+
186
228
  it('groups prototypes into folders when folder field is set', () => {
187
229
  init({
188
230
  flows: {