@colixsystems/widget-sdk 0.124.0 → 0.125.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/README.md CHANGED
@@ -70,7 +70,19 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
70
70
 
71
71
  ## Status
72
72
 
73
- `v0.124.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
73
+ `v0.125.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
74
+
75
+ ### What's new in 0.125.0 (contract 1.97.0)
76
+
77
+ **A `top-bar` app no longer chooses which row carries its menu — `CONTRACT.themeTopBarMenuStyles` is REMOVED and `normaliseNavigation` returns `menuType` alone (sc-7044).** 0.120.0 gave the shape two menu rows to pick between: `links`, the row of text links beside the brand it had always drawn, and `tabs`, a dedicated tab row beneath the bar. `links` turned out to be the site-mode header's own row — literally the same `header` nav variant, sitting in the Studio beside "Show a header on site-mode pages" and its "Menu links" toggle, so an author was offered three adjacent settings that render the same thing. The tab row is the one that reads as an app's global navigation, so it is now the only one.
78
+
79
+ A host branches on `menuType` alone:
80
+
81
+ - `CONTRACT.themeTopBarMenuStyles` is gone, and so is the `ThemeTopBarMenuStyle` type. `ResolvedNavigation` is `{ menuType }`.
82
+ - `resolveTopBarTokens` is UNCHANGED — the whole `topBar` tab vocabulary (`tabStyle`, `contentSurface`, `tabIndicatorWidth`, `tabCornerRadius`, `tabPaddingX` / `tabPaddingY`, `tabBackgroundColor`, `tabActiveBackgroundColor`) now applies to every `top-bar` app rather than only to one that opted into tabs.
83
+ - A stored `navigation.topBarMenuStyle` is **inert**, not migrated. Nothing reads it, so no backfill runs; a `top-bar` app that never chose `tabs` renders the tab row from this version on.
84
+
85
+ **BREAKING, host-integration surface only** — a host that destructured `topBarMenuStyle` must stop. Nothing a widget author imports moved: no hook, primitive, manifest field or `propertySchema` type changed. `CONTRACT.version` → `1.97.0`.
74
86
 
75
87
  ### What's new in 0.124.0 (contract 1.96.0)
76
88
 
package/dist/contract.cjs CHANGED
@@ -216,7 +216,7 @@ const THEME_MENU_TYPES = Object.freeze({
216
216
  "top-bar": Object.freeze({
217
217
  name: "Top bar",
218
218
  summary:
219
- "A horizontal row of links in the app header, scrolling sideways when it runs out of room. No rail at any width.",
219
+ "A tab row beneath the app header at every width -- icon and label per page, scrolling sideways when it runs out of room. No rail at any width.",
220
220
  maxItems: null,
221
221
  quickBarMaxItems: null,
222
222
  }),
@@ -228,18 +228,6 @@ const THEME_MENU_TYPES = Object.freeze({
228
228
  quickBarMaxItems: null,
229
229
  }),
230
230
  });
231
- const THEME_TOP_BAR_MENU_STYLES = Object.freeze({
232
- links: Object.freeze({
233
- name: "Links",
234
- summary:
235
- "Text links beside the brand, dropping to their own row only when a phone leaves them no space.",
236
- }),
237
- tabs: Object.freeze({
238
- name: "Tabs",
239
- summary:
240
- "A dedicated tab row under the bar at every width -- icon and label per page, marked by the brand, scrolling sideways when it runs out of room.",
241
- }),
242
- });
243
231
  const THEME_SPACING_SCALE = Object.freeze({
244
232
  min: 0.5,
245
233
  max: 2,
@@ -3671,7 +3659,15 @@ const CONTRACT = deepFreeze({
3671
3659
  // privacy remains the record ACL's job. Existing `literal` and
3672
3660
  // `relativeDate` conditions are untouched -- minor bump on the pre-1.0
3673
3661
  // channel.
3674
- version: "1.96.0",
3662
+ // 1.97.0: BREAKING (sc-7044) -- `themeTopBarMenuStyles` is REMOVED and
3663
+ // `normaliseNavigation` no longer returns `topBarMenuStyle`. A `top-bar`
3664
+ // app has ONE menu shape: the tab row beneath the bar. The `links` style
3665
+ // drew the menu on the same row-of-text-links the site-mode header draws,
3666
+ // so the Studio offered three adjacent settings that an author could not
3667
+ // tell apart. A host now branches on `menuType` alone; a stored
3668
+ // `navigation.topBarMenuStyle` is inert rather than migrated, so a
3669
+ // `top-bar` app that never chose `tabs` moves to the tab row.
3670
+ version: "1.97.0",
3675
3671
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
3676
3672
  hooks: HOOKS,
3677
3673
  primitives: PRIMITIVES,
@@ -3688,7 +3684,6 @@ const CONTRACT = deepFreeze({
3688
3684
  themeComponentGradient: THEME_COMPONENT_GRADIENT,
3689
3685
  themeSpacingScale: THEME_SPACING_SCALE,
3690
3686
  themeMenuTypes: THEME_MENU_TYPES,
3691
- themeTopBarMenuStyles: THEME_TOP_BAR_MENU_STYLES,
3692
3687
  themeWidgetStyles: THEME_WIDGET_STYLES,
3693
3688
  widgetContextShape: WIDGET_CONTEXT_SHAPE,
3694
3689
  bundleExportContract: BUNDLE_EXPORT_CONTRACT,
package/dist/contract.js CHANGED
@@ -216,7 +216,7 @@ const THEME_MENU_TYPES = Object.freeze({
216
216
  "top-bar": Object.freeze({
217
217
  name: "Top bar",
218
218
  summary:
219
- "A horizontal row of links in the app header, scrolling sideways when it runs out of room. No rail at any width.",
219
+ "A tab row beneath the app header at every width -- icon and label per page, scrolling sideways when it runs out of room. No rail at any width.",
220
220
  maxItems: null,
221
221
  quickBarMaxItems: null,
222
222
  }),
@@ -228,18 +228,6 @@ const THEME_MENU_TYPES = Object.freeze({
228
228
  quickBarMaxItems: null,
229
229
  }),
230
230
  });
231
- const THEME_TOP_BAR_MENU_STYLES = Object.freeze({
232
- links: Object.freeze({
233
- name: "Links",
234
- summary:
235
- "Text links beside the brand, dropping to their own row only when a phone leaves them no space.",
236
- }),
237
- tabs: Object.freeze({
238
- name: "Tabs",
239
- summary:
240
- "A dedicated tab row under the bar at every width -- icon and label per page, marked by the brand, scrolling sideways when it runs out of room.",
241
- }),
242
- });
243
231
  const THEME_SPACING_SCALE = Object.freeze({
244
232
  min: 0.5,
245
233
  max: 2,
@@ -3671,7 +3659,15 @@ const CONTRACT = deepFreeze({
3671
3659
  // privacy remains the record ACL's job. Existing `literal` and
3672
3660
  // `relativeDate` conditions are untouched -- minor bump on the pre-1.0
3673
3661
  // channel.
3674
- version: "1.96.0",
3662
+ // 1.97.0: BREAKING (sc-7044) -- `themeTopBarMenuStyles` is REMOVED and
3663
+ // `normaliseNavigation` no longer returns `topBarMenuStyle`. A `top-bar`
3664
+ // app has ONE menu shape: the tab row beneath the bar. The `links` style
3665
+ // drew the menu on the same row-of-text-links the site-mode header draws,
3666
+ // so the Studio offered three adjacent settings that an author could not
3667
+ // tell apart. A host now branches on `menuType` alone; a stored
3668
+ // `navigation.topBarMenuStyle` is inert rather than migrated, so a
3669
+ // `top-bar` app that never chose `tabs` moves to the tab row.
3670
+ version: "1.97.0",
3675
3671
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
3676
3672
  hooks: HOOKS,
3677
3673
  primitives: PRIMITIVES,
@@ -3688,7 +3684,6 @@ const CONTRACT = deepFreeze({
3688
3684
  themeComponentGradient: THEME_COMPONENT_GRADIENT,
3689
3685
  themeSpacingScale: THEME_SPACING_SCALE,
3690
3686
  themeMenuTypes: THEME_MENU_TYPES,
3691
- themeTopBarMenuStyles: THEME_TOP_BAR_MENU_STYLES,
3692
3687
  themeWidgetStyles: THEME_WIDGET_STYLES,
3693
3688
  widgetContextShape: WIDGET_CONTEXT_SHAPE,
3694
3689
  bundleExportContract: BUNDLE_EXPORT_CONTRACT,
package/dist/host.d.ts CHANGED
@@ -155,14 +155,8 @@ export function createToastController(
155
155
 
156
156
  export type ThemeMenuType = "sidebar" | "top-bar" | "bottom-tabs";
157
157
 
158
- // How a `top-bar` app draws its menu — closed by
159
- // `CONTRACT.themeTopBarMenuStyles`. Meaningless on the other two shapes, where
160
- // the resolver always reports "links".
161
- export type ThemeTopBarMenuStyle = "links" | "tabs";
162
-
163
158
  export interface ResolvedNavigation {
164
159
  menuType: ThemeMenuType;
165
- topBarMenuStyle: ThemeTopBarMenuStyle;
166
160
  }
167
161
 
168
162
  /**
@@ -30,9 +30,6 @@ const { CONTRACT, isHexColor } = require("./contract.cjs");
30
30
  // Absent or unknown resolves here, so every app authored before menu types
31
31
  // existed renders and compiles byte-identically.
32
32
  const DEFAULT_MENU_TYPE = "sidebar";
33
- // Today's row of text links, so an app that never made the choice draws exactly
34
- // what it drew before it existed.
35
- const DEFAULT_TOP_BAR_MENU_STYLE = "links";
36
33
 
37
34
  function isPlainObject(value) {
38
35
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -45,8 +42,7 @@ function isPlainObject(value) {
45
42
  * yields the default sidebar rather than something a host has to guard against.
46
43
  *
47
44
  * @param {unknown} navigation — the raw `theme_config.navigation` value.
48
- * @returns {{ menuType: string, topBarMenuStyle: string }} the resolved
49
- * navigation structure.
45
+ * @returns {{ menuType: string }} the resolved navigation structure.
50
46
  */
51
47
  function normaliseNavigation(navigation) {
52
48
  const block = isPlainObject(navigation) ? navigation : {};
@@ -55,16 +51,11 @@ function normaliseNavigation(navigation) {
55
51
  typeof raw === "string" && Object.hasOwn(CONTRACT.themeMenuTypes, raw)
56
52
  ? raw
57
53
  : DEFAULT_MENU_TYPE;
58
- const rawStyle = block.topBarMenuStyle;
59
- // Resolved to `links` for every shape but `top-bar`, so a host reading it can
60
- // never act on a value the app's own chrome has no row to draw.
61
- const topBarMenuStyle =
62
- menuType === "top-bar" &&
63
- typeof rawStyle === "string" &&
64
- Object.hasOwn(CONTRACT.themeTopBarMenuStyles, rawStyle)
65
- ? rawStyle
66
- : DEFAULT_TOP_BAR_MENU_STYLE;
67
- return { menuType, topBarMenuStyle };
54
+ // sc-7044: the SHAPE is the whole answer. A `top-bar` app used to also choose
55
+ // which row carried its menu, and the `links` option drew the site header's
56
+ // own row of text links — a second way to say the same thing. The tab row is
57
+ // the only shape now, so a stored `topBarMenuStyle` is read by nobody.
58
+ return { menuType };
68
59
  }
69
60
 
70
61
  /**
@@ -339,8 +330,8 @@ function resolveTopBarTokens(theme) {
339
330
  // `underline` keeps the tab in the bar's surface and marks it with an
340
331
  // indicator. `attached` makes it a real folder tab: it takes the CONTENT's
341
332
  // surface and sits over the row's divider, so the tab and the page beneath
342
- // read as one plane. Meaningless under the `links` style, which draws no
343
- // tabs — a host reads it only where it has tabs to draw.
333
+ // read as one plane. Meaningless on a shape that draws no tab row — a host
334
+ // reads it only where it has tabs to draw.
344
335
  tabStyle: topBar.tabStyle === "attached" ? "attached" : "underline",
345
336
  // The surface an `attached` tab AND its content panel share. One value for
346
337
  // both, so the join cannot come apart: whatever the current tab is painted,
@@ -369,8 +360,8 @@ function resolveTopBarTokens(theme) {
369
360
  tabPaddingX: tabSpaceOr(topBar.tabPaddingX, DEFAULT_TAB_PADDING_X),
370
361
  tabPaddingY: tabSpaceOr(topBar.tabPaddingY, DEFAULT_TAB_PADDING_Y),
371
362
  // A tab's own surface, authored or nothing. Null means the tab paints none
372
- // and the bar shows through it, which is what both styles did before the
373
- // keys existed — so an app that never set them is unchanged.
363
+ // and the bar shows through it, which is what the row did before the keys
364
+ // existed — so an app that never set them is unchanged.
374
365
  tabBackgroundColor: hexOrNull(topBar.tabBackgroundColor),
375
366
  // Only meaningful where the style draws no panel: under `attached` the
376
367
  // active surface IS `contentSurface` above. Null under `underline` leaves
@@ -21,9 +21,6 @@ import { CONTRACT, isHexColor } from "./contract.js";
21
21
  // Absent or unknown resolves here, so every app authored before menu types
22
22
  // existed renders and compiles byte-identically.
23
23
  const DEFAULT_MENU_TYPE = "sidebar";
24
- // Today's row of text links, so an app that never made the choice draws exactly
25
- // what it drew before it existed.
26
- const DEFAULT_TOP_BAR_MENU_STYLE = "links";
27
24
 
28
25
  function isPlainObject(value) {
29
26
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -36,8 +33,7 @@ function isPlainObject(value) {
36
33
  * yields the default sidebar rather than something a host has to guard against.
37
34
  *
38
35
  * @param {unknown} navigation — the raw `theme_config.navigation` value.
39
- * @returns {{ menuType: string, topBarMenuStyle: string }} the resolved
40
- * navigation structure.
36
+ * @returns {{ menuType: string }} the resolved navigation structure.
41
37
  */
42
38
  export function normaliseNavigation(navigation) {
43
39
  const block = isPlainObject(navigation) ? navigation : {};
@@ -46,16 +42,11 @@ export function normaliseNavigation(navigation) {
46
42
  typeof raw === "string" && Object.hasOwn(CONTRACT.themeMenuTypes, raw)
47
43
  ? raw
48
44
  : DEFAULT_MENU_TYPE;
49
- const rawStyle = block.topBarMenuStyle;
50
- // Resolved to `links` for every shape but `top-bar`, so a host reading it can
51
- // never act on a value the app's own chrome has no row to draw.
52
- const topBarMenuStyle =
53
- menuType === "top-bar" &&
54
- typeof rawStyle === "string" &&
55
- Object.hasOwn(CONTRACT.themeTopBarMenuStyles, rawStyle)
56
- ? rawStyle
57
- : DEFAULT_TOP_BAR_MENU_STYLE;
58
- return { menuType, topBarMenuStyle };
45
+ // sc-7044: the SHAPE is the whole answer. A `top-bar` app used to also choose
46
+ // which row carried its menu, and the `links` option drew the site header's
47
+ // own row of text links — a second way to say the same thing. The tab row is
48
+ // the only shape now, so a stored `topBarMenuStyle` is read by nobody.
49
+ return { menuType };
59
50
  }
60
51
 
61
52
  /**
@@ -330,8 +321,8 @@ export function resolveTopBarTokens(theme) {
330
321
  // `underline` keeps the tab in the bar's surface and marks it with an
331
322
  // indicator. `attached` makes it a real folder tab: it takes the CONTENT's
332
323
  // surface and sits over the row's divider, so the tab and the page beneath
333
- // read as one plane. Meaningless under the `links` style, which draws no
334
- // tabs — a host reads it only where it has tabs to draw.
324
+ // read as one plane. Meaningless on a shape that draws no tab row — a host
325
+ // reads it only where it has tabs to draw.
335
326
  tabStyle: topBar.tabStyle === "attached" ? "attached" : "underline",
336
327
  // The surface an `attached` tab AND its content panel share. One value for
337
328
  // both, so the join cannot come apart: whatever the current tab is painted,
@@ -360,8 +351,8 @@ export function resolveTopBarTokens(theme) {
360
351
  tabPaddingX: tabSpaceOr(topBar.tabPaddingX, DEFAULT_TAB_PADDING_X),
361
352
  tabPaddingY: tabSpaceOr(topBar.tabPaddingY, DEFAULT_TAB_PADDING_Y),
362
353
  // A tab's own surface, authored or nothing. Null means the tab paints none
363
- // and the bar shows through it, which is what both styles did before the
364
- // keys existed — so an app that never set them is unchanged.
354
+ // and the bar shows through it, which is what the row did before the keys
355
+ // existed — so an app that never set them is unchanged.
365
356
  tabBackgroundColor: hexOrNull(topBar.tabBackgroundColor),
366
357
  // Only meaningful where the style draws no panel: under `attached` the
367
358
  // active surface IS `contentSurface` above. Null under `underline` leaves
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.124.0",
3
+ "version": "0.125.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "homepage": "https://github.com/Colix-AB/AppStudio",
6
6
  "type": "module",