@flamingo-stack/openframe-frontend-core 0.0.842 → 0.0.844

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 (77) hide show
  1. package/dist/build/export-map.json +1 -1
  2. package/dist/{chunk-2HRQU4ZL.cjs → chunk-7ERU4NGF.cjs} +10 -10
  3. package/dist/{chunk-RAFWT3WG.cjs → chunk-B4FUDHRE.cjs} +24 -24
  4. package/dist/{chunk-WOYARKPQ.cjs → chunk-C2X73N3E.cjs} +7 -7
  5. package/dist/{chunk-GQJXOX5S.cjs → chunk-EGOCAIZJ.cjs} +158 -86
  6. package/dist/{chunk-IVM35OAW.cjs → chunk-GBB7NLN7.cjs} +3 -3
  7. package/dist/{chunk-IVS23CAK.cjs → chunk-IUWCOK3X.cjs} +3 -3
  8. package/dist/{chunk-7UZYI4GV.cjs → chunk-KSOBM66X.cjs} +2 -2
  9. package/dist/{chunk-3ONBCAD2.cjs → chunk-KSPMB4GV.cjs} +22 -33
  10. package/dist/{chunk-CQQRGM6D.cjs → chunk-M7QI23GE.cjs} +31 -31
  11. package/dist/{chunk-SMZWXY4X.cjs → chunk-QKZQ5Q6E.cjs} +6 -1
  12. package/dist/{chunk-ZOWAYKDN.cjs → chunk-SVI3AB3A.cjs} +3 -3
  13. package/dist/{chunk-YNKXXDOI.cjs → chunk-WUJGPHF2.cjs} +4 -4
  14. package/dist/{cloud-tenants-U6Y2UZVF.cjs → cloud-tenants-5C32YDIE.cjs} +2 -2
  15. package/dist/components/case-studies/index.cjs +11 -11
  16. package/dist/components/contact/index.cjs +3 -3
  17. package/dist/components/docs/index.cjs +4 -4
  18. package/dist/components/embeds/index.cjs +3 -3
  19. package/dist/components/faq/index.cjs +3 -3
  20. package/dist/components/features/devices/device-unread-indicator.d.ts +6 -0
  21. package/dist/components/features/devices/device-unread-indicator.d.ts.map +1 -0
  22. package/dist/components/features/devices/device-unread-indicator.js +12 -0
  23. package/dist/components/features/devices/devices-grid.d.ts.map +1 -1
  24. package/dist/components/features/devices/devices-grid.js +5 -1
  25. package/dist/components/features/devices/devices-table-columns.d.ts.map +1 -1
  26. package/dist/components/features/devices/devices-table-columns.js +3 -1
  27. package/dist/components/features/devices/devices-view-config.d.ts +6 -4
  28. package/dist/components/features/devices/devices-view-config.d.ts.map +1 -1
  29. package/dist/components/features/devices/index.d.ts +1 -0
  30. package/dist/components/features/devices/index.d.ts.map +1 -1
  31. package/dist/components/features/devices/index.js +2 -0
  32. package/dist/components/features/devices/types.d.ts +2 -0
  33. package/dist/components/features/devices/types.d.ts.map +1 -1
  34. package/dist/components/features/index.cjs +4 -2
  35. package/dist/components/features/software/update-settings-fields.d.ts.map +1 -1
  36. package/dist/components/features/software/update-settings-fields.js +1 -1
  37. package/dist/components/help-center-pages/index.cjs +46 -46
  38. package/dist/components/index.cjs +25 -17
  39. package/dist/components/meeting-scheduler/index.cjs +20 -20
  40. package/dist/components/navigation/count-pill.d.ts +10 -0
  41. package/dist/components/navigation/count-pill.d.ts.map +1 -0
  42. package/dist/components/navigation/count-pill.js +18 -0
  43. package/dist/components/navigation/index.cjs +9 -3
  44. package/dist/components/navigation/index.d.ts +4 -0
  45. package/dist/components/navigation/index.d.ts.map +1 -1
  46. package/dist/components/navigation/index.js +5 -0
  47. package/dist/components/navigation/navigation-sidebar-grouped.d.ts.map +1 -1
  48. package/dist/components/navigation/navigation-sidebar-grouped.js +1 -12
  49. package/dist/components/navigation/unread-notifications-indicator.d.ts +19 -0
  50. package/dist/components/navigation/unread-notifications-indicator.d.ts.map +1 -0
  51. package/dist/components/navigation/unread-notifications-indicator.js +45 -0
  52. package/dist/components/onboarding-guides/index.cjs +3 -3
  53. package/dist/components/product-demo/fixtures/devices.d.ts.map +1 -1
  54. package/dist/components/product-demo/fixtures/devices.js +6 -1
  55. package/dist/components/product-demo/index.cjs +11 -11
  56. package/dist/components/tickets/index.cjs +5 -5
  57. package/dist/components/ui/index.cjs +2 -2
  58. package/dist/{devices-HB4GEMJH.cjs → devices-F2ENCTJM.cjs} +5 -5
  59. package/dist/index.cjs +11 -3
  60. package/dist/{knowledge-I53Z6DV7.cjs → knowledge-ZNUNREKL.cjs} +2 -2
  61. package/dist/{logs-SFLAZIQG.cjs → logs-DHZFCX2I.cjs} +9 -9
  62. package/dist/{policies-EIAGQO6T.cjs → policies-2FKRLSOC.cjs} +6 -6
  63. package/dist/{scripts-SNJ4UVRF.cjs → scripts-J3J7XCT4.cjs} +7 -7
  64. package/dist/{software-update-AZFRUC5N.cjs → software-update-AIQHLJ5O.cjs} +7 -7
  65. package/package.json +1 -1
  66. package/src/components/features/devices/device-unread-indicator.tsx +14 -0
  67. package/src/components/features/devices/devices-grid.tsx +9 -1
  68. package/src/components/features/devices/devices-table-columns.tsx +3 -0
  69. package/src/components/features/devices/devices-view-config.tsx +6 -4
  70. package/src/components/features/devices/index.ts +1 -0
  71. package/src/components/features/devices/types.ts +2 -0
  72. package/src/components/features/software/update-settings-fields.tsx +19 -16
  73. package/src/components/navigation/count-pill.tsx +25 -0
  74. package/src/components/navigation/index.ts +4 -0
  75. package/src/components/navigation/navigation-sidebar-grouped.tsx +1 -14
  76. package/src/components/navigation/unread-notifications-indicator.tsx +69 -0
  77. package/src/components/product-demo/fixtures/devices.ts +7 -0
@@ -8,7 +8,7 @@ var _chunkLHV2R6QUcjs = require('./chunk-LHV2R6QU.cjs');
8
8
  var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
9
9
 
10
10
 
11
- var _chunkGQJXOX5Scjs = require('./chunk-GQJXOX5S.cjs');
11
+ var _chunkEGOCAIZJcjs = require('./chunk-EGOCAIZJ.cjs');
12
12
  require('./chunk-WCK5T3UD.cjs');
13
13
  require('./chunk-744JXTDP.cjs');
14
14
  require('./chunk-NMLNZCK2.cjs');
@@ -1908,7 +1908,7 @@ function KnowledgeScreen(_props) {
1908
1908
  const cast = _chunkLHV2R6QUcjs.useProductDemoCast.call(void 0, );
1909
1909
  const fixture = _react.useMemo.call(void 0, () => buildKnowledgeFixture(cast), [cast]);
1910
1910
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1911
- _chunkGQJXOX5Scjs.KnowledgeBaseArticleView,
1911
+ _chunkEGOCAIZJcjs.KnowledgeBaseArticleView,
1912
1912
  {
1913
1913
  ...fixture.article,
1914
1914
  content: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWPMJUYW3cjs.SimpleMarkdownRenderer, { content: fixture.content, textSize: "compact" })
@@ -15,7 +15,7 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
15
15
 
16
16
 
17
17
 
18
- var _chunkGQJXOX5Scjs = require('./chunk-GQJXOX5S.cjs');
18
+ var _chunkEGOCAIZJcjs = require('./chunk-EGOCAIZJ.cjs');
19
19
  require('./chunk-WCK5T3UD.cjs');
20
20
  require('./chunk-744JXTDP.cjs');
21
21
  require('./chunk-NMLNZCK2.cjs');
@@ -1890,7 +1890,7 @@ function entry(logId, minutesAgo, severity, toolType, device2, summary) {
1890
1890
  id: logId,
1891
1891
  logId,
1892
1892
  timestamp: displayTime(_chunkFDZOE5TLcjs.demoMinutesAgo.call(void 0, minutesAgo)),
1893
- status: { label: severity, variant: _chunkGQJXOX5Scjs.logSeverityVariant.call(void 0, severity) },
1893
+ status: { label: severity, variant: _chunkEGOCAIZJcjs.logSeverityVariant.call(void 0, severity) },
1894
1894
  source: { name: toolType, toolType },
1895
1895
  device: device2,
1896
1896
  description: { title: summary }
@@ -1962,13 +1962,13 @@ var NO_FILTERS = {};
1962
1962
  var ACTION_COLUMNS = ["copy", "quickView", "open"];
1963
1963
  var BESIDE_DRAWER_HIDDEN = [...ACTION_COLUMNS, "tool", "source"];
1964
1964
  var logHref = () => "#";
1965
- var copyAction = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGQJXOX5Scjs.LogCopyIconButton, {});
1965
+ var copyAction = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEGOCAIZJcjs.LogCopyIconButton, {});
1966
1966
  function LogsScreen({ compact = false }) {
1967
1967
  const [dock, setDock] = _react.useState.call(void 0, null);
1968
1968
  const selected = LOGS_FIXTURE_SELECTED;
1969
1969
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full bg-ods-bg", children: [
1970
1970
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 flex-1 overflow-clip", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1971
- _chunkGQJXOX5Scjs.LogsPageView,
1971
+ _chunkEGOCAIZJcjs.LogsPageView,
1972
1972
  {
1973
1973
  showHeader: false,
1974
1974
  className: "p-[var(--spacing-system-l)]",
@@ -1976,7 +1976,7 @@ function LogsScreen({ compact = false }) {
1976
1976
  onSearchChange: noop,
1977
1977
  onOpenFilters: noop,
1978
1978
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1979
- _chunkGQJXOX5Scjs.LogsTableView,
1979
+ _chunkEGOCAIZJcjs.LogsTableView,
1980
1980
  {
1981
1981
  logs: LOGS_FIXTURE_ENTRIES,
1982
1982
  facets: LOGS_FIXTURE_FACETS,
@@ -1991,7 +1991,7 @@ function LogsScreen({ compact = false }) {
1991
1991
  }
1992
1992
  ) }),
1993
1993
  !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: setDock, className: "relative w-[calc(400px+var(--spacing-system-m))] shrink-0", children: dock && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkOT6P57ZScjs.AppLayoutDrawerContainerContext.Provider, { value: dock, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1994
- _chunkGQJXOX5Scjs.LogDrawer,
1994
+ _chunkEGOCAIZJcjs.LogDrawer,
1995
1995
  {
1996
1996
  docked: true,
1997
1997
  isOpen: true,
@@ -2003,12 +2003,12 @@ function LogsScreen({ compact = false }) {
2003
2003
  { label: "Log ID", value: selected.logId },
2004
2004
  {
2005
2005
  label: "Source",
2006
- value: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGQJXOX5Scjs.ToolBadge, { toolType: _chunk4ASUUB3Acjs.normalizeToolTypeWithFallback.call(void 0, selected.source.toolType) })
2006
+ value: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEGOCAIZJcjs.ToolBadge, { toolType: _chunk4ASUUB3Acjs.normalizeToolTypeWithFallback.call(void 0, selected.source.toolType) })
2007
2007
  },
2008
- { label: "Device", value: _chunkGQJXOX5Scjs.logSourceLabels.call(void 0, selected.device).deviceName }
2008
+ { label: "Device", value: _chunkEGOCAIZJcjs.logSourceLabels.call(void 0, selected.device).deviceName }
2009
2009
  ],
2010
2010
  deviceCard: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2011
- _chunkGQJXOX5Scjs.DeviceCard,
2011
+ _chunkEGOCAIZJcjs.DeviceCard,
2012
2012
  {
2013
2013
  ...LOGS_FIXTURE_DEVICE,
2014
2014
  actions: {
@@ -10,7 +10,7 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
10
10
 
11
11
 
12
12
 
13
- var _chunkGQJXOX5Scjs = require('./chunk-GQJXOX5S.cjs');
13
+ var _chunkEGOCAIZJcjs = require('./chunk-EGOCAIZJ.cjs');
14
14
  require('./chunk-WCK5T3UD.cjs');
15
15
  require('./chunk-744JXTDP.cjs');
16
16
  require('./chunk-NMLNZCK2.cjs');
@@ -1894,14 +1894,14 @@ var POLICIES = [
1894
1894
  policy("policy-local-admins", "No local admins", "No everyday account is a local administrator.", false, 0)
1895
1895
  ];
1896
1896
  var POLICIES_FIXTURE = {
1897
- summary: _chunkGQJXOX5Scjs.computePolicySummary.call(void 0, POLICIES),
1897
+ summary: _chunkEGOCAIZJcjs.computePolicySummary.call(void 0, POLICIES),
1898
1898
  rows: POLICIES.map((item) => ({
1899
1899
  id: item.id,
1900
1900
  name: item.name,
1901
1901
  description: item.description,
1902
1902
  critical: item.critical,
1903
1903
  severityLabel: item.critical ? "Critical" : "Low",
1904
- status: _chunkGQJXOX5Scjs.getPolicyTableStatus.call(void 0, item),
1904
+ status: _chunkEGOCAIZJcjs.getPolicyTableStatus.call(void 0, item),
1905
1905
  actions: [
1906
1906
  { label: "Policy Details", onClick: noop },
1907
1907
  { label: "Delete Policy", onClick: noop }
@@ -1915,16 +1915,16 @@ var POLICIES_FIXTURE = {
1915
1915
  var _jsxruntime = require('react/jsx-runtime');
1916
1916
  function PoliciesScreen({ compact = false }) {
1917
1917
  const table = /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1918
- _chunkGQJXOX5Scjs.PoliciesTable,
1918
+ _chunkEGOCAIZJcjs.PoliciesTable,
1919
1919
  {
1920
1920
  rows: POLICIES_FIXTURE.rows,
1921
1921
  rowAsLink: true,
1922
- rightSlot: compact ? void 0 : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGQJXOX5Scjs.DataTable.RowCount, {}),
1922
+ rightSlot: compact ? void 0 : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEGOCAIZJcjs.DataTable.RowCount, {}),
1923
1923
  emptyMessage: "No policies found."
1924
1924
  }
1925
1925
  );
1926
1926
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full flex-col gap-[var(--spacing-system-l)] bg-ods-bg p-[var(--spacing-system-l)]", children: [
1927
- !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGQJXOX5Scjs.PolicySummaryCards, { summary: POLICIES_FIXTURE.summary, now: POLICIES_FIXTURE.now }),
1927
+ !compact && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEGOCAIZJcjs.PolicySummaryCards, { summary: POLICIES_FIXTURE.summary, now: POLICIES_FIXTURE.now }),
1928
1928
  table
1929
1929
  ] });
1930
1930
  }
@@ -13,7 +13,7 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
13
13
 
14
14
 
15
15
 
16
- var _chunkGQJXOX5Scjs = require('./chunk-GQJXOX5S.cjs');
16
+ var _chunkEGOCAIZJcjs = require('./chunk-EGOCAIZJ.cjs');
17
17
 
18
18
 
19
19
  var _chunkWCK5T3UDcjs = require('./chunk-WCK5T3UD.cjs');
@@ -1998,8 +1998,8 @@ function buildScriptsFixture(cast) {
1998
1998
  var _jsxruntime = require('react/jsx-runtime');
1999
1999
  var WIDE_HIDDEN_COLUMNS = ["result", "open"];
2000
2000
  var COMPACT_COLUMNS = [
2001
- { ..._chunkGQJXOX5Scjs.EXECUTION_COLUMNS.machineId, width: "flex-1 min-w-0", hideAt: void 0 },
2002
- _chunkGQJXOX5Scjs.EXECUTION_COLUMNS.status
2001
+ { ..._chunkEGOCAIZJcjs.EXECUTION_COLUMNS.machineId, width: "flex-1 min-w-0", hideAt: void 0 },
2002
+ _chunkEGOCAIZJcjs.EXECUTION_COLUMNS.status
2003
2003
  ];
2004
2004
  var COMPACT_ROWS = 4;
2005
2005
  var TOKEN_CLASS = {
@@ -2058,7 +2058,7 @@ function ScriptCodePicture({ script }) {
2058
2058
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex min-w-0 items-center justify-between gap-[var(--spacing-system-m)]", children: [
2059
2059
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkWCK5T3UDcjs.TruncateText, { variant: "h6", tone: "secondary", children: `Written by ${provenance.author} \xB7 approved by ${provenance.approver}` }),
2060
2060
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2061
- _chunkGQJXOX5Scjs.ScriptShellBadge,
2061
+ _chunkEGOCAIZJcjs.ScriptShellBadge,
2062
2062
  {
2063
2063
  value: script.shellType,
2064
2064
  className: "shrink-0 text-ods-text-secondary text-h6",
@@ -2082,7 +2082,7 @@ function ScriptsScreen({ compact = false }) {
2082
2082
  return (
2083
2083
  // Four runs of the card's row height fill the frame; the padding is what is left round them.
2084
2084
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full overflow-hidden bg-ods-bg p-[var(--spacing-system-xsf)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2085
- _chunkGQJXOX5Scjs.ScriptExecutionsTable,
2085
+ _chunkEGOCAIZJcjs.ScriptExecutionsTable,
2086
2086
  {
2087
2087
  rows: fixture.executions.slice(0, COMPACT_ROWS),
2088
2088
  columns: COMPACT_COLUMNS,
@@ -2092,13 +2092,13 @@ function ScriptsScreen({ compact = false }) {
2092
2092
  ) })
2093
2093
  );
2094
2094
  }
2095
- const renderOutput = (row) => row.id === fixture.openExecutionId ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkGQJXOX5Scjs.ScriptExecutionResult, { result: row.result, empty: "\u2014" }) : null;
2095
+ const renderOutput = (row) => row.id === fixture.openExecutionId ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEGOCAIZJcjs.ScriptExecutionResult, { result: row.result, empty: "\u2014" }) : null;
2096
2096
  return (
2097
2097
  // The frame's tighter padding and no header row: the output opened under a run needs the height.
2098
2098
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex h-full gap-[var(--spacing-system-m)] overflow-hidden bg-ods-bg p-[var(--spacing-system-m)]", children: [
2099
2099
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-[39%] shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ScriptCodePicture, { script: fixture.script }) }),
2100
2100
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 flex-1 overflow-hidden", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
2101
- _chunkGQJXOX5Scjs.ScriptExecutionsTable,
2101
+ _chunkEGOCAIZJcjs.ScriptExecutionsTable,
2102
2102
  {
2103
2103
  rows: fixture.executions,
2104
2104
  hideColumns: WIDE_HIDDEN_COLUMNS,
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkSMZWXY4Xcjs = require('./chunk-SMZWXY4X.cjs');
5
+ var _chunkQKZQ5Q6Ecjs = require('./chunk-QKZQ5Q6E.cjs');
6
6
 
7
7
 
8
8
  var _chunkLHV2R6QUcjs = require('./chunk-LHV2R6QU.cjs');
@@ -15,7 +15,7 @@ var _chunkFDZOE5TLcjs = require('./chunk-FDZOE5TL.cjs');
15
15
 
16
16
 
17
17
 
18
- var _chunkGQJXOX5Scjs = require('./chunk-GQJXOX5S.cjs');
18
+ var _chunkEGOCAIZJcjs = require('./chunk-EGOCAIZJ.cjs');
19
19
 
20
20
 
21
21
  var _chunkWCK5T3UDcjs = require('./chunk-WCK5T3UD.cjs');
@@ -1905,7 +1905,7 @@ var initialValues = {
1905
1905
  timeReference: "SERVER"
1906
1906
  };
1907
1907
  function buildSoftwareUpdateFixture(cast) {
1908
- const devices = _chunkSMZWXY4Xcjs.buildDevicesFixture.call(void 0, cast).devices.filter((device) => device.osType === "WINDOWS");
1908
+ const devices = _chunkQKZQ5Q6Ecjs.buildDevicesFixture.call(void 0, cast).devices.filter((device) => device.osType === "WINDOWS");
1909
1909
  return {
1910
1910
  action: "UPDATE",
1911
1911
  package: chrome,
@@ -1925,12 +1925,12 @@ function StaticPackageField({ value }) {
1925
1925
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1926
1926
  _chunkWCK5T3UDcjs.Autocomplete,
1927
1927
  {
1928
- label: _chunkGQJXOX5Scjs.PACKAGE_SEARCH_LABEL,
1928
+ label: _chunkEGOCAIZJcjs.PACKAGE_SEARCH_LABEL,
1929
1929
  labelVariant: "large",
1930
1930
  options: value ? [{ value: value.id, label: value.name, description: _nullishCoalesce(value.description, () => ( void 0)) }] : [],
1931
1931
  value: _nullishCoalesce(_optionalChain([value, 'optionalAccess', _ => _.id]), () => ( null)),
1932
1932
  onChange: noop,
1933
- placeholder: _chunkGQJXOX5Scjs.PACKAGE_SEARCH_PLACEHOLDER
1933
+ placeholder: _chunkEGOCAIZJcjs.PACKAGE_SEARCH_PLACEHOLDER
1934
1934
  }
1935
1935
  );
1936
1936
  }
@@ -1944,7 +1944,7 @@ function SoftwareUpdateScreen({ compact = false }) {
1944
1944
  [fixture.devices, fixture.selectedIds]
1945
1945
  );
1946
1946
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full bg-ods-bg pt-[var(--spacing-system-l)]", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "h-full", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1947
- _chunkGQJXOX5Scjs.SoftwareActionForm,
1947
+ _chunkEGOCAIZJcjs.SoftwareActionForm,
1948
1948
  {
1949
1949
  action: fixture.action,
1950
1950
  showHeader: false,
@@ -1956,7 +1956,7 @@ function SoftwareUpdateScreen({ compact = false }) {
1956
1956
  initialValues: fixture.initialValues,
1957
1957
  renderPackageSearch,
1958
1958
  renderDevicePicker: () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1959
- _chunkGQJXOX5Scjs.DeviceSelector,
1959
+ _chunkEGOCAIZJcjs.DeviceSelector,
1960
1960
  {
1961
1961
  devices: selected,
1962
1962
  loading: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.842",
3
+ "version": "0.0.844",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+
3
+ import { UnreadNotificationsIndicator } from '../../navigation/unread-notifications-indicator';
4
+ import { useDevicesViewConfig } from './devices-view-config';
5
+ import type { DeviceRow } from './types';
6
+
7
+ /** The viewer's unread notifications about a device, opening them where the host lists them. Nothing at 0. */
8
+ export function DeviceUnreadIndicator({ device }: { device: DeviceRow }) {
9
+ const { getDeviceNotificationsHref } = useDevicesViewConfig();
10
+ const count = device.unreadNotificationCount ?? 0;
11
+ return (
12
+ <UnreadNotificationsIndicator count={count} href={count > 0 ? getDeviceNotificationsHref?.(device) : undefined} />
13
+ );
14
+ }
@@ -7,6 +7,7 @@ import { DeviceCard } from '../../ui';
7
7
  import { LoadingItems, useLoadingScope } from '../../ui/loading-scope';
8
8
  import { getDeviceName } from './device-name';
9
9
  import { getDeviceOperatingSystem, getDeviceStatusConfig } from './device-status';
10
+ import { DeviceUnreadIndicator } from './device-unread-indicator';
10
11
  import { useDevicesViewConfig } from './devices-view-config';
11
12
  import type { DeviceRow } from './types';
12
13
 
@@ -102,7 +103,14 @@ export function DevicesGrid<T extends DeviceRow = DeviceRow>({
102
103
  : undefined
103
104
  }
104
105
  onDeviceClick={() => handleDeviceClick(device)}
105
- actions={GRID_CARD_ACTIONS}
106
+ actions={
107
+ device.unreadNotificationCount
108
+ ? {
109
+ ...GRID_CARD_ACTIONS,
110
+ detailsButton: { component: <DeviceUnreadIndicator device={device} /> },
111
+ }
112
+ : GRID_CARD_ACTIONS
113
+ }
106
114
  className="h-full"
107
115
  // One card of a read list: how many there are is the record's (the height gate counts them).
108
116
  data-loading-item
@@ -27,6 +27,7 @@ import { getDeviceName } from './device-name';
27
27
  import { getDeviceStatusConfig } from './device-status';
28
28
  import { DEFAULT_VISIBLE_STATUSES } from './device-statuses';
29
29
  import { DeviceTypeTile } from './device-type-tile';
30
+ import { DeviceUnreadIndicator } from './device-unread-indicator';
30
31
  import { useDeviceImageUrl, useDevicesViewConfig } from './devices-view-config';
31
32
  import type { DeviceFilters, DeviceRow } from './types';
32
33
 
@@ -341,6 +342,8 @@ export function getDeviceTableColumns<T extends DeviceRow = DeviceRow>(
341
342
  <div className="min-w-0 flex-1">
342
343
  <TruncateText>{getDeviceName(device)}</TruncateText>
343
344
  </div>
345
+ {/* In the name's cell, not the open column: that one is dropped on a narrow page. */}
346
+ <DeviceUnreadIndicator device={device} />
344
347
  </div>
345
348
  );
346
349
  },
@@ -5,14 +5,16 @@ import type { DeviceRow } from './types';
5
5
 
6
6
  /**
7
7
  * What the device views cannot know by themselves, because it belongs to the
8
- * host: where a device's page lives, where images are served from, and whether
9
- * the page runs inside the mobile shell. The product provides it once, above
10
- * every page; without a provider the views render as a picture (no links,
11
- * image URLs used as given).
8
+ * host: where a device's page and its notifications live, where images are
9
+ * served from, and whether the page runs inside the mobile shell. The product
10
+ * provides it once, above every page; without a provider the views render as a
11
+ * picture (no links, image URLs used as given).
12
12
  */
13
13
  export interface DevicesViewConfig {
14
14
  /** The device's page. Absent: rows and cards are not links. */
15
15
  getDeviceHref?: (device: DeviceRow) => string | undefined;
16
+ /** The viewer's notifications about the device. Absent: the unread indicator is a counter, not a button. */
17
+ getDeviceNotificationsHref?: (device: DeviceRow) => string | undefined;
16
18
  /** A customer image's full URL from its stored path and content hash. */
17
19
  resolveImageUrl?: (imageUrl: string | null | undefined, hash?: string | null) => string | undefined;
18
20
  /** The page runs inside the mobile shell: options nobody can pick there are dropped. */
@@ -38,6 +38,7 @@ export {
38
38
  export { DeviceTagsFilterButton } from './device-tags-filter-button';
39
39
  export { renderDeviceTypeIcon } from './device-type-icon';
40
40
  export { DeviceTypeTile } from './device-type-tile';
41
+ export { DeviceUnreadIndicator } from './device-unread-indicator';
41
42
  export { DevicesFilterToolbar, type DevicesFilterToolbarProps } from './devices-filter-toolbar';
42
43
  export { DevicesGrid, type DevicesGridProps } from './devices-grid';
43
44
  export { DevicesGridFilters, type DevicesGridFiltersProps } from './devices-grid-filters';
@@ -34,6 +34,8 @@ export interface DeviceRow extends DeviceNameSource {
34
34
  organizationImageUrl?: string | null;
35
35
  organizationImageHash?: string | null;
36
36
  tags?: DeviceTag[];
37
+ /** The viewer's unread notifications about this device. Absent or 0: no indicator. */
38
+ unreadNotificationCount?: number;
37
39
  }
38
40
 
39
41
  export interface DeviceFilterValue {
@@ -244,22 +244,25 @@ export function SoftwareUpdateSettingsFields({ value, onChange }: SoftwareUpdate
244
244
  </div>
245
245
  </div>
246
246
 
247
- {/* Core Alert on the card surface, as a note: it is there from the start, nothing to announce. The icon sits in
248
- a span so Alert's `[&>svg]` positioning does not apply. */}
249
- <Alert
250
- role="note"
251
- className="flex items-center gap-[var(--spacing-system-m)] rounded-[6px] border-ods-border bg-ods-card p-[var(--spacing-system-m)]"
252
- >
253
- <span className="shrink-0 text-ods-text-secondary">
254
- <InfoCircleIcon size={24} color="currentColor" />
255
- </span>
256
- <div className="flex min-w-0 flex-1 flex-col gap-[var(--spacing-system-xxs)]">
257
- <p className="text-ods-text-primary text-h3">Apps installed by hand get reinstalled</p>
258
- <p className="text-ods-text-secondary text-h6">
259
- On the first update, OpenFrame reinstalls them with Winget or Brew so it can keep them updated.
260
- </p>
261
- </div>
262
- </Alert>
247
+ {/* Core Alert on the card surface, as a note: it comes with "Keep it auto-updated" and goes with it, since the
248
+ reinstall is what lets OpenFrame keep a hand-installed app updated, and a one-off update promises no more. The
249
+ icon sits in a span so Alert's `[&>svg]` positioning does not apply. */}
250
+ {value.autoUpdate && (
251
+ <Alert
252
+ role="note"
253
+ className="flex items-center gap-[var(--spacing-system-m)] rounded-[6px] border-ods-border bg-ods-card p-[var(--spacing-system-m)]"
254
+ >
255
+ <span className="shrink-0 text-ods-text-secondary">
256
+ <InfoCircleIcon size={24} color="currentColor" />
257
+ </span>
258
+ <div className="flex min-w-0 flex-1 flex-col gap-[var(--spacing-system-xxs)]">
259
+ <p className="text-ods-text-primary text-h3">Apps installed by hand get reinstalled</p>
260
+ <p className="text-ods-text-secondary text-h6">
261
+ On the first update, OpenFrame reinstalls them with Winget or Brew so it can keep them updated.
262
+ </p>
263
+ </div>
264
+ </Alert>
265
+ )}
263
266
  </section>
264
267
  );
265
268
  }
@@ -0,0 +1,25 @@
1
+ 'use client';
2
+
3
+ import { cn } from '../../utils/cn';
4
+
5
+ export interface CountPillProps {
6
+ count: number;
7
+ className?: string;
8
+ }
9
+
10
+ /**
11
+ * The navigation's unread count: a 24px accent pill, widening for "99+". The grouped sidebar ends its rows with it,
12
+ * and a list row that counts the same notifications draws the same pill, so the two read alike.
13
+ */
14
+ export function CountPill({ count, className }: CountPillProps) {
15
+ return (
16
+ <span
17
+ className={cn(
18
+ 'flex h-6 min-w-6 shrink-0 items-center justify-center rounded-md bg-ods-accent px-[var(--spacing-system-xxs)]',
19
+ className,
20
+ )}
21
+ >
22
+ <span className="text-ods-text-on-accent text-h5">{count > 99 ? '99+' : count}</span>
23
+ </span>
24
+ );
25
+ }
@@ -90,6 +90,10 @@ export { HeaderButton } from './header-button';
90
90
  export type { HeaderButtonProps } from './header-button';
91
91
  export { UnreadDot, UnreadCountBadge } from './unread-dot';
92
92
  export type { UnreadDotProps, UnreadCountBadgeProps } from './unread-dot';
93
+ export { CountPill } from './count-pill';
94
+ export type { CountPillProps } from './count-pill';
95
+ export { UnreadNotificationsIndicator, unreadNotificationsLabel } from './unread-notifications-indicator';
96
+ export type { UnreadNotificationsIndicatorProps } from './unread-notifications-indicator';
93
97
  export { NavigationItemBadge } from './navigation-item-badge';
94
98
  export type { NavigationItemBadgeProps } from './navigation-item-badge';
95
99
  export { TicketAlertsButton } from './ticket-alerts-button';
@@ -7,6 +7,7 @@ import type { NavigationSidebarItem } from '../../types/navigation';
7
7
  import { cn } from '../../utils';
8
8
  import { UnifiedSkeleton } from '../loading/unified-skeleton';
9
9
  import { FloatingTooltip } from '../ui/floating-tooltip';
10
+ import { CountPill } from './count-pill';
10
11
  import { NavigationItemBadge } from './navigation-item-badge';
11
12
  import { UnreadDot } from './unread-dot';
12
13
 
@@ -133,20 +134,6 @@ interface RowStateProps {
133
134
  disabled: boolean;
134
135
  }
135
136
 
136
- /** The count pill: 24px, widening for "99+". */
137
- function CountPill({ count, className }: { count: number; className?: string }) {
138
- return (
139
- <span
140
- className={cn(
141
- 'flex h-6 min-w-6 shrink-0 items-center justify-center rounded-md bg-ods-accent px-[var(--spacing-system-xxs)]',
142
- className,
143
- )}
144
- >
145
- <span className="text-ods-text-on-accent text-h5">{count > 99 ? '99+' : count}</span>
146
- </span>
147
- );
148
- }
149
-
150
137
  /** The row's end, flush right where every row's count sits. */
151
138
  function RowEnd({ count }: { count: number }) {
152
139
  return count > 0 ? <CountPill count={count} className="ml-auto" /> : null;
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import type { MouseEvent } from 'react';
4
+ import { useChatRuntime } from '../../contexts/chat-runtime-context';
5
+ import { cn } from '../../utils/cn';
6
+ import { isModifierClick } from '../chat/utils/chat-nav-resolution';
7
+ import { executeNavigationImperative, executeNewTabNavigation } from '../chat/utils/execute-navigation';
8
+ import { CountPill } from './count-pill';
9
+
10
+ /** What the indicator says to a screen reader: the count, in words. */
11
+ export function unreadNotificationsLabel(count: number): string {
12
+ return `${count} unread ${count === 1 ? 'notification' : 'notifications'}`;
13
+ }
14
+
15
+ export interface UnreadNotificationsIndicatorProps {
16
+ /** The viewer's unread notifications about one item. Nothing is drawn at 0. */
17
+ count: number;
18
+ /** Where those notifications are listed. Absent: the count alone, not a button. */
19
+ href?: string;
20
+ className?: string;
21
+ }
22
+
23
+ /**
24
+ * The viewer's unread notifications about one item of a list (a device, an incident), drawn with the sidebar's own
25
+ * count pill so a row and its section's badge read alike.
26
+ *
27
+ * A button, not a link: the row or card it sits in is usually a link itself, and a link cannot hold another
28
+ * (`data-no-row-click` keeps a table row from following its own). It goes through the lib's navigation executor,
29
+ * so the host decides how `href` opens, and a modifier click asks for a new tab as it would on a link.
30
+ */
31
+ export function UnreadNotificationsIndicator({ count, href, className }: UnreadNotificationsIndicatorProps) {
32
+ const runtime = useChatRuntime();
33
+ if (count <= 0) return null;
34
+
35
+ const label = unreadNotificationsLabel(count);
36
+
37
+ if (!href) {
38
+ return (
39
+ <span data-loading-optional role="img" aria-label={label} className={cn('inline-flex shrink-0', className)}>
40
+ <CountPill count={count} />
41
+ </span>
42
+ );
43
+ }
44
+
45
+ const open = (event: MouseEvent) => {
46
+ event.preventDefault();
47
+ event.stopPropagation();
48
+ if (isModifierClick(event)) executeNewTabNavigation({ runtime, href });
49
+ else executeNavigationImperative({ runtime, href });
50
+ };
51
+
52
+ return (
53
+ <button
54
+ type="button"
55
+ data-loading-optional
56
+ data-no-row-click
57
+ onClick={open}
58
+ aria-label={label}
59
+ title={label}
60
+ className={cn(
61
+ 'pointer-events-auto inline-flex shrink-0 rounded-md transition-opacity hover:opacity-80',
62
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus',
63
+ className,
64
+ )}
65
+ >
66
+ <CountPill count={count} />
67
+ </button>
68
+ );
69
+ }
@@ -4,6 +4,12 @@ import { type DEMO_DEVICES, DEMO_FLEET, DEMO_FLEET_SEARCH_OS, demoMinutesAgo } f
4
4
 
5
5
  type DemoDevice = (typeof DEMO_DEVICES)[keyof typeof DEMO_DEVICES];
6
6
 
7
+ /** The viewer's unread notifications per device: the offline reception desktop's recordings failed overnight. */
8
+ const DEMO_UNREAD_NOTIFICATIONS: Partial<Record<DemoDevice['id'], number>> = {
9
+ 'device-reception': 2,
10
+ 'device-leo': 1,
11
+ };
12
+
7
13
  function device(
8
14
  cast: DemoCast,
9
15
  demo: DemoDevice,
@@ -24,6 +30,7 @@ function device(
24
30
  organizationId: organization.id,
25
31
  organization: organization.name,
26
32
  organizationImageUrl: organization.logoUrl,
33
+ unreadNotificationCount: DEMO_UNREAD_NOTIFICATIONS[demo.id],
27
34
  };
28
35
  }
29
36