@camstack/ui-library 1.2.179 → 1.2.181

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.
@@ -32,13 +32,25 @@ export interface GridProfileOffer {
32
32
  readonly profile: 'high' | 'mid' | 'low';
33
33
  readonly offered: boolean;
34
34
  readonly missingSources: readonly number[];
35
+ /**
36
+ * The canvas this profile composes onto, `WxH`, or empty when it is not
37
+ * offered. DERIVED — the operator no longer types a resolution, so this is
38
+ * the only place the grid's actual size is ever stated.
39
+ */
40
+ readonly canvas: string;
41
+ /**
42
+ * Whether this profile is PUBLISHED, of the ones the grid could serve.
43
+ *
44
+ * A grid's `high` composes its sources' `high`, so on a 4K fleet it is a 4K
45
+ * canvas built from 4K decodes — an opt-in, not something a viewer's adaptive
46
+ * should be handed by climbing to the top rung it can see.
47
+ */
48
+ readonly published: boolean;
35
49
  }
36
50
  export interface GridLayoutView {
37
51
  readonly instanceId: string;
38
52
  readonly deviceId: number;
39
53
  readonly name: string;
40
- readonly width: number;
41
- readonly height: number;
42
54
  readonly fps: number;
43
55
  readonly cells: readonly GridLayoutCell[];
44
56
  readonly profiles: readonly GridProfileOffer[];
package/dist/index.cjs CHANGED
@@ -16641,7 +16641,9 @@ function readProfileOffer(value) {
16641
16641
  return {
16642
16642
  profile,
16643
16643
  offered,
16644
- missingSources
16644
+ missingSources,
16645
+ canvas: typeof value["canvas"] === "string" ? value["canvas"] : "",
16646
+ published: typeof value["published"] === "boolean" ? value["published"] : offered
16645
16647
  };
16646
16648
  }
16647
16649
  /**
@@ -16655,8 +16657,8 @@ function readProfileOffer(value) {
16655
16657
  function parseGridView(raw) {
16656
16658
  if (raw === null) return null;
16657
16659
  if (!isRecord(raw)) return "unreadable";
16658
- const { instanceId, deviceId, name, width, height, fps, cells, profiles } = raw;
16659
- if (typeof instanceId !== "string" || typeof deviceId !== "number" || typeof name !== "string" || typeof width !== "number" || typeof height !== "number" || typeof fps !== "number" || !Array.isArray(cells) || !Array.isArray(profiles)) return "unreadable";
16660
+ const { instanceId, deviceId, name, fps, cells, profiles } = raw;
16661
+ if (typeof instanceId !== "string" || typeof deviceId !== "number" || typeof name !== "string" || typeof fps !== "number" || !Array.isArray(cells) || !Array.isArray(profiles)) return "unreadable";
16660
16662
  const parsed = [];
16661
16663
  for (const entry of cells) {
16662
16664
  const cell = readCell(entry);
@@ -16673,8 +16675,6 @@ function parseGridView(raw) {
16673
16675
  instanceId,
16674
16676
  deviceId,
16675
16677
  name,
16676
- width,
16677
- height,
16678
16678
  fps,
16679
16679
  cells: parsed,
16680
16680
  profiles: offers
@@ -16888,7 +16888,7 @@ function CameraGridLayoutEditor({ deviceId }) {
16888
16888
  setLoaded("unreadable");
16889
16889
  });
16890
16890
  }, [dev, reload]);
16891
- const save = (0, react$1.useCallback)(async () => {
16891
+ const save = (0, react$1.useCallback)(async (extra) => {
16892
16892
  const layout = dev?.cameraGridLayout;
16893
16893
  if (draft === null || !layout) return;
16894
16894
  setSaving(true);
@@ -16896,10 +16896,9 @@ function CameraGridLayoutEditor({ deviceId }) {
16896
16896
  try {
16897
16897
  const outcome = parseGridView(await layout.saveLayout({
16898
16898
  name: draft.name,
16899
- width: draft.width,
16900
- height: draft.height,
16901
16899
  fps: draft.fps,
16902
- cells: draft.cells.map((cell) => ({ ...cell }))
16900
+ cells: draft.cells.map((cell) => ({ ...cell })),
16901
+ ...extra?.publishedProfiles !== void 0 ? { publishedProfiles: [...extra.publishedProfiles] } : {}
16903
16902
  }));
16904
16903
  if (outcome !== null && outcome !== "unreadable") {
16905
16904
  setLoaded(outcome);
@@ -16920,7 +16919,27 @@ function CameraGridLayoutEditor({ deviceId }) {
16920
16919
  } : cell)
16921
16920
  });
16922
16921
  }, []);
16923
- const canvasAspect = (0, react$1.useMemo)(() => draft === null ? 16 / 9 : draft.width / draft.height, [draft]);
16922
+ /**
16923
+ * The shape the grid will actually come out as.
16924
+ *
16925
+ * The canvas is no longer a field: each profile derives its own from the
16926
+ * cells and the sources' dimensions, and the server reports what it came out
16927
+ * as. Drawing at that aspect is the only way the editor shows the truth —
16928
+ * before, it drew at whatever two numbers had been typed, which is exactly
16929
+ * how a layout could look right here and wrong on screen.
16930
+ *
16931
+ * 16:9 while nothing is offered yet: a grid with no cells has no shape, and
16932
+ * an empty editor has to be drawn at something.
16933
+ */
16934
+ const canvasAspect = (0, react$1.useMemo)(() => {
16935
+ const offered = loaded === void 0 || loaded === null || loaded === "unreadable" ? [] : loaded.profiles;
16936
+ for (const row of offered) {
16937
+ if (!row.offered) continue;
16938
+ const [width, height] = row.canvas.split("x").map(Number);
16939
+ if (width !== void 0 && height !== void 0 && width > 0 && height > 0) return width / height;
16940
+ }
16941
+ return 16 / 9;
16942
+ }, [loaded]);
16924
16943
  if (loaded === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WidgetPanel, {
16925
16944
  title: "Grid layout",
16926
16945
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Grid2x2, { className: "h-3.5 w-3.5" }),
@@ -17031,66 +17050,48 @@ function CameraGridLayoutEditor({ deviceId }) {
17031
17050
  }),
17032
17051
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
17033
17052
  className: "grid grid-cols-4 gap-2 text-[10.5px]",
17034
- children: [
17035
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LabelledNumber, {
17036
- label: "Width",
17037
- value: draft.width,
17038
- onChange: (width) => {
17039
- setDraft({
17040
- ...draft,
17041
- width
17042
- });
17043
- }
17044
- }),
17045
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LabelledNumber, {
17046
- label: "Height",
17047
- value: draft.height,
17048
- onChange: (height) => {
17049
- setDraft({
17050
- ...draft,
17051
- height
17052
- });
17053
- }
17054
- }),
17055
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LabelledNumber, {
17056
- label: "FPS",
17057
- value: draft.fps,
17058
- onChange: (fps) => {
17059
- setDraft({
17060
- ...draft,
17061
- fps
17062
- });
17063
- }
17064
- }),
17065
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
17066
- type: "button",
17067
- disabled: draft.cells.length >= MAX_CELLS,
17068
- onClick: () => {
17069
- const first = cameras.find((camera) => camera.id !== deviceId);
17070
- if (!first) return;
17071
- setDraft({
17072
- ...draft,
17073
- cells: [...draft.cells, {
17074
- deviceId: first.id,
17075
- source: FULL_FRAME,
17076
- cell: clampNormalizedRect({
17077
- x: .1,
17078
- y: .1,
17079
- width: .4,
17080
- height: .4
17081
- })
17082
- }]
17083
- });
17084
- setSelected(draft.cells.length);
17085
- },
17086
- className: "flex items-center justify-center gap-1 self-end rounded border border-border px-2 py-1 uppercase",
17087
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3 w-3" }), " Cell"]
17088
- })
17089
- ]
17053
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LabelledNumber, {
17054
+ label: "FPS",
17055
+ value: draft.fps,
17056
+ onChange: (fps) => {
17057
+ setDraft({
17058
+ ...draft,
17059
+ fps
17060
+ });
17061
+ }
17062
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
17063
+ type: "button",
17064
+ disabled: draft.cells.length >= MAX_CELLS,
17065
+ onClick: () => {
17066
+ const first = cameras.find((camera) => camera.id !== deviceId);
17067
+ if (!first) return;
17068
+ setDraft({
17069
+ ...draft,
17070
+ cells: [...draft.cells, {
17071
+ deviceId: first.id,
17072
+ source: FULL_FRAME,
17073
+ cell: clampNormalizedRect({
17074
+ x: .1,
17075
+ y: .1,
17076
+ width: .4,
17077
+ height: .4
17078
+ })
17079
+ }]
17080
+ });
17081
+ setSelected(draft.cells.length);
17082
+ },
17083
+ className: "flex items-center justify-center gap-1 self-end rounded border border-border px-2 py-1 uppercase",
17084
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3 w-3" }), " Cell"]
17085
+ })]
17090
17086
  }),
17091
17087
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridProfileReport, {
17092
17088
  offers: loaded.profiles,
17093
- cameras
17089
+ cameras,
17090
+ onToggle: (profile) => {
17091
+ if (draft === null) return;
17092
+ const current = loaded.profiles.filter((row) => row.published).map((row) => row.profile);
17093
+ save({ publishedProfiles: current.includes(profile) ? current.filter((entry) => entry !== profile) : [...current, profile] });
17094
+ }
17094
17095
  }),
17095
17096
  draft.cells[selected] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CellInspector, {
17096
17097
  cell: draft.cells[selected],
@@ -17120,7 +17121,7 @@ function CameraGridLayoutEditor({ deviceId }) {
17120
17121
  * Read-only on purpose: what is on offer is decided by the source cameras'
17121
17122
  * assignments, and a control here would be a switch that writes nothing (D62).
17122
17123
  */
17123
- function GridProfileReport({ offers, cameras }) {
17124
+ function GridProfileReport({ offers, cameras, onToggle }) {
17124
17125
  const nameOf = (deviceId) => cameras.find((camera) => camera.id === deviceId)?.name ?? `#${String(deviceId)}`;
17125
17126
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
17126
17127
  className: "flex flex-col gap-1 rounded border border-border p-2 text-[10.5px]",
@@ -17129,16 +17130,33 @@ function GridProfileReport({ offers, cameras }) {
17129
17130
  children: "Published profiles"
17130
17131
  }), offers.map((offer) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
17131
17132
  className: "flex items-baseline gap-2",
17132
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17133
- className: "w-10 uppercase",
17134
- children: offer.profile
17135
- }), offer.offered ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
17136
- className: "text-foreground-subtle",
17137
- children: ["composed from every source’s ", offer.profile]
17138
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17139
- className: "text-danger",
17140
- children: offer.missingSources.length === 0 ? "not offered — this grid has no cells yet" : `not offered — no ${offer.profile} on ${offer.missingSources.map(nameOf).join(", ")}`
17141
- })]
17133
+ children: [
17134
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
17135
+ type: "checkbox",
17136
+ checked: offer.published,
17137
+ disabled: !offer.offered,
17138
+ "aria-label": `publish ${offer.profile}`,
17139
+ onChange: () => {
17140
+ onToggle(offer.profile);
17141
+ }
17142
+ }),
17143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17144
+ className: "w-10 uppercase",
17145
+ children: offer.profile
17146
+ }),
17147
+ offer.offered ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
17148
+ className: "text-foreground-subtle",
17149
+ children: [
17150
+ offer.canvas,
17151
+ " —",
17152
+ " ",
17153
+ offer.published ? `composed from every source’s ${offer.profile}` : "serviceable, not published"
17154
+ ]
17155
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
17156
+ className: "text-danger",
17157
+ children: offer.missingSources.length === 0 ? "not offered — this grid has no cells yet" : `not offered — no ${offer.profile} on ${offer.missingSources.map(nameOf).join(", ")}`
17158
+ })
17159
+ ]
17142
17160
  }, offer.profile))]
17143
17161
  });
17144
17162
  }
package/dist/index.js CHANGED
@@ -16617,7 +16617,9 @@ function readProfileOffer(value) {
16617
16617
  return {
16618
16618
  profile,
16619
16619
  offered,
16620
- missingSources
16620
+ missingSources,
16621
+ canvas: typeof value["canvas"] === "string" ? value["canvas"] : "",
16622
+ published: typeof value["published"] === "boolean" ? value["published"] : offered
16621
16623
  };
16622
16624
  }
16623
16625
  /**
@@ -16631,8 +16633,8 @@ function readProfileOffer(value) {
16631
16633
  function parseGridView(raw) {
16632
16634
  if (raw === null) return null;
16633
16635
  if (!isRecord(raw)) return "unreadable";
16634
- const { instanceId, deviceId, name, width, height, fps, cells, profiles } = raw;
16635
- if (typeof instanceId !== "string" || typeof deviceId !== "number" || typeof name !== "string" || typeof width !== "number" || typeof height !== "number" || typeof fps !== "number" || !Array.isArray(cells) || !Array.isArray(profiles)) return "unreadable";
16636
+ const { instanceId, deviceId, name, fps, cells, profiles } = raw;
16637
+ if (typeof instanceId !== "string" || typeof deviceId !== "number" || typeof name !== "string" || typeof fps !== "number" || !Array.isArray(cells) || !Array.isArray(profiles)) return "unreadable";
16636
16638
  const parsed = [];
16637
16639
  for (const entry of cells) {
16638
16640
  const cell = readCell(entry);
@@ -16649,8 +16651,6 @@ function parseGridView(raw) {
16649
16651
  instanceId,
16650
16652
  deviceId,
16651
16653
  name,
16652
- width,
16653
- height,
16654
16654
  fps,
16655
16655
  cells: parsed,
16656
16656
  profiles: offers
@@ -16864,7 +16864,7 @@ function CameraGridLayoutEditor({ deviceId }) {
16864
16864
  setLoaded("unreadable");
16865
16865
  });
16866
16866
  }, [dev, reload]);
16867
- const save = useCallback(async () => {
16867
+ const save = useCallback(async (extra) => {
16868
16868
  const layout = dev?.cameraGridLayout;
16869
16869
  if (draft === null || !layout) return;
16870
16870
  setSaving(true);
@@ -16872,10 +16872,9 @@ function CameraGridLayoutEditor({ deviceId }) {
16872
16872
  try {
16873
16873
  const outcome = parseGridView(await layout.saveLayout({
16874
16874
  name: draft.name,
16875
- width: draft.width,
16876
- height: draft.height,
16877
16875
  fps: draft.fps,
16878
- cells: draft.cells.map((cell) => ({ ...cell }))
16876
+ cells: draft.cells.map((cell) => ({ ...cell })),
16877
+ ...extra?.publishedProfiles !== void 0 ? { publishedProfiles: [...extra.publishedProfiles] } : {}
16879
16878
  }));
16880
16879
  if (outcome !== null && outcome !== "unreadable") {
16881
16880
  setLoaded(outcome);
@@ -16896,7 +16895,27 @@ function CameraGridLayoutEditor({ deviceId }) {
16896
16895
  } : cell)
16897
16896
  });
16898
16897
  }, []);
16899
- const canvasAspect = useMemo(() => draft === null ? 16 / 9 : draft.width / draft.height, [draft]);
16898
+ /**
16899
+ * The shape the grid will actually come out as.
16900
+ *
16901
+ * The canvas is no longer a field: each profile derives its own from the
16902
+ * cells and the sources' dimensions, and the server reports what it came out
16903
+ * as. Drawing at that aspect is the only way the editor shows the truth —
16904
+ * before, it drew at whatever two numbers had been typed, which is exactly
16905
+ * how a layout could look right here and wrong on screen.
16906
+ *
16907
+ * 16:9 while nothing is offered yet: a grid with no cells has no shape, and
16908
+ * an empty editor has to be drawn at something.
16909
+ */
16910
+ const canvasAspect = useMemo(() => {
16911
+ const offered = loaded === void 0 || loaded === null || loaded === "unreadable" ? [] : loaded.profiles;
16912
+ for (const row of offered) {
16913
+ if (!row.offered) continue;
16914
+ const [width, height] = row.canvas.split("x").map(Number);
16915
+ if (width !== void 0 && height !== void 0 && width > 0 && height > 0) return width / height;
16916
+ }
16917
+ return 16 / 9;
16918
+ }, [loaded]);
16900
16919
  if (loaded === void 0) return /* @__PURE__ */ jsx(WidgetPanel, {
16901
16920
  title: "Grid layout",
16902
16921
  icon: /* @__PURE__ */ jsx(Grid2x2, { className: "h-3.5 w-3.5" }),
@@ -17007,66 +17026,48 @@ function CameraGridLayoutEditor({ deviceId }) {
17007
17026
  }),
17008
17027
  /* @__PURE__ */ jsxs("div", {
17009
17028
  className: "grid grid-cols-4 gap-2 text-[10.5px]",
17010
- children: [
17011
- /* @__PURE__ */ jsx(LabelledNumber, {
17012
- label: "Width",
17013
- value: draft.width,
17014
- onChange: (width) => {
17015
- setDraft({
17016
- ...draft,
17017
- width
17018
- });
17019
- }
17020
- }),
17021
- /* @__PURE__ */ jsx(LabelledNumber, {
17022
- label: "Height",
17023
- value: draft.height,
17024
- onChange: (height) => {
17025
- setDraft({
17026
- ...draft,
17027
- height
17028
- });
17029
- }
17030
- }),
17031
- /* @__PURE__ */ jsx(LabelledNumber, {
17032
- label: "FPS",
17033
- value: draft.fps,
17034
- onChange: (fps) => {
17035
- setDraft({
17036
- ...draft,
17037
- fps
17038
- });
17039
- }
17040
- }),
17041
- /* @__PURE__ */ jsxs("button", {
17042
- type: "button",
17043
- disabled: draft.cells.length >= MAX_CELLS,
17044
- onClick: () => {
17045
- const first = cameras.find((camera) => camera.id !== deviceId);
17046
- if (!first) return;
17047
- setDraft({
17048
- ...draft,
17049
- cells: [...draft.cells, {
17050
- deviceId: first.id,
17051
- source: FULL_FRAME,
17052
- cell: clampNormalizedRect({
17053
- x: .1,
17054
- y: .1,
17055
- width: .4,
17056
- height: .4
17057
- })
17058
- }]
17059
- });
17060
- setSelected(draft.cells.length);
17061
- },
17062
- className: "flex items-center justify-center gap-1 self-end rounded border border-border px-2 py-1 uppercase",
17063
- children: [/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }), " Cell"]
17064
- })
17065
- ]
17029
+ children: [/* @__PURE__ */ jsx(LabelledNumber, {
17030
+ label: "FPS",
17031
+ value: draft.fps,
17032
+ onChange: (fps) => {
17033
+ setDraft({
17034
+ ...draft,
17035
+ fps
17036
+ });
17037
+ }
17038
+ }), /* @__PURE__ */ jsxs("button", {
17039
+ type: "button",
17040
+ disabled: draft.cells.length >= MAX_CELLS,
17041
+ onClick: () => {
17042
+ const first = cameras.find((camera) => camera.id !== deviceId);
17043
+ if (!first) return;
17044
+ setDraft({
17045
+ ...draft,
17046
+ cells: [...draft.cells, {
17047
+ deviceId: first.id,
17048
+ source: FULL_FRAME,
17049
+ cell: clampNormalizedRect({
17050
+ x: .1,
17051
+ y: .1,
17052
+ width: .4,
17053
+ height: .4
17054
+ })
17055
+ }]
17056
+ });
17057
+ setSelected(draft.cells.length);
17058
+ },
17059
+ className: "flex items-center justify-center gap-1 self-end rounded border border-border px-2 py-1 uppercase",
17060
+ children: [/* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }), " Cell"]
17061
+ })]
17066
17062
  }),
17067
17063
  /* @__PURE__ */ jsx(GridProfileReport, {
17068
17064
  offers: loaded.profiles,
17069
- cameras
17065
+ cameras,
17066
+ onToggle: (profile) => {
17067
+ if (draft === null) return;
17068
+ const current = loaded.profiles.filter((row) => row.published).map((row) => row.profile);
17069
+ save({ publishedProfiles: current.includes(profile) ? current.filter((entry) => entry !== profile) : [...current, profile] });
17070
+ }
17070
17071
  }),
17071
17072
  draft.cells[selected] !== void 0 && /* @__PURE__ */ jsx(CellInspector, {
17072
17073
  cell: draft.cells[selected],
@@ -17096,7 +17097,7 @@ function CameraGridLayoutEditor({ deviceId }) {
17096
17097
  * Read-only on purpose: what is on offer is decided by the source cameras'
17097
17098
  * assignments, and a control here would be a switch that writes nothing (D62).
17098
17099
  */
17099
- function GridProfileReport({ offers, cameras }) {
17100
+ function GridProfileReport({ offers, cameras, onToggle }) {
17100
17101
  const nameOf = (deviceId) => cameras.find((camera) => camera.id === deviceId)?.name ?? `#${String(deviceId)}`;
17101
17102
  return /* @__PURE__ */ jsxs("div", {
17102
17103
  className: "flex flex-col gap-1 rounded border border-border p-2 text-[10.5px]",
@@ -17105,16 +17106,33 @@ function GridProfileReport({ offers, cameras }) {
17105
17106
  children: "Published profiles"
17106
17107
  }), offers.map((offer) => /* @__PURE__ */ jsxs("div", {
17107
17108
  className: "flex items-baseline gap-2",
17108
- children: [/* @__PURE__ */ jsx("span", {
17109
- className: "w-10 uppercase",
17110
- children: offer.profile
17111
- }), offer.offered ? /* @__PURE__ */ jsxs("span", {
17112
- className: "text-foreground-subtle",
17113
- children: ["composed from every source’s ", offer.profile]
17114
- }) : /* @__PURE__ */ jsx("span", {
17115
- className: "text-danger",
17116
- children: offer.missingSources.length === 0 ? "not offered — this grid has no cells yet" : `not offered — no ${offer.profile} on ${offer.missingSources.map(nameOf).join(", ")}`
17117
- })]
17109
+ children: [
17110
+ /* @__PURE__ */ jsx("input", {
17111
+ type: "checkbox",
17112
+ checked: offer.published,
17113
+ disabled: !offer.offered,
17114
+ "aria-label": `publish ${offer.profile}`,
17115
+ onChange: () => {
17116
+ onToggle(offer.profile);
17117
+ }
17118
+ }),
17119
+ /* @__PURE__ */ jsx("span", {
17120
+ className: "w-10 uppercase",
17121
+ children: offer.profile
17122
+ }),
17123
+ offer.offered ? /* @__PURE__ */ jsxs("span", {
17124
+ className: "text-foreground-subtle",
17125
+ children: [
17126
+ offer.canvas,
17127
+ " —",
17128
+ " ",
17129
+ offer.published ? `composed from every source’s ${offer.profile}` : "serviceable, not published"
17130
+ ]
17131
+ }) : /* @__PURE__ */ jsx("span", {
17132
+ className: "text-danger",
17133
+ children: offer.missingSources.length === 0 ? "not offered — this grid has no cells yet" : `not offered — no ${offer.profile} on ${offer.missingSources.map(nameOf).join(", ")}`
17134
+ })
17135
+ ]
17118
17136
  }, offer.profile))]
17119
17137
  });
17120
17138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/ui-library",
3
- "version": "1.2.179",
3
+ "version": "1.2.181",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",