@flytedan/flytebot-design-system 0.48.0 → 0.48.1

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/dist/index.d.cts CHANGED
@@ -294,15 +294,22 @@ interface CardProps {
294
294
  * with no new colour. */
295
295
  draft?: boolean;
296
296
  /** Renders the card as a real `<a>` (keeps `hoverable`'s lift, adds real
297
- * hover/focus-visible/press states, MP-271). Takes precedence over `onClick`. */
297
+ * hover/focus-visible/press states, MP-271). Takes precedence over `onClick`.
298
+ * Review (comment 2920, finding 3): when either `href` or `onClick` is set, `action`
299
+ * and `footer` are rendered OUTSIDE the `<a>`/`<button>` (as ordinary siblings, via
300
+ * `.fd-card-shell`) rather than inside it — an interactive Card can never nest another
301
+ * interactive element, whatever `action`/`footer` contain. */
298
302
  href?: string;
299
303
  /** Renders the card as a real `<button type="button">` when there's no `href`
300
304
  * (MP-271) — never a `<div onClick>`, so it's reachable by Tab and activates on
301
305
  * both Enter and Space for free, and gets the kit's global `:focus-visible`
302
- * ring (styles/foundations.css) without this component drawing its own. */
306
+ * ring (styles/foundations.css) without this component drawing its own. Same
307
+ * `action`/`footer`-stay-outside rule as `href` above applies here too. */
303
308
  onClick?: (e: React.MouseEvent<HTMLElement>) => void;
304
- /** Passed through to the root element — CardGrid (MP-271) sets this to
305
- * "listitem" on every card it lays out, unless the caller already chose one. */
309
+ /** Passed through to the root element. A Card rendered inside CardGrid does NOT need this
310
+ * set — CardGrid (MP-271) wraps each item in its own `<div role="listitem">` rather than
311
+ * setting `role` on the card itself; set this only when Card is used outside CardGrid and
312
+ * needs a specific ARIA role of its own. */
306
313
  role?: string;
307
314
  tabIndex?: number;
308
315
  className?: string;
@@ -391,7 +398,10 @@ declare function ScreenHead({ kicker, title, lede, actions }: ScreenHeadProps):
391
398
  * other block box. `--card-grid-min` (default 280px) is the floor a card may shrink to before
392
399
  * a track drops; two 280px cards plus the gap don't fit in a 375px-wide content column, so
393
400
  * that width is always exactly one column, and a typical ~1280px desktop content column fits
394
- * four (see CardGrid.browser.test.tsx for the measured counts this rests on).
401
+ * four (see CardGrid.browser.test.tsx for the measured counts this rests on, including the
402
+ * container-vs-viewport regression case added under review comment 2920 finding 1: a fixed-
403
+ * width container nested inside a differently-sized real viewport still shows the container's
404
+ * own column count, which a `@media` breakpoint on `.fd-card-grid` could never do).
395
405
  *
396
406
  * Equal-height rows fall out of the grid's own default `align-items: stretch` (restated below
397
407
  * for clarity, not because the default would otherwise fail): every item in a row stretches to
@@ -403,12 +413,25 @@ declare function ScreenHead({ kicker, title, lede, actions }: ScreenHeadProps):
403
413
  * property `fd-gap-*` already sets on a flex container, so extending that vocabulary to grids
404
414
  * costs nothing new and keeps "the medium gap" meaning one token everywhere it's used.
405
415
  *
406
- * List semantics: the grid itself is `role="list"`, and each direct child is cloned with
407
- * `role="listitem"` added — the caller just renders `<CardGrid><Card .../><Card .../></CardGrid>`
408
- * and never has to remember the ARIA role itself. A child that already sets its own `role` is
409
- * left alone (cloning only fills in a role when the child didn't already choose one), so a
410
- * consumer with an unusual case (e.g. a child that should stay out of the list, such as an
411
- * inline empty-state message spanning every column) can opt out explicitly.
416
+ * List semantics (review comment 2920 finding 2): the grid itself is `role="list"`, and every
417
+ * item WRAPS in a plain `<div role="listitem">`, rather than cloning `role` onto the child's own
418
+ * props. Wrapping instead of cloning is deliberate: `React.cloneElement` on a child whose type is
419
+ * `React.Fragment` silently drops an added `role` prop (Fragments only accept `key`/`children`),
420
+ * so cloning left a Fragment child with NO listitem semantics at all and no warning either. A
421
+ * wrapper element needs no such prop-cloning heuristic, and it works identically no matter what
422
+ * the child is — an element, a Fragment, a string, a number — because it never inspects or
423
+ * touches the child's own props. Two behaviours fall out on top of "wrap by default":
424
+ * - Fragments are flattened first (recursively), so `<><Card/><Card/></>` as one CardGrid
425
+ * child becomes TWO listitems, not one listitem wrapping a Fragment.
426
+ * - A child that already sets its own `role` opts out of being a listitem at all (left
427
+ * unwrapped, exactly where React put it) — e.g. an inline empty-state message that should
428
+ * span every column rather than sit inside the list structure. This is the one case where
429
+ * "leave it alone" (the old cloning behaviour) is still correct, since wrapping it would
430
+ * just move the opt-out problem one level down.
431
+ * The wrapper needs a stylesheet counterpart (`.fd-card-grid > [role="listitem"]`, components.css)
432
+ * so the wrapper — now the actual grid item — passes the row's stretched height through to its
433
+ * one real child, the same way `.fd-card`'s own internal flex column already passes extra height
434
+ * down to `.fd-card-foot`.
412
435
  */
413
436
  interface CardGridProps {
414
437
  children?: React.ReactNode;
package/dist/index.d.ts CHANGED
@@ -294,15 +294,22 @@ interface CardProps {
294
294
  * with no new colour. */
295
295
  draft?: boolean;
296
296
  /** Renders the card as a real `<a>` (keeps `hoverable`'s lift, adds real
297
- * hover/focus-visible/press states, MP-271). Takes precedence over `onClick`. */
297
+ * hover/focus-visible/press states, MP-271). Takes precedence over `onClick`.
298
+ * Review (comment 2920, finding 3): when either `href` or `onClick` is set, `action`
299
+ * and `footer` are rendered OUTSIDE the `<a>`/`<button>` (as ordinary siblings, via
300
+ * `.fd-card-shell`) rather than inside it — an interactive Card can never nest another
301
+ * interactive element, whatever `action`/`footer` contain. */
298
302
  href?: string;
299
303
  /** Renders the card as a real `<button type="button">` when there's no `href`
300
304
  * (MP-271) — never a `<div onClick>`, so it's reachable by Tab and activates on
301
305
  * both Enter and Space for free, and gets the kit's global `:focus-visible`
302
- * ring (styles/foundations.css) without this component drawing its own. */
306
+ * ring (styles/foundations.css) without this component drawing its own. Same
307
+ * `action`/`footer`-stay-outside rule as `href` above applies here too. */
303
308
  onClick?: (e: React.MouseEvent<HTMLElement>) => void;
304
- /** Passed through to the root element — CardGrid (MP-271) sets this to
305
- * "listitem" on every card it lays out, unless the caller already chose one. */
309
+ /** Passed through to the root element. A Card rendered inside CardGrid does NOT need this
310
+ * set — CardGrid (MP-271) wraps each item in its own `<div role="listitem">` rather than
311
+ * setting `role` on the card itself; set this only when Card is used outside CardGrid and
312
+ * needs a specific ARIA role of its own. */
306
313
  role?: string;
307
314
  tabIndex?: number;
308
315
  className?: string;
@@ -391,7 +398,10 @@ declare function ScreenHead({ kicker, title, lede, actions }: ScreenHeadProps):
391
398
  * other block box. `--card-grid-min` (default 280px) is the floor a card may shrink to before
392
399
  * a track drops; two 280px cards plus the gap don't fit in a 375px-wide content column, so
393
400
  * that width is always exactly one column, and a typical ~1280px desktop content column fits
394
- * four (see CardGrid.browser.test.tsx for the measured counts this rests on).
401
+ * four (see CardGrid.browser.test.tsx for the measured counts this rests on, including the
402
+ * container-vs-viewport regression case added under review comment 2920 finding 1: a fixed-
403
+ * width container nested inside a differently-sized real viewport still shows the container's
404
+ * own column count, which a `@media` breakpoint on `.fd-card-grid` could never do).
395
405
  *
396
406
  * Equal-height rows fall out of the grid's own default `align-items: stretch` (restated below
397
407
  * for clarity, not because the default would otherwise fail): every item in a row stretches to
@@ -403,12 +413,25 @@ declare function ScreenHead({ kicker, title, lede, actions }: ScreenHeadProps):
403
413
  * property `fd-gap-*` already sets on a flex container, so extending that vocabulary to grids
404
414
  * costs nothing new and keeps "the medium gap" meaning one token everywhere it's used.
405
415
  *
406
- * List semantics: the grid itself is `role="list"`, and each direct child is cloned with
407
- * `role="listitem"` added — the caller just renders `<CardGrid><Card .../><Card .../></CardGrid>`
408
- * and never has to remember the ARIA role itself. A child that already sets its own `role` is
409
- * left alone (cloning only fills in a role when the child didn't already choose one), so a
410
- * consumer with an unusual case (e.g. a child that should stay out of the list, such as an
411
- * inline empty-state message spanning every column) can opt out explicitly.
416
+ * List semantics (review comment 2920 finding 2): the grid itself is `role="list"`, and every
417
+ * item WRAPS in a plain `<div role="listitem">`, rather than cloning `role` onto the child's own
418
+ * props. Wrapping instead of cloning is deliberate: `React.cloneElement` on a child whose type is
419
+ * `React.Fragment` silently drops an added `role` prop (Fragments only accept `key`/`children`),
420
+ * so cloning left a Fragment child with NO listitem semantics at all and no warning either. A
421
+ * wrapper element needs no such prop-cloning heuristic, and it works identically no matter what
422
+ * the child is — an element, a Fragment, a string, a number — because it never inspects or
423
+ * touches the child's own props. Two behaviours fall out on top of "wrap by default":
424
+ * - Fragments are flattened first (recursively), so `<><Card/><Card/></>` as one CardGrid
425
+ * child becomes TWO listitems, not one listitem wrapping a Fragment.
426
+ * - A child that already sets its own `role` opts out of being a listitem at all (left
427
+ * unwrapped, exactly where React put it) — e.g. an inline empty-state message that should
428
+ * span every column rather than sit inside the list structure. This is the one case where
429
+ * "leave it alone" (the old cloning behaviour) is still correct, since wrapping it would
430
+ * just move the opt-out problem one level down.
431
+ * The wrapper needs a stylesheet counterpart (`.fd-card-grid > [role="listitem"]`, components.css)
432
+ * so the wrapper — now the actual grid item — passes the row's stretched height through to its
433
+ * one real child, the same way `.fd-card`'s own internal flex column already passes extra height
434
+ * down to `.fd-card-foot`.
412
435
  */
413
436
  interface CardGridProps {
414
437
  children?: React.ReactNode;
package/dist/index.js CHANGED
@@ -851,28 +851,28 @@ function Card({
851
851
  interactive ? "fd-card-interactive" : "",
852
852
  className
853
853
  ].filter(Boolean).join(" ");
854
- const head = title ? /* @__PURE__ */ jsxs4("div", { className: "fd-card-head", children: [
854
+ const titleOnlyHead = title ? /* @__PURE__ */ jsxs4("div", { className: "fd-card-head", children: [
855
855
  /* @__PURE__ */ jsx7("span", { className: "fd-card-head-title", title: truncationTitle(title), children: title }),
856
- action
856
+ !interactive && action
857
857
  ] }) : null;
858
858
  const body = padded ? /* @__PURE__ */ jsx7("div", { className: "fd-card-body", children }) : children;
859
859
  const foot = footer ? /* @__PURE__ */ jsx7("div", { className: "fd-card-foot", children: footer }) : null;
860
- if (href != null) {
861
- return /* @__PURE__ */ jsxs4("a", { href, onClick, className: cls, role, tabIndex, ...rest, children: [
862
- head,
863
- body,
864
- foot
860
+ if (interactive) {
861
+ const inner = href != null ? /* @__PURE__ */ jsxs4("a", { href, onClick, className: cls, role, tabIndex, ...rest, children: [
862
+ titleOnlyHead,
863
+ body
864
+ ] }) : /* @__PURE__ */ jsxs4("button", { type: "button", onClick, className: cls, role, tabIndex, ...rest, children: [
865
+ titleOnlyHead,
866
+ body
865
867
  ] });
866
- }
867
- if (onClick != null) {
868
- return /* @__PURE__ */ jsxs4("button", { type: "button", onClick, className: cls, role, tabIndex, ...rest, children: [
869
- head,
870
- body,
868
+ return /* @__PURE__ */ jsxs4("div", { className: "fd-card-shell", children: [
869
+ inner,
870
+ action ? /* @__PURE__ */ jsx7("div", { className: "fd-card-shell-action", children: action }) : null,
871
871
  foot
872
872
  ] });
873
873
  }
874
874
  return /* @__PURE__ */ jsxs4("div", { className: cls, role, tabIndex, ...rest, children: [
875
- head,
875
+ titleOnlyHead,
876
876
  body,
877
877
  foot
878
878
  ] });
@@ -1046,8 +1046,21 @@ function ScreenHead({ kicker, title, lede, actions }) {
1046
1046
  // src/components/layout/CardGrid.tsx
1047
1047
  import * as React8 from "react";
1048
1048
  import { jsx as jsx12 } from "react/jsx-runtime";
1049
+ function flattenFragments(children) {
1050
+ const out = [];
1051
+ React8.Children.forEach(children, (child) => {
1052
+ if (child === null || child === void 0 || typeof child === "boolean") return;
1053
+ if (React8.isValidElement(child) && child.type === React8.Fragment) {
1054
+ const fragmentChildren = child.props.children;
1055
+ out.push(...flattenFragments(fragmentChildren));
1056
+ return;
1057
+ }
1058
+ out.push(child);
1059
+ });
1060
+ return out;
1061
+ }
1049
1062
  function CardGrid({ children, minCardWidth = 280, gap = "lg", className = "", style }) {
1050
- const items = React8.Children.toArray(children);
1063
+ const items = flattenFragments(children);
1051
1064
  return /* @__PURE__ */ jsx12(
1052
1065
  "div",
1053
1066
  {
@@ -1055,10 +1068,12 @@ function CardGrid({ children, minCardWidth = 280, gap = "lg", className = "", st
1055
1068
  className: ["fd-card-grid", "fd-gap-" + gap, className].filter(Boolean).join(" "),
1056
1069
  style: { ["--card-grid-min"]: minCardWidth + "px", ...style },
1057
1070
  children: items.map((child, i) => {
1058
- if (!React8.isValidElement(child)) return child;
1059
- const existingProps = child.props;
1060
- if (existingProps.role) return child;
1061
- return React8.cloneElement(child, { role: "listitem", key: child.key ?? i });
1071
+ if (React8.isValidElement(child)) {
1072
+ const existingRole = child.props.role;
1073
+ if (existingRole) return child;
1074
+ return /* @__PURE__ */ jsx12("div", { role: "listitem", children: child }, child.key ?? i);
1075
+ }
1076
+ return /* @__PURE__ */ jsx12("div", { role: "listitem", children: child }, i);
1062
1077
  })
1063
1078
  }
1064
1079
  );
@@ -1330,7 +1345,7 @@ var SidebarNav = React11.forwardRef(function SidebarNav2({ brand, groups = [], c
1330
1345
 
1331
1346
  // src/components/navigation/Topbar.tsx
1332
1347
  import * as React12 from "react";
1333
- import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1348
+ import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1334
1349
  function useIsNarrow(breakpoint) {
1335
1350
  const [narrow, setNarrow] = React12.useState(
1336
1351
  () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
@@ -1379,7 +1394,7 @@ function Topbar({
1379
1394
  }
1380
1395
  )
1381
1396
  ] }, i)) }),
1382
- overflowActions != null ? narrow ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
1397
+ overflowActions != null ? narrow ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
1383
1398
  /* @__PURE__ */ jsx20(
1384
1399
  "button",
1385
1400
  {
@@ -1689,12 +1704,12 @@ function EmptyState({ icon = "tray", title, children, action, className = "", ..
1689
1704
  }
1690
1705
 
1691
1706
  // src/components/feedback/Flag.tsx
1692
- import { Fragment as Fragment4, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
1707
+ import { Fragment as Fragment5, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
1693
1708
  function Flag({ statement, cost, actions, tone = "warning", className = "", ...rest }) {
1694
1709
  return /* @__PURE__ */ jsxs22("div", { className: ["fd-flag", "fd-flag-" + tone, className].filter(Boolean).join(" "), role: "status", ...rest, children: [
1695
1710
  /* @__PURE__ */ jsxs22("span", { className: "fd-flag-text", children: [
1696
1711
  statement,
1697
- cost ? /* @__PURE__ */ jsxs22(Fragment4, { children: [
1712
+ cost ? /* @__PURE__ */ jsxs22(Fragment5, { children: [
1698
1713
  " ",
1699
1714
  /* @__PURE__ */ jsx28("span", { className: "fd-flag-cost", children: cost })
1700
1715
  ] }) : null
@@ -1921,7 +1936,7 @@ function Tooltip({ label, placement = "top", children, className = "" }) {
1921
1936
 
1922
1937
  // src/components/data/AnimNum.tsx
1923
1938
  import * as React15 from "react";
1924
- import { Fragment as Fragment5, jsx as jsx33 } from "react/jsx-runtime";
1939
+ import { Fragment as Fragment6, jsx as jsx33 } from "react/jsx-runtime";
1925
1940
  function shouldSkipEasing() {
1926
1941
  if (typeof document !== "undefined" && document.hidden) return true;
1927
1942
  return window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
@@ -1953,7 +1968,7 @@ function AnimNum({ value, format, duration = 550 }) {
1953
1968
  setDisplay(to);
1954
1969
  };
1955
1970
  }, [value, duration]);
1956
- return /* @__PURE__ */ jsx33(Fragment5, { children: format ? format(display) : Math.round(display).toLocaleString() });
1971
+ return /* @__PURE__ */ jsx33(Fragment6, { children: format ? format(display) : Math.round(display).toLocaleString() });
1957
1972
  }
1958
1973
 
1959
1974
  // src/components/data/TrendChart.tsx
@@ -4436,7 +4451,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
4436
4451
 
4437
4452
  // src/components/data/CodeBlock.tsx
4438
4453
  import * as React27 from "react";
4439
- import { Fragment as Fragment6, jsx as jsx49, jsxs as jsxs41 } from "react/jsx-runtime";
4454
+ import { Fragment as Fragment7, jsx as jsx49, jsxs as jsxs41 } from "react/jsx-runtime";
4440
4455
  var JS_KW = /^(?:const|let|var|function|return|if|else|for|while|do|new|class|extends|import|from|export|default|await|async|try|catch|finally|throw|typeof|instanceof|of|in|switch|case|break|continue|delete|void|yield|static|get|set|this|super|interface|type|enum|implements|public|private|protected|readonly|abstract|as|satisfies|declare|namespace)$/;
4441
4456
  var RULES = {
4442
4457
  js: [
@@ -4592,7 +4607,7 @@ function tokenize(src, lang) {
4592
4607
  }
4593
4608
  function Highlighted({ code, lang }) {
4594
4609
  const toks = React27.useMemo(() => tokenize(code, lang), [code, lang]);
4595
- return /* @__PURE__ */ jsx49(Fragment6, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx49("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
4610
+ return /* @__PURE__ */ jsx49(Fragment7, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx49("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
4596
4611
  }
4597
4612
  function useCopy() {
4598
4613
  const [done, setDone] = React27.useState(false);
@@ -4956,7 +4971,7 @@ function Meter({ segments = [], unallocated = 0, height = 8, label, legend = fal
4956
4971
  }
4957
4972
 
4958
4973
  // src/components/data/Pagination.tsx
4959
- import { Fragment as Fragment8, jsx as jsx52, jsxs as jsxs44 } from "react/jsx-runtime";
4974
+ import { Fragment as Fragment9, jsx as jsx52, jsxs as jsxs44 } from "react/jsx-runtime";
4960
4975
  var unitPluralRules = new Intl.PluralRules("en-US");
4961
4976
  function pluralizeUnit(n, singular, irregularPlural) {
4962
4977
  return unitPluralRules.select(n) === "one" ? singular : irregularPlural ?? `${singular}s`;
@@ -4988,10 +5003,10 @@ function Pagination({
4988
5003
  else if (nums[nums.length - 1] !== "\u2026") nums.push("\u2026");
4989
5004
  }
4990
5005
  return /* @__PURE__ */ jsxs44("div", { className: ["fd-pag", className].filter(Boolean).join(" "), ...rest, children: [
4991
- /* @__PURE__ */ jsx52("span", { className: "fd-pag-count", children: loading ? /* @__PURE__ */ jsx52("span", { className: "fd-skel fd-skel-text", style: { display: "inline-block", height: 12, width: 108 } }) : total === 0 ? /* @__PURE__ */ jsxs44(Fragment8, { children: [
5006
+ /* @__PURE__ */ jsx52("span", { className: "fd-pag-count", children: loading ? /* @__PURE__ */ jsx52("span", { className: "fd-skel fd-skel-text", style: { display: "inline-block", height: 12, width: 108 } }) : total === 0 ? /* @__PURE__ */ jsxs44(Fragment9, { children: [
4992
5007
  "No ",
4993
5008
  unitLabel
4994
- ] }) : /* @__PURE__ */ jsxs44(Fragment8, { children: [
5009
+ ] }) : /* @__PURE__ */ jsxs44(Fragment9, { children: [
4995
5010
  /* @__PURE__ */ jsxs44("strong", { children: [
4996
5011
  from.toLocaleString(),
4997
5012
  "\u2013",
@@ -7256,7 +7271,7 @@ function useSessionContext() {
7256
7271
  }
7257
7272
 
7258
7273
  // src/components/platform/AccountMenu.tsx
7259
- import { Fragment as Fragment15, jsx as jsx72, jsxs as jsxs63 } from "react/jsx-runtime";
7274
+ import { Fragment as Fragment16, jsx as jsx72, jsxs as jsxs63 } from "react/jsx-runtime";
7260
7275
  var DEFAULT_LINKS = [];
7261
7276
  var DEFAULT_GROUPS = [];
7262
7277
  function Item({ item, onPick }) {
@@ -7318,7 +7333,7 @@ function AccountMenu({
7318
7333
  }
7319
7334
  };
7320
7335
  const showSwitcher = allowUserSwitch && !!demo;
7321
- return /* @__PURE__ */ jsxs63(Fragment15, { children: [
7336
+ return /* @__PURE__ */ jsxs63(Fragment16, { children: [
7322
7337
  /* @__PURE__ */ jsxs63(
7323
7338
  "button",
7324
7339
  {
@@ -8076,7 +8091,7 @@ function ProfilePage({
8076
8091
 
8077
8092
  // src/components/platform/SignInPage.tsx
8078
8093
  import * as React50 from "react";
8079
- import { Fragment as Fragment18, jsx as jsx77, jsxs as jsxs68 } from "react/jsx-runtime";
8094
+ import { Fragment as Fragment19, jsx as jsx77, jsxs as jsxs68 } from "react/jsx-runtime";
8080
8095
  function SignInPage({ productName, onSignIn, continueAs, error }) {
8081
8096
  const [signingIn, setSigningIn] = React50.useState(false);
8082
8097
  const [resuming, setResuming] = React50.useState(false);
@@ -8164,7 +8179,7 @@ function SignInPage({ productName, onSignIn, continueAs, error }) {
8164
8179
  /* @__PURE__ */ jsx77("p", { className: "fd-body-sm fd-muted", style: { marginTop: "var(--s-4)" }, children: continueAs ? "Your session timed out, but you're likely still signed in" : "Sign in with your flytedesk workspace account" })
8165
8180
  ] }),
8166
8181
  error ? /* @__PURE__ */ jsx77("div", { role: "alert", style: { width: "100%" }, children: /* @__PURE__ */ jsx77(Banner, { tone: "danger", title: error }) }) : null,
8167
- continueAs ? /* @__PURE__ */ jsxs68(Fragment18, { children: [
8182
+ continueAs ? /* @__PURE__ */ jsxs68(Fragment19, { children: [
8168
8183
  /* @__PURE__ */ jsx77(
8169
8184
  Button,
8170
8185
  {
@@ -10281,7 +10296,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
10281
10296
  // src/components/platform/ComingSoon.tsx
10282
10297
  import * as React54 from "react";
10283
10298
  import { createPortal as createPortal5 } from "react-dom";
10284
- import { Fragment as Fragment20, jsx as jsx79, jsxs as jsxs70 } from "react/jsx-runtime";
10299
+ import { Fragment as Fragment21, jsx as jsx79, jsxs as jsxs70 } from "react/jsx-runtime";
10285
10300
  var BYPASS_STORE = "fd.soon.bypass.v1";
10286
10301
  function readBypassed() {
10287
10302
  try {
@@ -10311,7 +10326,7 @@ function useBypass(key) {
10311
10326
  return [on, set];
10312
10327
  }
10313
10328
  function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass }) {
10314
- return /* @__PURE__ */ jsxs70(Fragment20, { children: [
10329
+ return /* @__PURE__ */ jsxs70(Fragment21, { children: [
10315
10330
  /* @__PURE__ */ jsxs70("span", { className: "fd-soon-badge", children: [
10316
10331
  /* @__PURE__ */ jsx79("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
10317
10332
  label
@@ -10698,7 +10713,7 @@ function PermissionHint({ perm, children }) {
10698
10713
  }
10699
10714
 
10700
10715
  // src/components/platform/ModeSwitch.tsx
10701
- import { Fragment as Fragment21, jsx as jsx81, jsxs as jsxs72 } from "react/jsx-runtime";
10716
+ import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs72 } from "react/jsx-runtime";
10702
10717
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
10703
10718
  const mode2 = useRuntimeMode();
10704
10719
  const panel = useInlinePanel();
@@ -10753,7 +10768,7 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
10753
10768
  s.total,
10754
10769
  " features complete"
10755
10770
  ] }),
10756
- onOpenSpec ? /* @__PURE__ */ jsxs72(Fragment21, { children: [
10771
+ onOpenSpec ? /* @__PURE__ */ jsxs72(Fragment22, { children: [
10757
10772
  /* @__PURE__ */ jsx81("span", { style: { flex: 1 } }),
10758
10773
  /* @__PURE__ */ jsxs72("button", { type: "button", className: "fd-soon-link", onClick: () => {
10759
10774
  setOpen(false);
@@ -10945,7 +10960,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
10945
10960
 
10946
10961
  // src/components/planner/MixGap.tsx
10947
10962
  import * as React55 from "react";
10948
- import { Fragment as Fragment22, jsx as jsx84, jsxs as jsxs75 } from "react/jsx-runtime";
10963
+ import { Fragment as Fragment23, jsx as jsx84, jsxs as jsxs75 } from "react/jsx-runtime";
10949
10964
  var MIX_GAP_LAYOUT = {
10950
10965
  trackHeight: 30,
10951
10966
  barHeight: 20,
@@ -10989,7 +11004,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
10989
11004
  onMouseLeave: () => setHover(null),
10990
11005
  children: [
10991
11006
  /* @__PURE__ */ jsx84("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ jsx84(ChannelTag, { channel: r.channel, size: "sm" }) }),
10992
- /* @__PURE__ */ jsx84("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx84("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs75(Fragment22, { children: [
11007
+ /* @__PURE__ */ jsx84("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx84("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs75(Fragment23, { children: [
10993
11008
  /* @__PURE__ */ jsx84("span", { "data-mixgap-bar": "", style: { position: "absolute", left: 0, top: barTop, height: barHeight, width: mixGapOffset(r.realized, max), background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
10994
11009
  /* @__PURE__ */ jsx84("span", { "data-mixgap-target": "", style: { position: "absolute", left: `calc(${mixGapOffset(r.target, max)} - ${targetWidth / 2}px)`, top: 0, width: targetWidth, height: trackHeight, background: MIX_GAP_TARGET_STRIPE, borderRadius: 1 } }),
10995
11010
  /* @__PURE__ */ jsxs75("span", { "data-mixgap-label": "", className: "fd-num", style: { position: "absolute", left: labelLeft, top: "50%", transform: "translateY(-50%)", fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap" }, children: [
@@ -11103,7 +11118,7 @@ function ChannelContribution({
11103
11118
 
11104
11119
  // src/components/planner/BudgetReallocator.tsx
11105
11120
  import * as React56 from "react";
11106
- import { Fragment as Fragment23, jsx as jsx86, jsxs as jsxs77 } from "react/jsx-runtime";
11121
+ import { Fragment as Fragment24, jsx as jsx86, jsxs as jsxs77 } from "react/jsx-runtime";
11107
11122
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
11108
11123
  function BudgetReallocator({
11109
11124
  campus,
@@ -11167,7 +11182,7 @@ function BudgetReallocator({
11167
11182
  /* @__PURE__ */ jsxs77("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
11168
11183
  /* @__PURE__ */ jsx86("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
11169
11184
  /* @__PURE__ */ jsx86(CsiBadge, { band: nowBand, crp, size: "medium" }),
11170
- dirty ? /* @__PURE__ */ jsxs77(Fragment23, { children: [
11185
+ dirty ? /* @__PURE__ */ jsxs77(Fragment24, { children: [
11171
11186
  /* @__PURE__ */ jsx86("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
11172
11187
  /* @__PURE__ */ jsx86(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
11173
11188
  ] }) : null
@@ -13122,7 +13137,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
13122
13137
 
13123
13138
  // src/components/files/FileBrowser.tsx
13124
13139
  import * as React65 from "react";
13125
- import { Fragment as Fragment27, jsx as jsx96, jsxs as jsxs87 } from "react/jsx-runtime";
13140
+ import { Fragment as Fragment28, jsx as jsx96, jsxs as jsxs87 } from "react/jsx-runtime";
13126
13141
  function childrenOf(folders, parentId) {
13127
13142
  return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
13128
13143
  }
@@ -13421,7 +13436,7 @@ function FileBrowser({
13421
13436
  ] }),
13422
13437
  /* @__PURE__ */ jsxs87("span", { className: "fd-fb-actions", children: [
13423
13438
  canCreateFolder ? /* @__PURE__ */ jsx96(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
13424
- canUpload ? /* @__PURE__ */ jsxs87(Fragment27, { children: [
13439
+ canUpload ? /* @__PURE__ */ jsxs87(Fragment28, { children: [
13425
13440
  /* @__PURE__ */ jsx96(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
13426
13441
  /* @__PURE__ */ jsx96(
13427
13442
  "input",
@@ -13488,7 +13503,7 @@ function FileBrowser({
13488
13503
  draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
13489
13504
  isDropTarget: canMove,
13490
13505
  onDropPayload: (p) => moveIntoFolder(p, f.id),
13491
- actions: /* @__PURE__ */ jsxs87(Fragment27, { children: [
13506
+ actions: /* @__PURE__ */ jsxs87(Fragment28, { children: [
13492
13507
  canRenameFolder ? /* @__PURE__ */ jsx96(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
13493
13508
  canMove ? /* @__PURE__ */ jsx96(
13494
13509
  FolderPickerMenu,
@@ -13511,7 +13526,7 @@ function FileBrowser({
13511
13526
  meta: file.size ? formatBytes(file.size) : void 0,
13512
13527
  onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
13513
13528
  draggableId: canMove ? { kind: "file", id: file.id } : void 0,
13514
- actions: /* @__PURE__ */ jsxs87(Fragment27, { children: [
13529
+ actions: /* @__PURE__ */ jsxs87(Fragment28, { children: [
13515
13530
  canMove ? /* @__PURE__ */ jsx96(
13516
13531
  FolderPickerMenu,
13517
13532
  {
@@ -13529,7 +13544,7 @@ function FileBrowser({
13529
13544
  }
13530
13545
  ) })
13531
13546
  ] }),
13532
- /* @__PURE__ */ jsx96(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ jsxs87(Fragment27, { children: [
13547
+ /* @__PURE__ */ jsx96(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ jsxs87(Fragment28, { children: [
13533
13548
  /* @__PURE__ */ jsx96(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
13534
13549
  /* @__PURE__ */ jsx96(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
13535
13550
  ] }), children: /* @__PURE__ */ jsx96(
@@ -13546,7 +13561,7 @@ function FileBrowser({
13546
13561
  autoFocus: true
13547
13562
  }
13548
13563
  ) }),
13549
- /* @__PURE__ */ jsx96(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ jsxs87(Fragment27, { children: [
13564
+ /* @__PURE__ */ jsx96(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ jsxs87(Fragment28, { children: [
13550
13565
  /* @__PURE__ */ jsx96(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
13551
13566
  /* @__PURE__ */ jsx96(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
13552
13567
  ] }), children: /* @__PURE__ */ jsx96(
@@ -13570,7 +13585,7 @@ function FileBrowser({
13570
13585
  title: deleteTarget ? "Delete \u201C" + deleteTarget.name + "\u201D?" : "Delete folder?",
13571
13586
  description: "This can't be undone. The folder must be empty.",
13572
13587
  onClose: () => setDeleteTarget(null),
13573
- footer: /* @__PURE__ */ jsxs87(Fragment27, { children: [
13588
+ footer: /* @__PURE__ */ jsxs87(Fragment28, { children: [
13574
13589
  /* @__PURE__ */ jsx96(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
13575
13590
  /* @__PURE__ */ jsx96(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
13576
13591
  ] })