@pstrikez/fluent-winui-theme 0.1.1 → 0.2.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/dist/index.js CHANGED
@@ -1485,7 +1485,7 @@ var require_prism = __commonJS({
1485
1485
  import * as fluentNamespace from "@fluentui/react-components";
1486
1486
 
1487
1487
  // src/winui/appearance.ts
1488
- import { CheckmarkCircleFilled, ChevronDown12Regular, DismissCircleFilled, ErrorCircleFilled, InfoFilled } from "@fluentui/react-icons";
1488
+ import { CheckmarkCircleFilled, ChevronDown12Regular, ChevronUp12Regular, DismissCircleFilled, ErrorCircleFilled, InfoFilled } from "@fluentui/react-icons";
1489
1489
  import * as React2 from "react";
1490
1490
 
1491
1491
  // src/winui/wrap.ts
@@ -1562,6 +1562,16 @@ var withWinuiAppearance = (components) => {
1562
1562
  ...props,
1563
1563
  expandIcon: props.expandIcon === void 0 ? { children: React2.createElement(ChevronDown12Regular) } : props.expandIcon
1564
1564
  }));
1565
+ const withSpinGlyph = (slot, Glyph) => {
1566
+ if (slot === null) return slot;
1567
+ const slotProps2 = resolveSlotProps(slot);
1568
+ return slotProps2?.children === void 0 ? { ...slotProps2, children: React2.createElement(Glyph) } : slotProps2;
1569
+ };
1570
+ const winuiSpinGlyphs = (component) => wrapFluent(component, (props) => ({
1571
+ ...props,
1572
+ incrementButton: withSpinGlyph(props.incrementButton, ChevronUp12Regular),
1573
+ decrementButton: withSpinGlyph(props.decrementButton, ChevronDown12Regular)
1574
+ }));
1565
1575
  return {
1566
1576
  ...components,
1567
1577
  AccordionHeader: winuiChevron(components.AccordionHeader),
@@ -1578,6 +1588,7 @@ var withWinuiAppearance = (components) => {
1578
1588
  Input: stamp(components.Input, appearance("outline", rootAndPrimary)),
1579
1589
  Textarea: stamp(components.Textarea, appearance("outline", rootAndPrimary)),
1580
1590
  Select: stamp(components.Select, appearance("outline", rootAndPrimary)),
1591
+ SpinButton: winuiSpinGlyphs(stamp(components.SpinButton, appearance("outline", rootAndPrimary))),
1581
1592
  Combobox: stamp(components.Combobox, appearance("outline", rootAndPrimary)),
1582
1593
  Dropdown: stamp(components.Dropdown, appearance("outline", rootAndPrimary)),
1583
1594
  Card: stamp(components.Card, appearance("filled", rootIsPrimary)),
@@ -1743,11 +1754,11 @@ var withWinuiDrag = (components) => {
1743
1754
  const suppressClickRef = React4.useRef(false);
1744
1755
  const settleTimerRef = React4.useRef(void 0);
1745
1756
  React4.useEffect(() => () => clearTimeout(settleTimerRef.current), []);
1746
- const end = (gesture, toggle, fromDrag) => {
1757
+ const end = (gesture, toggle2, fromDrag) => {
1747
1758
  gestureRef.current = null;
1748
1759
  gesture.root.removeAttribute(DRAGGING);
1749
1760
  gesture.root.style.removeProperty(DRAG_X);
1750
- if (toggle) {
1761
+ if (toggle2) {
1751
1762
  if (fromDrag) {
1752
1763
  gesture.root.setAttribute(SETTLING, "");
1753
1764
  clearTimeout(settleTimerRef.current);
@@ -2704,29 +2715,15 @@ var readableTone = (hex, surface) => {
2704
2715
  };
2705
2716
 
2706
2717
  // src/components/ui/badge-hue.ts
2707
- var { makeStyles: makeStyles2 } = fluentComponents;
2708
2718
  var HARDEST_BADGE_SURFACE = { light: "#EBF3FC", dark: "#434343" };
2709
2719
  var BADGE_FILL_ALPHA = 0.1;
2710
2720
  var BADGE_STROKE_ALPHA = 0.35;
2711
- var useStyles2 = makeStyles2({
2712
- scheme: {
2713
- "--fwt-badge-fill": "var(--fwt-badge-fill-light)",
2714
- "--fwt-chip-stroke": "var(--fwt-badge-stroke-light)",
2715
- "--fwt-badge-label": "var(--fwt-badge-label-light)",
2716
- "@media (prefers-color-scheme: dark)": {
2717
- "--fwt-badge-fill": "var(--fwt-badge-fill-dark)",
2718
- "--fwt-chip-stroke": "var(--fwt-badge-stroke-dark)",
2719
- "--fwt-badge-label": "var(--fwt-badge-label-dark)"
2720
- }
2721
- }
2722
- });
2723
2721
  var useBadgeHue = (hue) => {
2724
- const styles = useStyles2();
2725
2722
  const pair = typeof hue === "string" ? { light: hue, dark: hue } : hue;
2726
2723
  const fill = { light: alphaColor(pair.light, BADGE_FILL_ALPHA), dark: alphaColor(pair.dark, BADGE_FILL_ALPHA) };
2727
2724
  const label = (own, surface) => readableTone(own, blendHex(own, BADGE_FILL_ALPHA, surface));
2728
2725
  return {
2729
- className: styles.scheme,
2726
+ className: "fwt-badge-hue",
2730
2727
  style: {
2731
2728
  "--fwt-badge-fill-light": fill.light,
2732
2729
  "--fwt-badge-fill-dark": fill.dark,
@@ -2743,8 +2740,8 @@ var useBadgeHue = (hue) => {
2743
2740
 
2744
2741
  // src/components/ui/chip.tsx
2745
2742
  import { jsx as jsx6 } from "react/jsx-runtime";
2746
- var { Tag, makeStyles: makeStyles3, mergeClasses } = fluentComponents;
2747
- var useStyles3 = makeStyles3({
2743
+ var { Tag, makeStyles: makeStyles2, mergeClasses } = fluentComponents;
2744
+ var useStyles2 = makeStyles2({
2748
2745
  // Fluent sizes the chip to its content, so a long label carries the chip
2749
2746
  // past the column that holds it.
2750
2747
  root: { maxWidth: "100%" },
@@ -2762,7 +2759,7 @@ var useStyles3 = makeStyles3({
2762
2759
  }
2763
2760
  });
2764
2761
  function Chip({ children, className, icon, style, textRef, ...trigger }) {
2765
- const styles = useStyles3();
2762
+ const styles = useStyles2();
2766
2763
  return /* @__PURE__ */ jsx6(
2767
2764
  Tag,
2768
2765
  {
@@ -5567,7 +5564,30 @@ var en = {
5567
5564
  "common.discard.discard": "Discard",
5568
5565
  "common.copy.action": "Copy",
5569
5566
  "common.copy.copied": "Copied",
5570
- "common.copy.failed": "Copy failed"
5567
+ "common.copy.failed": "Copy failed",
5568
+ "numberBox.increment": "Increase value",
5569
+ "numberBox.decrement": "Decrease value",
5570
+ "fileDropZone.browse": "Browse files",
5571
+ "fileDropZone.dropHint": "Drop files here",
5572
+ "fileDropZone.remove": "Remove file",
5573
+ "pager.label": "Pagination",
5574
+ "pager.first": "First page",
5575
+ "pager.previous": "Previous page",
5576
+ "pager.next": "Next page",
5577
+ "pager.last": "Last page",
5578
+ "pager.page": "Page {n}",
5579
+ "pager.pageSize": "Items per page",
5580
+ "pager.perPage": "{n} / page",
5581
+ "pager.total": "Total {n} items",
5582
+ "dataTable.empty": "No data",
5583
+ "dataTable.loading": "Loading",
5584
+ "dataTable.selectAll": "Select all rows",
5585
+ "dataTable.selectRow": "Select row",
5586
+ "dataTable.expandRow": "Expand row",
5587
+ "dataTable.collapseRow": "Collapse row",
5588
+ "tabView.close": "Close tab",
5589
+ "tabView.scrollBackward": "Scroll tabs left",
5590
+ "tabView.scrollForward": "Scroll tabs right"
5571
5591
  };
5572
5592
  var zhHans = {
5573
5593
  "common.on": "\u5F00",
@@ -5582,7 +5602,30 @@ var zhHans = {
5582
5602
  "common.discard.discard": "\u653E\u5F03",
5583
5603
  "common.copy.action": "\u590D\u5236",
5584
5604
  "common.copy.copied": "\u5DF2\u590D\u5236",
5585
- "common.copy.failed": "\u590D\u5236\u5931\u8D25"
5605
+ "common.copy.failed": "\u590D\u5236\u5931\u8D25",
5606
+ "numberBox.increment": "\u589E\u5927",
5607
+ "numberBox.decrement": "\u51CF\u5C0F",
5608
+ "fileDropZone.browse": "\u6D4F\u89C8\u6587\u4EF6",
5609
+ "fileDropZone.dropHint": "\u5C06\u6587\u4EF6\u62D6\u653E\u5230\u6B64\u5904",
5610
+ "fileDropZone.remove": "\u79FB\u9664\u6587\u4EF6",
5611
+ "pager.label": "\u5206\u9875",
5612
+ "pager.first": "\u7B2C\u4E00\u9875",
5613
+ "pager.previous": "\u4E0A\u4E00\u9875",
5614
+ "pager.next": "\u4E0B\u4E00\u9875",
5615
+ "pager.last": "\u6700\u540E\u4E00\u9875",
5616
+ "pager.page": "\u7B2C {n} \u9875",
5617
+ "pager.pageSize": "\u6BCF\u9875\u6761\u6570",
5618
+ "pager.perPage": "{n} \u6761/\u9875",
5619
+ "pager.total": "\u5171 {n} \u6761",
5620
+ "dataTable.empty": "\u6682\u65E0\u6570\u636E",
5621
+ "dataTable.loading": "\u52A0\u8F7D\u4E2D",
5622
+ "dataTable.selectAll": "\u9009\u62E9\u5168\u90E8\u884C",
5623
+ "dataTable.selectRow": "\u9009\u62E9\u6B64\u884C",
5624
+ "dataTable.expandRow": "\u5C55\u5F00\u6B64\u884C",
5625
+ "dataTable.collapseRow": "\u6536\u8D77\u6B64\u884C",
5626
+ "tabView.close": "\u5173\u95ED\u6807\u7B7E\u9875",
5627
+ "tabView.scrollBackward": "\u5411\u5DE6\u6EDA\u52A8\u6807\u7B7E\u9875",
5628
+ "tabView.scrollForward": "\u5411\u53F3\u6EDA\u52A8\u6807\u7B7E\u9875"
5586
5629
  };
5587
5630
  var winuiStringsByLocale = { en, "zh-Hans": zhHans };
5588
5631
  var StringsContext = createContext2(en);
@@ -5719,8 +5762,8 @@ var Textarea = forwardRef3(
5719
5762
 
5720
5763
  // src/components/ui/choice-group.tsx
5721
5764
  import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
5722
- var { makeStyles: makeStyles4, tokens } = fluentComponents;
5723
- var useStyles4 = makeStyles4({
5765
+ var { makeStyles: makeStyles3, tokens } = fluentComponents;
5766
+ var useStyles3 = makeStyles3({
5724
5767
  // The items sit flush: SelectorBar's horizontal StackLayout states no spacing,
5725
5768
  // so each item's 12px of side padding is the whole distance between one label
5726
5769
  // and the next. SelectorBarItemSpacing, 8, is the icon-to-text gap and these
@@ -5870,7 +5913,7 @@ function ChoiceGroup({
5870
5913
  readOnly,
5871
5914
  value
5872
5915
  }) {
5873
- const styles = useStyles4();
5916
+ const styles = useStyles3();
5874
5917
  const name = useId();
5875
5918
  const selectedIndex = items.findIndex((item) => item.value === value);
5876
5919
  const handleKeyDown = (event) => {
@@ -6829,8 +6872,8 @@ var useCopyToClipboard = () => {
6829
6872
 
6830
6873
  // src/components/ui/code-block.tsx
6831
6874
  import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
6832
- var { Button: Button2, makeStyles: makeStyles5, mergeClasses: mergeClasses4 } = fluentComponents;
6833
- var useStyles5 = makeStyles5({
6875
+ var { Button: Button2, makeStyles: makeStyles4, mergeClasses: mergeClasses4 } = fluentComponents;
6876
+ var useStyles4 = makeStyles4({
6834
6877
  // ControlCornerRadius. The clip is why the region's focus visual below is
6835
6878
  // drawn inside the viewport rather than around it.
6836
6879
  // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/Expander/Expander.xaml#L26
@@ -6895,7 +6938,7 @@ var useStyles5 = makeStyles5({
6895
6938
  });
6896
6939
  function CodeBlock({ code, collapsed = false, copyOutcome, disabled = false, header, language, onCopy, wrap = false }) {
6897
6940
  const { t: t2 } = useTranslation();
6898
- const styles = useStyles5();
6941
+ const styles = useStyles4();
6899
6942
  const copyLabel = useCopyLabel();
6900
6943
  const highlighted = useMemo2(
6901
6944
  () => highlight(code, language),
@@ -6931,7 +6974,8 @@ import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
6931
6974
  var { Dialog, DialogBody, DialogContent, DialogSurface, mergeClasses: mergeClasses5 } = fluentComponents;
6932
6975
  var surfaceWidthClasses = {
6933
6976
  standard: "",
6934
- editor: "fwt-dialog-shell--editor"
6977
+ editor: "fwt-dialog-shell--editor",
6978
+ wide: "fwt-dialog-shell--wide"
6935
6979
  };
6936
6980
  function DialogShell({ actions, children, onExited, onOpenChange, onSubmit, open, surfaceClassName, title, width = "standard" }) {
6937
6981
  return /* @__PURE__ */ jsx12(
@@ -7011,8 +7055,8 @@ function OutcomeMessageBar({
7011
7055
 
7012
7056
  // src/components/ui/confirm-dialog.tsx
7013
7057
  import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
7014
- var { Button: Button4, DialogActions, DialogTitle, makeStyles: makeStyles6, mergeClasses: mergeClasses6 } = fluentComponents;
7015
- var useStyles6 = makeStyles6({
7058
+ var { Button: Button4, DialogActions, DialogTitle, makeStyles: makeStyles5, mergeClasses: mergeClasses6 } = fluentComponents;
7059
+ var useStyles5 = makeStyles5({
7016
7060
  // The label is StaticInverted because the fill does not follow the theme.
7017
7061
  // Not OnBrand: ../../winui/theme.ts re-points it at WinUI's
7018
7062
  // TextOnAccentFillColorPrimary, which goes black in dark, reading 3.46:1 at
@@ -7042,6 +7086,7 @@ var useStyles6 = makeStyles6({
7042
7086
  }
7043
7087
  }
7044
7088
  });
7089
+ var useDangerFillClass = () => useStyles5().danger;
7045
7090
  function ConfirmDialog({
7046
7091
  actionIntent = "danger",
7047
7092
  actionLabel,
@@ -7058,7 +7103,7 @@ function ConfirmDialog({
7058
7103
  title
7059
7104
  }) {
7060
7105
  const { t: t2 } = useTranslation();
7061
- const styles = useStyles6();
7106
+ const styles = useStyles5();
7062
7107
  return /* @__PURE__ */ jsx14(
7063
7108
  DialogShell,
7064
7109
  {
@@ -7092,15 +7137,88 @@ function ConfirmDialog({
7092
7137
  surfaceClassName: "!w-[min(430px,calc(100vw-48px))]",
7093
7138
  title: /* @__PURE__ */ jsx14(DialogTitle, { children: title }),
7094
7139
  children: /* @__PURE__ */ jsxs6("div", { className: "grid gap-3 min-w-0", children: [
7095
- /* @__PURE__ */ jsx14("span", { children: message }),
7140
+ message && /* @__PURE__ */ jsx14("span", { children: message }),
7096
7141
  error && /* @__PURE__ */ jsx14(OutcomeMessageBar, { onDismiss: onDismissError, children: error })
7097
7142
  ] })
7098
7143
  }
7099
7144
  );
7100
7145
  }
7101
7146
 
7147
+ // src/components/ui/confirm-flyout.tsx
7148
+ import { useCallback as useCallback4, useState as useState5 } from "react";
7149
+ import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
7150
+ var { Button: Button5, Popover, PopoverSurface, PopoverTrigger, Spinner, Text, mergeClasses: mergeClasses7 } = fluentComponents;
7151
+ var errorText = (error) => error instanceof Error ? error.message : String(error);
7152
+ function ConfirmFlyout({
7153
+ cancelLabel,
7154
+ children,
7155
+ confirmLabel,
7156
+ disabled = false,
7157
+ intent = "danger",
7158
+ message,
7159
+ onConfirm,
7160
+ positioning,
7161
+ title
7162
+ }) {
7163
+ const { t: t2 } = useTranslation();
7164
+ const dangerFill = useDangerFillClass();
7165
+ const [open, setOpen] = useState5(false);
7166
+ const [busy, setBusy] = useState5(false);
7167
+ const [error, setError] = useState5(null);
7168
+ const changeOpen = useCallback4((next) => {
7169
+ if (busy || next && disabled) return;
7170
+ setError(null);
7171
+ setOpen(next);
7172
+ }, [busy, disabled]);
7173
+ const confirm = async () => {
7174
+ setError(null);
7175
+ setBusy(true);
7176
+ try {
7177
+ await onConfirm();
7178
+ setOpen(false);
7179
+ } catch (caught) {
7180
+ setError(errorText(caught));
7181
+ } finally {
7182
+ setBusy(false);
7183
+ }
7184
+ };
7185
+ return /* @__PURE__ */ jsxs7(
7186
+ Popover,
7187
+ {
7188
+ onOpenChange: (_2, data) => changeOpen(data.open),
7189
+ open: open && !disabled,
7190
+ positioning,
7191
+ trapFocus: true,
7192
+ children: [
7193
+ /* @__PURE__ */ jsx15(PopoverTrigger, { disableButtonEnhancement: true, children }),
7194
+ /* @__PURE__ */ jsx15(PopoverSurface, { "aria-label": title, children: /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 min-w-0 max-w-[min(320px,calc(100vw-48px))]", children: [
7195
+ title && /* @__PURE__ */ jsx15(Text, { weight: "semibold", children: title }),
7196
+ /* @__PURE__ */ jsx15(Text, { children: message }),
7197
+ error && /* @__PURE__ */ jsx15(OutcomeMessageBar, { onDismiss: () => setError(null), children: error }),
7198
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap gap-2", children: [
7199
+ /* @__PURE__ */ jsx15(
7200
+ Button5,
7201
+ {
7202
+ appearance: "primary",
7203
+ className: mergeClasses7("!whitespace-nowrap", intent === "danger" && dangerFill),
7204
+ disabledFocusable: busy,
7205
+ icon: busy ? /* @__PURE__ */ jsx15(Spinner, { size: "tiny" }) : void 0,
7206
+ onClick: () => {
7207
+ void confirm();
7208
+ },
7209
+ children: confirmLabel
7210
+ }
7211
+ ),
7212
+ cancelLabel !== void 0 && /* @__PURE__ */ jsx15(Button5, { className: "!whitespace-nowrap", disabled: busy, onClick: () => changeOpen(false), children: cancelLabel || t2("common.cancel") })
7213
+ ] })
7214
+ ] }) })
7215
+ ]
7216
+ }
7217
+ );
7218
+ }
7219
+
7102
7220
  // src/components/ui/danger.ts
7103
- var { buttonClassNames, makeStyles: makeStyles7 } = fluentComponents;
7221
+ var { buttonClassNames, makeStyles: makeStyles6 } = fluentComponents;
7104
7222
  var RED = "var(--winui-system-fill-critical) !important";
7105
7223
  var FORCED = "Highlight !important";
7106
7224
  var ICON = `& .${buttonClassNames.icon}`;
@@ -7110,7 +7228,7 @@ var PRESSED = `${ENABLED}:active`;
7110
7228
  var KEYBOARD = `${ENABLED}[data-fui-focus-visible]`;
7111
7229
  var buttonPaint = { color: RED, [ICON]: { color: RED } };
7112
7230
  var buttonForcedPaint = { color: FORCED, [ICON]: { color: FORCED } };
7113
- var useStyles7 = makeStyles7({
7231
+ var useStyles6 = makeStyles6({
7114
7232
  button: {
7115
7233
  [HOVER]: buttonPaint,
7116
7234
  [PRESSED]: buttonPaint,
@@ -7132,12 +7250,16 @@ var useStyles7 = makeStyles7({
7132
7250
  }
7133
7251
  }
7134
7252
  });
7135
- var useDangerActionClasses = () => useStyles7();
7136
- var useTextStyles = makeStyles7({
7253
+ var useDangerActionClasses = () => useStyles6();
7254
+ var useTextStyles = makeStyles6({
7137
7255
  danger: { color: "var(--winui-system-fill-critical)" }
7138
7256
  });
7139
7257
  var useDangerTextClass = () => useTextStyles().danger;
7140
7258
 
7259
+ // src/components/ui/data-table.tsx
7260
+ import { ChevronDown16Regular, ChevronRight16Regular } from "@fluentui/react-icons";
7261
+ import { useMemo as useMemo3, useState as useState6 } from "react";
7262
+
7141
7263
  // src/components/ui/layout.ts
7142
7264
  var TIGHT_STACK_CLASS = "grid gap-1";
7143
7265
  var HEADER_ROW_CLASS = "flex items-center justify-between min-w-0 max-[900px]:flex-col max-[900px]:items-stretch";
@@ -7149,19 +7271,6 @@ var SCROLLPORT_FILL_CLASS = "h-full min-h-max";
7149
7271
  var CONTROL_ROW_CLASS = "!min-h-[34px]";
7150
7272
  var CHECKBOX_LIST_CLASS = "gap-x-2 gap-y-1";
7151
7273
 
7152
- // src/components/ui/dashboard-page-header.tsx
7153
- import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
7154
- var { Text, mergeClasses: mergeClasses7 } = fluentComponents;
7155
- function DashboardPageHeader({ actions, className, description, title }) {
7156
- return /* @__PURE__ */ jsxs7("header", { className: mergeClasses7(HEADER_ROW_CLASS, "gap-[18px]", className), children: [
7157
- /* @__PURE__ */ jsxs7("div", { className: "grid gap-1.5 min-w-0", children: [
7158
- /* @__PURE__ */ jsx15(Text, { as: "h1", size: 700, weight: "semibold", className: "m-0", children: title }),
7159
- description !== void 0 && /* @__PURE__ */ jsx15(Text, { size: 200, className: "text-fui-fg2 max-w-[760px]", children: description })
7160
- ] }),
7161
- actions !== void 0 && /* @__PURE__ */ jsx15("div", { className: "flex items-center gap-2 flex-none max-[900px]:justify-start", children: actions })
7162
- ] });
7163
- }
7164
-
7165
7274
  // src/components/ui/empty-state.tsx
7166
7275
  import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
7167
7276
  var { Text: Text2, mergeClasses: mergeClasses8 } = fluentComponents;
@@ -7182,201 +7291,994 @@ function EmptyStateLine({ children, className }) {
7182
7291
  return /* @__PURE__ */ jsx16(Text2, { block: true, size: 300, className: mergeClasses8("text-fui-fg2", className), children });
7183
7292
  }
7184
7293
 
7185
- // src/components/ui/error-shell.tsx
7294
+ // src/components/ui/pager.tsx
7295
+ import {
7296
+ ChevronLeft12Regular,
7297
+ ChevronRight12Regular,
7298
+ MoreHorizontal16Regular,
7299
+ Next16Regular,
7300
+ Previous16Regular
7301
+ } from "@fluentui/react-icons";
7186
7302
  import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
7187
- var { Text: Text3 } = fluentComponents;
7188
- function ErrorShell({ action, children, message, title }) {
7189
- return /* @__PURE__ */ jsx17(ScrollArea, { axes: "vertical", className: "fwt-error-shell-viewport", contentClassName: "h-full", children: /* @__PURE__ */ jsxs9("main", { className: "fwt-error-shell", children: [
7190
- /* @__PURE__ */ jsxs9("div", { className: "fwt-error-shell-stack", children: [
7191
- /* @__PURE__ */ jsx17(Text3, { align: "center", as: "h1", size: 700, weight: "semibold", children: title }),
7192
- message !== void 0 && /* @__PURE__ */ jsx17(Text3, { align: "center", as: "p", className: "text-fui-fg2", size: 300, children: message })
7193
- ] }),
7194
- children,
7195
- action !== void 0 && /* @__PURE__ */ jsx17("div", { className: "fwt-error-shell-actions", children: action })
7196
- ] }) });
7303
+ var { Button: Button6, Option: Option2, Text: Text3, Tooltip: Tooltip2, makeStyles: makeStyles7, mergeClasses: mergeClasses9 } = fluentComponents;
7304
+ function pagerPageCount(total, pageSize) {
7305
+ if (!(pageSize > 0) || !(total > 0)) return 1;
7306
+ return Math.ceil(total / pageSize);
7197
7307
  }
7198
- function ErrorStack({ children }) {
7199
- return /* @__PURE__ */ jsx17(
7200
- ScrollArea,
7201
- {
7202
- axes: "horizontal",
7203
- className: "winui-focus-rect-within w-full min-w-0 rounded-[var(--winui-overlay-corner-radius,8px)] border border-solid border-fui-stroke1 bg-fui-bg2 text-left",
7204
- children: /* @__PURE__ */ jsx17("pre", { className: "m-0 w-max min-w-full p-4 font-mono", children: /* @__PURE__ */ jsx17("code", { children }) })
7308
+ function pagerPageWindow(page, pageCount) {
7309
+ if (pageCount < 8) return Array.from({ length: Math.max(pageCount, 0) }, (_2, index) => index + 1);
7310
+ const selectedIndex = page - 1;
7311
+ if (selectedIndex < 4) return [1, 2, 3, 4, 5, "end-ellipsis", pageCount];
7312
+ if (selectedIndex >= pageCount - 4) return [1, "start-ellipsis", pageCount - 4, pageCount - 3, pageCount - 2, pageCount - 1, pageCount];
7313
+ return [1, "start-ellipsis", page - 1, page, page + 1, "end-ellipsis", pageCount];
7314
+ }
7315
+ var clampPage = (page, pageCount) => Math.min(Math.max(Math.trunc(page) || 1, 1), pageCount);
7316
+ var useStyles7 = makeStyles7({
7317
+ navButton: {
7318
+ height: "40px !important",
7319
+ minWidth: "40px !important",
7320
+ padding: "0 !important",
7321
+ width: "40px !important"
7322
+ },
7323
+ numberButton: {
7324
+ height: "20px !important",
7325
+ minHeight: "20px !important",
7326
+ minWidth: "32px !important",
7327
+ paddingBlock: "0 !important",
7328
+ paddingInline: "0 !important"
7329
+ },
7330
+ selected: {
7331
+ position: "relative",
7332
+ "::after": {
7333
+ backgroundColor: "var(--winui-accent-fill-default)",
7334
+ borderRadius: "1px",
7335
+ bottom: "-10px",
7336
+ content: '""',
7337
+ height: "2px",
7338
+ insetInline: 0,
7339
+ position: "absolute",
7340
+ "@media (forced-colors: active)": { backgroundColor: "Highlight" }
7205
7341
  }
7206
- );
7342
+ },
7343
+ // The glyphs are mirrored for a right-to-left reading order.
7344
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/PagerControl/PagerControl_themeresources.xaml#L80
7345
+ mirrored: {
7346
+ ':where([dir="rtl"]) &': { transform: "scaleX(-1)" }
7347
+ }
7348
+ });
7349
+ function NavigationButton({ className, disabled, icon, label, onClick }) {
7350
+ return /* @__PURE__ */ jsx17(Tooltip2, { content: label, relationship: "label", children: /* @__PURE__ */ jsx17(Button6, { appearance: "subtle", className, disabled, icon, onClick }) });
7351
+ }
7352
+ function Pager({
7353
+ ariaLabel,
7354
+ className,
7355
+ disabled = false,
7356
+ onPageChange,
7357
+ onPageSizeChange,
7358
+ page,
7359
+ pageCount: pageCountProp,
7360
+ pageSize,
7361
+ pageSizeOptions,
7362
+ showTotal = false,
7363
+ total
7364
+ }) {
7365
+ const { t: t2 } = useTranslation();
7366
+ const styles = useStyles7();
7367
+ const pageCount = Math.max(1, Math.trunc(pageCountProp ?? pagerPageCount(total ?? 0, pageSize ?? 1)));
7368
+ const current = clampPage(page, pageCount);
7369
+ const go = (next) => {
7370
+ const target = clampPage(next, pageCount);
7371
+ if (target !== current) onPageChange(target);
7372
+ };
7373
+ const atStart = current <= 1;
7374
+ const atEnd = current >= pageCount;
7375
+ const mirrored = styles.mirrored;
7376
+ const sizes = pageSize !== void 0 && pageSizeOptions && onPageSizeChange ? [.../* @__PURE__ */ new Set([...pageSizeOptions, pageSize])].sort((a2, b2) => a2 - b2) : null;
7377
+ const sizeLabel = (size) => t2("pager.perPage").replace("{n}", String(size));
7378
+ return /* @__PURE__ */ jsxs9("nav", { "aria-label": ariaLabel ?? t2("pager.label"), className: mergeClasses9("flex flex-wrap items-center justify-end gap-x-4 gap-y-1", className), children: [
7379
+ showTotal && total !== void 0 && (typeof showTotal === "function" ? showTotal(total) : /* @__PURE__ */ jsx17(Text3, { className: "text-fui-fg2", size: 300, children: t2("pager.total").replace("{n}", String(total)) })),
7380
+ sizes && pageSize !== void 0 && /* @__PURE__ */ jsx17(
7381
+ Dropdown,
7382
+ {
7383
+ "aria-label": t2("pager.pageSize"),
7384
+ disabled,
7385
+ onOptionSelect: (_2, data) => {
7386
+ const next = Number(data.optionValue);
7387
+ if (next > 0 && next !== pageSize) onPageSizeChange?.(next);
7388
+ },
7389
+ selectedOptions: [String(pageSize)],
7390
+ style: { "--fwt-select-min-width": "112px" },
7391
+ value: sizeLabel(pageSize),
7392
+ children: sizes.map((size) => /* @__PURE__ */ jsx17(Option2, { text: sizeLabel(size), value: String(size), children: sizeLabel(size) }, size))
7393
+ }
7394
+ ),
7395
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center", children: [
7396
+ /* @__PURE__ */ jsx17(NavigationButton, { className: styles.navButton, disabled: disabled || atStart, icon: /* @__PURE__ */ jsx17(Previous16Regular, { className: mirrored }), label: t2("pager.first"), onClick: () => go(1) }),
7397
+ /* @__PURE__ */ jsx17(NavigationButton, { className: styles.navButton, disabled: disabled || atStart, icon: /* @__PURE__ */ jsx17(ChevronLeft12Regular, { className: mirrored }), label: t2("pager.previous"), onClick: () => go(current - 1) }),
7398
+ /* @__PURE__ */ jsx17("div", { className: "flex items-center h-10", style: { gap: 5 }, children: pagerPageWindow(current, pageCount).map((item) => typeof item === "number" ? /* @__PURE__ */ jsx17("span", { className: mergeClasses9("inline-flex", item === current && styles.selected), children: /* @__PURE__ */ jsx17(
7399
+ Button6,
7400
+ {
7401
+ appearance: "subtle",
7402
+ "aria-current": item === current ? "page" : void 0,
7403
+ "aria-label": t2("pager.page").replace("{n}", String(item)),
7404
+ className: styles.numberButton,
7405
+ disabled,
7406
+ onClick: () => go(item),
7407
+ children: item
7408
+ }
7409
+ ) }, item) : /* @__PURE__ */ jsx17("span", { "aria-hidden": true, className: "grid place-items-center w-5 text-fui-fg2", children: /* @__PURE__ */ jsx17(MoreHorizontal16Regular, {}) }, item)) }),
7410
+ /* @__PURE__ */ jsx17(NavigationButton, { className: styles.navButton, disabled: disabled || atEnd, icon: /* @__PURE__ */ jsx17(ChevronRight12Regular, { className: mirrored }), label: t2("pager.next"), onClick: () => go(current + 1) }),
7411
+ /* @__PURE__ */ jsx17(NavigationButton, { className: styles.navButton, disabled: disabled || atEnd, icon: /* @__PURE__ */ jsx17(Next16Regular, { className: mirrored }), label: t2("pager.last"), onClick: () => go(pageCount) })
7412
+ ] })
7413
+ ] });
7207
7414
  }
7208
7415
 
7209
- // src/components/ui/status-badge.tsx
7416
+ // src/components/ui/table-actions.tsx
7210
7417
  import { jsx as jsx18 } from "react/jsx-runtime";
7211
- var { Badge, makeStyles: makeStyles8, mergeClasses: mergeClasses9 } = fluentComponents;
7212
- var TONE_HUES = {
7213
- accent: { light: "#0078d4", dark: "#4cc2ff" },
7214
- danger: { light: "#c42b1c", dark: "#ff99a4" },
7215
- neutral: { light: "#8a8a8a", dark: "#9a9a9a" },
7216
- success: { light: "#0f7b0f", dark: "#6ccb5f" },
7217
- warning: { light: "#9d5d00", dark: "#fce100" }
7218
- };
7219
- var CHIP_ROOT_PADDING = "5px";
7220
- var LABEL_INSET = `calc(var(--strokeWidthThin) + ${CHIP_ROOT_PADDING} + var(--spacingHorizontalXXS))`;
7418
+ var { TableCell, TableHeaderCell, makeStyles: makeStyles8, mergeClasses: mergeClasses10 } = fluentComponents;
7221
7419
  var useStyles8 = makeStyles8({
7222
- root: {
7223
- paddingLeft: LABEL_INSET,
7224
- paddingRight: LABEL_INSET
7420
+ trailing: {
7421
+ textAlign: "right",
7422
+ "& .fui-TableHeaderCell__button": { justifyContent: "flex-end" }
7423
+ },
7424
+ centred: {
7425
+ textAlign: "center",
7426
+ "& .fui-TableHeaderCell__button": { justifyContent: "center" }
7225
7427
  }
7226
7428
  });
7227
- function StatusBadge({ children, className, tone }) {
7228
- const hue = useBadgeHue(TONE_HUES[tone]);
7429
+ function TableTrailingHeader({ className, ...props }) {
7229
7430
  const styles = useStyles8();
7230
- return /* @__PURE__ */ jsx18(Badge, { appearance: "tint", className: mergeClasses9(styles.root, hue.className, className), size: "large", style: hue.style, children });
7231
- }
7232
-
7233
- // src/components/ui/http-badge.tsx
7234
- import { jsx as jsx19 } from "react/jsx-runtime";
7235
- var { makeStyles: makeStyles9 } = fluentComponents;
7236
- var useStyles9 = makeStyles9({
7237
- root: {
7238
- fontFamily: "var(--fontFamilyMonospace)",
7239
- minWidth: "48px",
7240
- paddingTop: "1.5px"
7241
- }
7242
- });
7243
- function HttpBadge({ children, tone }) {
7244
- const styles = useStyles9();
7245
- return /* @__PURE__ */ jsx19(StatusBadge, { className: styles.root, tone, children: /* @__PURE__ */ jsx19("span", { translate: "no", children }) });
7431
+ return /* @__PURE__ */ jsx18(TableHeaderCell, { ...props, className: mergeClasses10(styles.trailing, className) });
7246
7432
  }
7247
- function HttpMethodBadge({ method }) {
7248
- return /* @__PURE__ */ jsx19(HttpBadge, { tone: method === "GET" ? "accent" : method === "POST" ? "success" : "neutral", children: method });
7433
+ function TableTrailingCell({ className, ...props }) {
7434
+ const styles = useStyles8();
7435
+ return /* @__PURE__ */ jsx18(TableCell, { ...props, className: mergeClasses10(styles.trailing, className) });
7249
7436
  }
7250
- function HttpStatusBadge({ children, severity }) {
7251
- return /* @__PURE__ */ jsx19(HttpBadge, { tone: severity === "error" ? "danger" : severity, children });
7437
+ function TableCentredHeader({ className, ...props }) {
7438
+ const styles = useStyles8();
7439
+ return /* @__PURE__ */ jsx18(TableHeaderCell, { ...props, className: mergeClasses10(styles.centred, className) });
7252
7440
  }
7253
-
7254
- // src/components/ui/info-label.tsx
7255
- import { jsx as jsx20 } from "react/jsx-runtime";
7256
- var { InfoLabel } = fluentComponents;
7257
- var infoLabelSlot = (label, info) => (_2, slotProps2) => /* @__PURE__ */ jsx20(InfoLabel, { ...slotProps2, info, children: label });
7258
-
7259
- // src/components/ui/loading-screen.tsx
7260
- import { jsx as jsx21 } from "react/jsx-runtime";
7261
- var { Spinner } = fluentComponents;
7262
- function AppLoadingScreen({ label }) {
7263
- return /* @__PURE__ */ jsx21("main", { className: "fwt-loading fwt-loading-app", children: /* @__PURE__ */ jsx21(Spinner, { label }) });
7441
+ function TableCentredCell({ className, ...props }) {
7442
+ const styles = useStyles8();
7443
+ return /* @__PURE__ */ jsx18(TableCell, { ...props, className: mergeClasses10(styles.centred, className) });
7264
7444
  }
7265
- function ContentLoadingScreen({ label }) {
7266
- return /* @__PURE__ */ jsx21("div", { className: "fwt-loading", children: /* @__PURE__ */ jsx21(Spinner, { label }) });
7445
+ var stopRowSelection = { onClick: (event) => event.stopPropagation() };
7446
+ var ACTION_GAP = "var(--spacingHorizontalXS)";
7447
+ var ACTION_BUTTON_SIZE = "24px";
7448
+ var TABLE_ACTIONS_WIDTH = `calc(3 * ${ACTION_BUTTON_SIZE} + 2 * ${ACTION_GAP} + var(--spacingHorizontalS) + var(--fwt-panel-inset))`;
7449
+ function TableActions({ children }) {
7450
+ return /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-end", style: { gap: ACTION_GAP }, ...stopRowSelection, children });
7267
7451
  }
7268
7452
 
7269
- // src/components/ui/masked-icon.tsx
7270
- import { jsx as jsx22 } from "react/jsx-runtime";
7271
- var { mergeClasses: mergeClasses10 } = fluentComponents;
7272
- function MaskedIcon({ className, maskSize = "contain", url }) {
7273
- const style = {
7274
- backgroundColor: "currentColor",
7275
- maskImage: `url(${url})`,
7276
- WebkitMaskImage: `url(${url})`,
7277
- maskSize,
7278
- WebkitMaskSize: maskSize,
7279
- maskRepeat: "no-repeat",
7280
- WebkitMaskRepeat: "no-repeat",
7281
- maskPosition: "center",
7282
- WebkitMaskPosition: "center"
7283
- };
7284
- return /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: mergeClasses10("block flex-none", className), style });
7453
+ // src/components/ui/table-columns.tsx
7454
+ import { jsx as jsx19 } from "react/jsx-runtime";
7455
+ function TableColumns({ widths }) {
7456
+ return /* @__PURE__ */ jsx19("colgroup", { children: widths.map((width, index) => /* @__PURE__ */ jsx19("col", { style: width === null ? void 0 : { width } }, index)) });
7285
7457
  }
7286
7458
 
7287
- // src/components/ui/multiselect-combobox.tsx
7288
- import { useState as useState5 } from "react";
7289
- import { jsx as jsx23, jsxs as jsxs10 } from "react/jsx-runtime";
7290
- var { Option: Option2 } = fluentComponents;
7291
- var sameValues = (left, right) => left.length === right.length && new Set(right).size === right.length && left.every((entry) => right.includes(entry));
7292
- function MultiselectCombobox({
7459
+ // src/components/ui/data-table.tsx
7460
+ import { jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
7461
+ var {
7462
+ Button: Button7,
7463
+ Spinner: Spinner2,
7464
+ Table,
7465
+ TableBody,
7466
+ TableCell: TableCell2,
7467
+ TableCellLayout,
7468
+ TableHeader,
7469
+ TableHeaderCell: TableHeaderCell2,
7470
+ TableRow,
7471
+ TableSelectionCell,
7472
+ mergeClasses: mergeClasses11
7473
+ } = fluentComponents;
7474
+ var LEADING_COLUMN_WIDTH = "44px";
7475
+ var HEADER_BY_ALIGN = { center: TableCentredHeader, end: TableTrailingHeader, start: TableHeaderCell2 };
7476
+ var CELL_BY_ALIGN = { center: TableCentredCell, end: TableTrailingCell, start: TableCell2 };
7477
+ var toggle = (keys2, key) => keys2.includes(key) ? keys2.filter((candidate) => candidate !== key) : [...keys2, key];
7478
+ function DataTable({
7293
7479
  ariaLabel,
7294
7480
  className,
7295
- clearLabel,
7296
- closedLabel = "",
7297
- disabled = false,
7298
- freeform = false,
7299
- normalizeValue = (entry) => entry,
7300
- onChange,
7301
- options,
7302
- placeholder,
7303
- readOnly,
7304
- renderOption,
7305
- value
7481
+ columns,
7482
+ defaultSort,
7483
+ empty,
7484
+ expandable,
7485
+ loading = false,
7486
+ minWidth,
7487
+ onRowClick,
7488
+ onSortChange,
7489
+ pagination,
7490
+ rowClassName,
7491
+ rowKey,
7492
+ rows,
7493
+ selection,
7494
+ size = "medium"
7306
7495
  }) {
7307
- const [open, setOpen] = useState5(false);
7308
- const [query, setQuery] = useState5("");
7309
- const needle = query.trim().toLowerCase();
7310
- const optionValues = new Set(options.map((option) => option.value));
7311
- const completeOptions = [
7312
- ...options,
7313
- ...[...new Set(value)].filter((entry) => !optionValues.has(entry)).map((entry) => ({ value: entry, label: entry }))
7314
- ];
7315
- const visible = completeOptions.filter((option) => option.label.toLowerCase().includes(needle));
7316
- const commit = (next) => {
7317
- const normalized = [...new Set(next.map(normalizeValue).filter(Boolean))];
7318
- if (!sameValues(normalized, value)) onChange(normalized);
7319
- setQuery("");
7496
+ const { t: t2 } = useTranslation();
7497
+ const [sort, setSort] = useState6(defaultSort ?? null);
7498
+ const [ownPage, setOwnPage] = useState6(1);
7499
+ const [ownPageSize, setOwnPageSize] = useState6(null);
7500
+ const [ownExpanded, setOwnExpanded] = useState6([]);
7501
+ const sortedRows = useMemo3(() => {
7502
+ const column = sort && columns.find((candidate) => candidate.key === sort.key);
7503
+ if (!sort || !column?.sort) return rows;
7504
+ const direction = sort.direction === "ascending" ? 1 : -1;
7505
+ const compare = column.sort;
7506
+ return [...rows].sort((a2, b2) => direction * compare(a2, b2));
7507
+ }, [columns, rows, sort]);
7508
+ const paged = pagination !== void 0 && pagination !== false;
7509
+ const pageSize = !paged ? 0 : pagination.onPageSizeChange ? pagination.pageSize : ownPageSize ?? pagination.pageSize;
7510
+ const pageCount = paged ? Math.max(1, Math.ceil(sortedRows.length / Math.max(pageSize, 1))) : 1;
7511
+ const requestedPage = paged ? pagination.page ?? ownPage : 1;
7512
+ const page = Math.min(Math.max(requestedPage, 1), pageCount);
7513
+ const visibleRows = paged ? sortedRows.slice((page - 1) * pageSize, page * pageSize) : sortedRows;
7514
+ const changePage = (next) => {
7515
+ setOwnPage(next);
7516
+ if (paged) pagination.onPageChange?.(next);
7320
7517
  };
7321
- return /* @__PURE__ */ jsxs10(
7322
- Combobox,
7323
- {
7324
- "aria-label": ariaLabel,
7325
- className,
7326
- disabled,
7327
- freeform,
7328
- multiselect: true,
7329
- onChange: (event) => setQuery(event.target.value),
7330
- onKeyDown: freeform ? (event) => {
7331
- if (event.key !== "Enter" || query.trim() === "") return;
7332
- event.preventDefault();
7333
- commit([...value, query]);
7334
- } : void 0,
7335
- onOpenChange: (_2, data) => {
7336
- setOpen(data.open);
7337
- setQuery("");
7338
- },
7339
- onOptionSelect: (_2, data) => commit(data.optionValue === "" ? [] : data.selectedOptions),
7340
- placeholder,
7341
- readOnly,
7342
- selectedOptions: clearLabel && value.length === 0 ? [""] : [...value],
7343
- value: open ? query : closedLabel,
7344
- children: [
7345
- clearLabel && needle === "" && /* @__PURE__ */ jsx23(Option2, { text: clearLabel, value: "", children: clearLabel }),
7346
- visible.map((option) => /* @__PURE__ */ jsx23(Option2, { text: option.label, value: option.value, children: renderOption ? renderOption(option) : option.label }, option.value))
7347
- ]
7518
+ const changePageSize = (next) => {
7519
+ setOwnPageSize(next);
7520
+ setOwnPage(1);
7521
+ if (paged) {
7522
+ pagination.onPageSizeChange?.(next);
7523
+ pagination.onPageChange?.(1);
7348
7524
  }
7349
- );
7350
- }
7351
- var valuesAsOptions = (values) => values.map((value) => ({ value, label: value }));
7352
-
7353
- // src/components/ui/open-link-label.tsx
7354
- import { Open16Regular } from "@fluentui/react-icons";
7355
- import { jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
7356
- function OpenLinkLabel({ children }) {
7357
- return /* @__PURE__ */ jsxs11("span", { className: "inline-flex items-center gap-1", children: [
7358
- children,
7359
- /* @__PURE__ */ jsx24(Open16Regular, { "aria-hidden": true, className: "block flex-none" })
7525
+ };
7526
+ const cycleSort = (column) => {
7527
+ const next = sort?.key !== column.key ? { key: column.key, direction: "ascending" } : sort.direction === "ascending" ? { key: column.key, direction: "descending" } : null;
7528
+ setSort(next);
7529
+ onSortChange?.(next);
7530
+ setOwnPage(1);
7531
+ };
7532
+ const selectedKeys = selection?.selectedKeys ?? [];
7533
+ const selectionMode = selection?.mode ?? "multiple";
7534
+ const isSelectable = (row) => selection?.isSelectable?.(row) ?? true;
7535
+ const selectableVisible = visibleRows.filter(isSelectable).map(rowKey);
7536
+ const selectedVisible = selectableVisible.filter((key) => selectedKeys.includes(key));
7537
+ const allChecked = selectedVisible.length === 0 ? false : selectedVisible.length === selectableVisible.length ? true : "mixed";
7538
+ const toggleAll = () => {
7539
+ if (!selection) return;
7540
+ selection.onChange(allChecked === true ? selectedKeys.filter((key) => !selectableVisible.includes(key)) : [...selectedKeys, ...selectableVisible.filter((key) => !selectedKeys.includes(key))]);
7541
+ };
7542
+ const selectRow = (key) => {
7543
+ if (!selection) return;
7544
+ selection.onChange(selectionMode === "single" ? [key] : toggle(selectedKeys, key));
7545
+ };
7546
+ const stop = (event) => event.stopPropagation();
7547
+ const expandedKeys = expandable?.expandedKeys ?? ownExpanded;
7548
+ const toggleExpanded = (key) => {
7549
+ const next = toggle(expandedKeys, key);
7550
+ setOwnExpanded(next);
7551
+ expandable?.onExpandedChange?.(next);
7552
+ };
7553
+ const columnCount = columns.length + (selection ? 1 : 0) + (expandable ? 1 : 0);
7554
+ const widths = [
7555
+ ...selection ? [LEADING_COLUMN_WIDTH] : [],
7556
+ ...expandable ? [LEADING_COLUMN_WIDTH] : [],
7557
+ ...columns.map((column) => column.width ?? null)
7558
+ ];
7559
+ const tableStyle = minWidth === void 0 ? void 0 : { minWidth };
7560
+ return /* @__PURE__ */ jsxs10("div", { className: mergeClasses11("min-w-0", className), children: [
7561
+ /* @__PURE__ */ jsxs10("div", { "aria-busy": loading || void 0, className: "relative min-w-0", children: [
7562
+ /* @__PURE__ */ jsx20(ScrollArea, { axes: "horizontal", className: "min-w-0", children: /* @__PURE__ */ jsxs10(Table, { "aria-label": ariaLabel, size, style: tableStyle, children: [
7563
+ /* @__PURE__ */ jsx20(TableColumns, { widths }),
7564
+ /* @__PURE__ */ jsx20(TableHeader, { children: /* @__PURE__ */ jsxs10(TableRow, { children: [
7565
+ selection && (selectionMode === "multiple" ? /* @__PURE__ */ jsx20(
7566
+ TableSelectionCell,
7567
+ {
7568
+ checkboxIndicator: { "aria-label": t2("dataTable.selectAll"), disabled: selectableVisible.length === 0 },
7569
+ checked: allChecked,
7570
+ onClick: selectableVisible.length === 0 ? void 0 : toggleAll
7571
+ }
7572
+ ) : /* @__PURE__ */ jsx20(TableHeaderCell2, {})),
7573
+ expandable && /* @__PURE__ */ jsx20(TableHeaderCell2, {}),
7574
+ columns.map((column) => {
7575
+ const Header = HEADER_BY_ALIGN[column.align ?? "start"];
7576
+ const sorted = sort?.key === column.key ? sort.direction : void 0;
7577
+ return /* @__PURE__ */ jsx20(
7578
+ Header,
7579
+ {
7580
+ onClick: column.sort ? () => cycleSort(column) : void 0,
7581
+ sortable: column.sort !== void 0,
7582
+ sortDirection: sorted,
7583
+ children: column.title
7584
+ },
7585
+ column.key
7586
+ );
7587
+ })
7588
+ ] }) }),
7589
+ /* @__PURE__ */ jsxs10(TableBody, { children: [
7590
+ visibleRows.length === 0 && /* @__PURE__ */ jsx20(TableRow, { children: /* @__PURE__ */ jsx20(TableCell2, { colSpan: columnCount, children: empty ?? /* @__PURE__ */ jsx20(EmptyStateLine, { className: "py-6 text-center", children: t2("dataTable.empty") }) }) }),
7591
+ visibleRows.flatMap((row, index) => {
7592
+ const key = rowKey(row);
7593
+ const selected = selectedKeys.includes(key);
7594
+ const expanded = expandable !== void 0 && expandedKeys.includes(key);
7595
+ const canExpand = expandable !== void 0 && (expandable.isExpandable?.(row) ?? true);
7596
+ const items = [/* @__PURE__ */ jsxs10(
7597
+ TableRow,
7598
+ {
7599
+ "aria-selected": selection ? selected : void 0,
7600
+ className: mergeClasses11(onRowClick && "cursor-pointer", rowClassName?.(row, index)),
7601
+ onClick: onRowClick ? () => onRowClick(row, index) : void 0,
7602
+ children: [
7603
+ selection && /* @__PURE__ */ jsx20(
7604
+ TableSelectionCell,
7605
+ {
7606
+ checkboxIndicator: { "aria-label": t2("dataTable.selectRow"), disabled: !isSelectable(row) },
7607
+ checked: selected,
7608
+ onClick: (event) => {
7609
+ stop(event);
7610
+ if (isSelectable(row)) selectRow(key);
7611
+ },
7612
+ radioIndicator: { "aria-label": t2("dataTable.selectRow"), disabled: !isSelectable(row) },
7613
+ type: selectionMode === "single" ? "radio" : "checkbox"
7614
+ }
7615
+ ),
7616
+ expandable && /* @__PURE__ */ jsx20(TableCell2, { ...stopRowSelection, children: canExpand && /* @__PURE__ */ jsx20(
7617
+ Button7,
7618
+ {
7619
+ appearance: "subtle",
7620
+ "aria-expanded": expanded,
7621
+ "aria-label": t2(expanded ? "dataTable.collapseRow" : "dataTable.expandRow"),
7622
+ icon: expanded ? /* @__PURE__ */ jsx20(ChevronDown16Regular, {}) : /* @__PURE__ */ jsx20(ChevronRight16Regular, {}),
7623
+ onClick: () => toggleExpanded(key),
7624
+ size: "small"
7625
+ }
7626
+ ) }),
7627
+ columns.map((column) => {
7628
+ const Cell = CELL_BY_ALIGN[column.align ?? "start"];
7629
+ const content = column.render ? column.render(row, index) : column.dataKey === void 0 ? null : row[column.dataKey];
7630
+ return /* @__PURE__ */ jsx20(Cell, { className: column.ellipsis ? "overflow-hidden" : void 0, children: column.ellipsis ? /* @__PURE__ */ jsx20(TableCellLayout, { title: typeof content === "string" || typeof content === "number" ? String(content) : void 0, truncate: true, children: content }) : content }, column.key);
7631
+ })
7632
+ ]
7633
+ },
7634
+ key
7635
+ )];
7636
+ if (expanded && canExpand) {
7637
+ items.push(/* @__PURE__ */ jsx20(TableRow, { children: /* @__PURE__ */ jsx20(TableCell2, { colSpan: columnCount, children: expandable.render(row) }) }, `${key}:expanded`));
7638
+ }
7639
+ return items;
7640
+ })
7641
+ ] })
7642
+ ] }) }),
7643
+ loading && /* @__PURE__ */ jsx20(
7644
+ "div",
7645
+ {
7646
+ className: "absolute inset-0 grid place-items-center",
7647
+ style: { background: "color-mix(in srgb, var(--colorNeutralBackground1) 60%, transparent)" },
7648
+ children: /* @__PURE__ */ jsx20(Spinner2, { label: t2("dataTable.loading"), size: "small" })
7649
+ }
7650
+ )
7651
+ ] }),
7652
+ paged && !(pagination.hideOnSinglePage && pageCount <= 1) && /* @__PURE__ */ jsx20("div", { className: "flex justify-end px-3 py-1", style: { borderTop: "1px solid var(--colorNeutralStroke3)" }, children: /* @__PURE__ */ jsx20(
7653
+ Pager,
7654
+ {
7655
+ onPageChange: changePage,
7656
+ onPageSizeChange: changePageSize,
7657
+ page,
7658
+ pageCount,
7659
+ pageSize,
7660
+ pageSizeOptions: pagination.pageSizeOptions,
7661
+ showTotal: pagination.showTotal,
7662
+ total: sortedRows.length
7663
+ }
7664
+ ) })
7360
7665
  ] });
7361
7666
  }
7362
7667
 
7363
- // src/components/ui/outcome-toast.tsx
7364
- import { createContext as createContext3, useCallback as useCallback4, useContext as useContext3, useId as useId2, useMemo as useMemo3, useRef as useRef5 } from "react";
7365
- import { jsx as jsx25, jsxs as jsxs12 } from "react/jsx-runtime";
7366
- var { Spinner: Spinner2, Toast, Toaster, ToastTitle, useToastController } = fluentComponents;
7668
+ // src/components/ui/dashboard-page-header.tsx
7669
+ import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
7670
+ var { Text: Text4, mergeClasses: mergeClasses12 } = fluentComponents;
7671
+ function DashboardPageHeader({ actions, className, description, title }) {
7672
+ return /* @__PURE__ */ jsxs11("header", { className: mergeClasses12(HEADER_ROW_CLASS, "gap-[18px]", className), children: [
7673
+ /* @__PURE__ */ jsxs11("div", { className: "grid gap-1.5 min-w-0", children: [
7674
+ /* @__PURE__ */ jsx21(Text4, { as: "h1", size: 700, weight: "semibold", className: "m-0", children: title }),
7675
+ description !== void 0 && /* @__PURE__ */ jsx21(Text4, { size: 200, className: "text-fui-fg2 max-w-[760px]", children: description })
7676
+ ] }),
7677
+ actions !== void 0 && /* @__PURE__ */ jsx21("div", { className: "flex items-center gap-2 flex-none max-[900px]:justify-start", children: actions })
7678
+ ] });
7679
+ }
7680
+
7681
+ // src/components/ui/error-shell.tsx
7682
+ import { jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
7683
+ var { Text: Text5 } = fluentComponents;
7684
+ function ErrorShell({ action, children, message, title }) {
7685
+ return /* @__PURE__ */ jsx22(ScrollArea, { axes: "vertical", className: "fwt-error-shell-viewport", contentClassName: "h-full", children: /* @__PURE__ */ jsxs12("main", { className: "fwt-error-shell", children: [
7686
+ /* @__PURE__ */ jsxs12("div", { className: "fwt-error-shell-stack", children: [
7687
+ /* @__PURE__ */ jsx22(Text5, { align: "center", as: "h1", size: 700, weight: "semibold", children: title }),
7688
+ message !== void 0 && /* @__PURE__ */ jsx22(Text5, { align: "center", as: "p", className: "text-fui-fg2", size: 300, children: message })
7689
+ ] }),
7690
+ children,
7691
+ action !== void 0 && /* @__PURE__ */ jsx22("div", { className: "fwt-error-shell-actions", children: action })
7692
+ ] }) });
7693
+ }
7694
+ function ErrorStack({ children }) {
7695
+ return /* @__PURE__ */ jsx22(
7696
+ ScrollArea,
7697
+ {
7698
+ axes: "horizontal",
7699
+ className: "winui-focus-rect-within w-full min-w-0 rounded-[var(--winui-overlay-corner-radius,8px)] border border-solid border-fui-stroke1 bg-fui-bg2 text-left",
7700
+ children: /* @__PURE__ */ jsx22("pre", { className: "m-0 w-max min-w-full p-4 font-mono", children: /* @__PURE__ */ jsx22("code", { children }) })
7701
+ }
7702
+ );
7703
+ }
7704
+
7705
+ // src/components/ui/file-drop-zone.tsx
7706
+ import { ArrowUploadRegular, DismissRegular as DismissRegular3, DocumentRegular } from "@fluentui/react-icons";
7707
+ import { useId as useId2, useRef as useRef5, useState as useState7 } from "react";
7708
+ import { Fragment as Fragment2, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
7709
+ var { Button: Button8, Text: Text6, makeStyles: makeStyles9, mergeClasses: mergeClasses13, shorthands } = fluentComponents;
7710
+ var useStyles9 = makeStyles9({
7711
+ root: {
7712
+ display: "grid",
7713
+ rowGap: "8px",
7714
+ minWidth: 0
7715
+ },
7716
+ zone: {
7717
+ alignItems: "center",
7718
+ backgroundColor: "var(--winui-card-background-fill-default)",
7719
+ ...shorthands.border("1px", "dashed", "var(--winui-surface-stroke-default)"),
7720
+ borderRadius: "var(--winui-control-corner-radius)",
7721
+ boxSizing: "border-box",
7722
+ color: "var(--winui-text-fill-primary)",
7723
+ cursor: "pointer",
7724
+ display: "grid",
7725
+ justifyItems: "center",
7726
+ minHeight: "120px",
7727
+ padding: "24px 16px",
7728
+ rowGap: "4px",
7729
+ textAlign: "center",
7730
+ transitionDuration: "var(--winui-control-faster-animation-duration)",
7731
+ transitionProperty: "background-color, border-color",
7732
+ "@media (prefers-reduced-motion: reduce)": { transitionDuration: "0.01ms" },
7733
+ "&:hover": { backgroundColor: "var(--winui-control-fill-secondary)" },
7734
+ "&:active": { backgroundColor: "var(--winui-control-fill-tertiary)" },
7735
+ // The system focus visual, as ./settings-card draws it.
7736
+ "&:focus-visible": {
7737
+ boxShadow: "0 0 0 1px var(--winui-focus-stroke-inner)",
7738
+ outlineColor: "var(--winui-focus-stroke-outer)",
7739
+ outlineOffset: "1px",
7740
+ outlineStyle: "solid",
7741
+ outlineWidth: "2px"
7742
+ }
7743
+ },
7744
+ dragOver: {
7745
+ backgroundColor: "var(--winui-accent-tint-fill-default)",
7746
+ ...shorthands.borderColor("var(--winui-accent-base)"),
7747
+ "&:hover": { backgroundColor: "var(--winui-accent-tint-fill-default)" },
7748
+ "&:active": { backgroundColor: "var(--winui-accent-tint-fill-secondary)" }
7749
+ },
7750
+ disabled: {
7751
+ backgroundColor: "var(--winui-control-fill-disabled)",
7752
+ color: "var(--winui-text-fill-disabled)",
7753
+ cursor: "not-allowed",
7754
+ "&:hover": { backgroundColor: "var(--winui-control-fill-disabled)" },
7755
+ "&:active": { backgroundColor: "var(--winui-control-fill-disabled)" }
7756
+ },
7757
+ icon: {
7758
+ color: "var(--winui-text-fill-secondary)",
7759
+ display: "inline-flex",
7760
+ fontSize: "24px",
7761
+ lineHeight: 0,
7762
+ pointerEvents: "none"
7763
+ },
7764
+ iconDragOver: { color: "var(--winui-accent-base)" },
7765
+ list: {
7766
+ display: "grid",
7767
+ listStyleType: "none",
7768
+ margin: 0,
7769
+ padding: 0,
7770
+ rowGap: "4px"
7771
+ },
7772
+ file: {
7773
+ alignItems: "center",
7774
+ backgroundColor: "var(--winui-card-background-fill-default)",
7775
+ ...shorthands.border("1px", "solid", "var(--winui-card-stroke-default)"),
7776
+ borderRadius: "var(--winui-control-corner-radius)",
7777
+ columnGap: "8px",
7778
+ display: "flex",
7779
+ minHeight: "40px",
7780
+ paddingBlock: "4px",
7781
+ paddingInlineStart: "12px",
7782
+ paddingInlineEnd: "4px"
7783
+ },
7784
+ fileName: {
7785
+ flexGrow: 1,
7786
+ minWidth: 0,
7787
+ overflow: "hidden",
7788
+ textOverflow: "ellipsis",
7789
+ whiteSpace: "nowrap"
7790
+ }
7791
+ });
7792
+ var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
7793
+ var formatFileSize = (bytes) => {
7794
+ let size = bytes;
7795
+ let unit = 0;
7796
+ while (size >= 1024 && unit < BYTE_UNITS.length - 1) {
7797
+ size /= 1024;
7798
+ unit += 1;
7799
+ }
7800
+ const text = new Intl.NumberFormat(void 0, { maximumFractionDigits: unit === 0 ? 0 : 1 }).format(size);
7801
+ return `${text} ${BYTE_UNITS[unit]}`;
7802
+ };
7803
+ var matchesAccept = (file, accept) => {
7804
+ const tokens3 = accept?.split(",").map((token) => token.trim().toLowerCase()).filter(Boolean) ?? [];
7805
+ if (tokens3.length === 0) return true;
7806
+ const name = file.name.toLowerCase();
7807
+ const type = file.type.toLowerCase();
7808
+ return tokens3.some((token) => {
7809
+ if (token.startsWith(".")) return name.endsWith(token);
7810
+ if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
7811
+ return type === token;
7812
+ });
7813
+ };
7814
+ var pickFiles = (files, accept, multiple) => {
7815
+ const accepted = [...files].filter((file) => matchesAccept(file, accept));
7816
+ return multiple ? accepted : accepted.slice(0, 1);
7817
+ };
7818
+ function FileDropZone({
7819
+ accept,
7820
+ className,
7821
+ description,
7822
+ disabled,
7823
+ icon,
7824
+ multiple,
7825
+ onFiles,
7826
+ onRemove,
7827
+ selectedFiles,
7828
+ title
7829
+ }) {
7830
+ const styles = useStyles9();
7831
+ const { t: t2 } = useTranslation();
7832
+ const inputRef = useRef5(null);
7833
+ const depth = useRef5(0);
7834
+ const [dragOver, setDragOver] = useState7(false);
7835
+ const titleId = useId2();
7836
+ const reset = () => {
7837
+ depth.current = 0;
7838
+ setDragOver(false);
7839
+ };
7840
+ const emit = (files) => {
7841
+ const picked = pickFiles(files, accept, multiple);
7842
+ if (picked.length > 0) onFiles(picked);
7843
+ };
7844
+ const open = () => {
7845
+ if (!disabled) inputRef.current?.click();
7846
+ };
7847
+ const handleDragEnter = (event) => {
7848
+ if (disabled) return;
7849
+ event.preventDefault();
7850
+ depth.current += 1;
7851
+ setDragOver(true);
7852
+ };
7853
+ const handleDragOver = (event) => {
7854
+ if (disabled) return;
7855
+ event.preventDefault();
7856
+ event.dataTransfer.dropEffect = "copy";
7857
+ };
7858
+ const handleDragLeave = () => {
7859
+ if (disabled) return;
7860
+ depth.current = Math.max(0, depth.current - 1);
7861
+ if (depth.current === 0) setDragOver(false);
7862
+ };
7863
+ const handleDrop = (event) => {
7864
+ if (disabled) return;
7865
+ event.preventDefault();
7866
+ reset();
7867
+ emit(Array.from(event.dataTransfer.files));
7868
+ };
7869
+ const handleKeyDown = (event) => {
7870
+ if (event.target !== event.currentTarget) return;
7871
+ if (event.key === "Enter" || event.key === " ") {
7872
+ event.preventDefault();
7873
+ open();
7874
+ }
7875
+ };
7876
+ const handleInputChange = (event) => {
7877
+ const files = Array.from(event.target.files ?? []);
7878
+ event.target.value = "";
7879
+ emit(files);
7880
+ };
7881
+ return /* @__PURE__ */ jsxs13("div", { className: mergeClasses13(styles.root, className), children: [
7882
+ /* @__PURE__ */ jsxs13(
7883
+ "div",
7884
+ {
7885
+ "aria-disabled": disabled || void 0,
7886
+ "aria-labelledby": titleId,
7887
+ className: mergeClasses13(styles.zone, dragOver && styles.dragOver, disabled && styles.disabled),
7888
+ "data-drag-over": dragOver || void 0,
7889
+ onClick: open,
7890
+ onDragEnter: handleDragEnter,
7891
+ onDragLeave: handleDragLeave,
7892
+ onDragOver: handleDragOver,
7893
+ onDrop: handleDrop,
7894
+ onKeyDown: handleKeyDown,
7895
+ role: "button",
7896
+ tabIndex: disabled ? -1 : 0,
7897
+ children: [
7898
+ /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: mergeClasses13(styles.icon, dragOver && styles.iconDragOver), children: icon ?? /* @__PURE__ */ jsx23(ArrowUploadRegular, {}) }),
7899
+ /* @__PURE__ */ jsx23(Text6, { id: titleId, size: 300, weight: "semibold", children: dragOver ? t2("fileDropZone.dropHint") : title }),
7900
+ description !== void 0 && /* @__PURE__ */ jsx23(Text6, { size: 200, className: "text-fui-fg2", children: description })
7901
+ ]
7902
+ }
7903
+ ),
7904
+ /* @__PURE__ */ jsx23(
7905
+ "input",
7906
+ {
7907
+ accept,
7908
+ disabled,
7909
+ hidden: true,
7910
+ multiple,
7911
+ onChange: handleInputChange,
7912
+ ref: inputRef,
7913
+ tabIndex: -1,
7914
+ type: "file"
7915
+ }
7916
+ ),
7917
+ selectedFiles !== void 0 && selectedFiles.length > 0 && /* @__PURE__ */ jsx23("ul", { className: styles.list, children: selectedFiles.map((file, index) => /* @__PURE__ */ jsxs13("li", { className: styles.file, children: [
7918
+ /* @__PURE__ */ jsx23(DocumentRegular, { "aria-hidden": true, className: "text-fui-fg2 shrink-0" }),
7919
+ /* @__PURE__ */ jsx23(Text6, { className: styles.fileName, size: 300, title: file.name, children: file.name }),
7920
+ /* @__PURE__ */ jsx23(Text6, { className: "text-fui-fg2 shrink-0", size: 200, children: formatFileSize(file.size) }),
7921
+ onRemove && /* @__PURE__ */ jsx23(
7922
+ Button8,
7923
+ {
7924
+ appearance: "subtle",
7925
+ "aria-label": `${t2("fileDropZone.remove")}: ${file.name}`,
7926
+ icon: /* @__PURE__ */ jsx23(DismissRegular3, {}),
7927
+ onClick: () => onRemove(file, index),
7928
+ size: "small"
7929
+ }
7930
+ )
7931
+ ] }, `${file.name}:${file.size}:${file.lastModified}:${index}`)) })
7932
+ ] });
7933
+ }
7934
+ function FilePickerButton({ accept, children, disabled, multiple, onClick, onFiles, ...rest }) {
7935
+ const { t: t2 } = useTranslation();
7936
+ const inputRef = useRef5(null);
7937
+ const buttonProps = {
7938
+ ...rest,
7939
+ disabled,
7940
+ onClick: (event) => {
7941
+ onClick?.(event);
7942
+ if (!event.defaultPrevented) inputRef.current?.click();
7943
+ }
7944
+ };
7945
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7946
+ /* @__PURE__ */ jsx23(Button8, { ...buttonProps, children: children ?? t2("fileDropZone.browse") }),
7947
+ /* @__PURE__ */ jsx23(
7948
+ "input",
7949
+ {
7950
+ accept,
7951
+ disabled,
7952
+ hidden: true,
7953
+ multiple,
7954
+ onChange: (event) => {
7955
+ const files = Array.from(event.target.files ?? []);
7956
+ event.target.value = "";
7957
+ if (files.length > 0) onFiles(files);
7958
+ },
7959
+ ref: inputRef,
7960
+ tabIndex: -1,
7961
+ type: "file"
7962
+ }
7963
+ )
7964
+ ] });
7965
+ }
7966
+
7967
+ // src/components/ui/status-badge.tsx
7968
+ import { jsx as jsx24 } from "react/jsx-runtime";
7969
+ var { Badge, makeStyles: makeStyles10, mergeClasses: mergeClasses14 } = fluentComponents;
7970
+ var TONE_HUES = {
7971
+ accent: { light: "#0078d4", dark: "#4cc2ff" },
7972
+ danger: { light: "#c42b1c", dark: "#ff99a4" },
7973
+ neutral: { light: "#8a8a8a", dark: "#9a9a9a" },
7974
+ success: { light: "#0f7b0f", dark: "#6ccb5f" },
7975
+ warning: { light: "#9d5d00", dark: "#fce100" }
7976
+ };
7977
+ var CHIP_ROOT_PADDING = "5px";
7978
+ var LABEL_INSET = `calc(var(--strokeWidthThin) + ${CHIP_ROOT_PADDING} + var(--spacingHorizontalXXS))`;
7979
+ var useStyles10 = makeStyles10({
7980
+ root: {
7981
+ paddingLeft: LABEL_INSET,
7982
+ paddingRight: LABEL_INSET
7983
+ }
7984
+ });
7985
+ function StatusBadge({ children, className, tone }) {
7986
+ const hue = useBadgeHue(TONE_HUES[tone]);
7987
+ const styles = useStyles10();
7988
+ return /* @__PURE__ */ jsx24(Badge, { appearance: "tint", className: mergeClasses14(styles.root, hue.className, className), size: "large", style: hue.style, children });
7989
+ }
7990
+
7991
+ // src/components/ui/http-badge.tsx
7992
+ import { jsx as jsx25 } from "react/jsx-runtime";
7993
+ var { makeStyles: makeStyles11 } = fluentComponents;
7994
+ var useStyles11 = makeStyles11({
7995
+ root: {
7996
+ fontFamily: "var(--fontFamilyMonospace)",
7997
+ minWidth: "48px",
7998
+ paddingTop: "1.5px"
7999
+ }
8000
+ });
8001
+ function HttpBadge({ children, tone }) {
8002
+ const styles = useStyles11();
8003
+ return /* @__PURE__ */ jsx25(StatusBadge, { className: styles.root, tone, children: /* @__PURE__ */ jsx25("span", { translate: "no", children }) });
8004
+ }
8005
+ function HttpMethodBadge({ method }) {
8006
+ return /* @__PURE__ */ jsx25(HttpBadge, { tone: method === "GET" ? "accent" : method === "POST" ? "success" : "neutral", children: method });
8007
+ }
8008
+ function HttpStatusBadge({ children, severity }) {
8009
+ return /* @__PURE__ */ jsx25(HttpBadge, { tone: severity === "error" ? "danger" : severity, children });
8010
+ }
8011
+
8012
+ // src/components/ui/info-label.tsx
8013
+ import { jsx as jsx26 } from "react/jsx-runtime";
8014
+ var { InfoLabel } = fluentComponents;
8015
+ var infoLabelSlot = (label, info) => (_2, slotProps2) => /* @__PURE__ */ jsx26(InfoLabel, { ...slotProps2, info, children: label });
8016
+
8017
+ // src/components/ui/loading-screen.tsx
8018
+ import { jsx as jsx27 } from "react/jsx-runtime";
8019
+ var { Spinner: Spinner3 } = fluentComponents;
8020
+ function AppLoadingScreen({ label }) {
8021
+ return /* @__PURE__ */ jsx27("main", { className: "fwt-loading fwt-loading-app", children: /* @__PURE__ */ jsx27(Spinner3, { label }) });
8022
+ }
8023
+ function ContentLoadingScreen({ label }) {
8024
+ return /* @__PURE__ */ jsx27("div", { className: "fwt-loading", children: /* @__PURE__ */ jsx27(Spinner3, { label }) });
8025
+ }
8026
+
8027
+ // src/components/ui/masked-icon.tsx
8028
+ import { jsx as jsx28 } from "react/jsx-runtime";
8029
+ var { mergeClasses: mergeClasses15 } = fluentComponents;
8030
+ function MaskedIcon({ className, maskSize = "contain", url }) {
8031
+ const style = {
8032
+ backgroundColor: "currentColor",
8033
+ maskImage: `url(${url})`,
8034
+ WebkitMaskImage: `url(${url})`,
8035
+ maskSize,
8036
+ WebkitMaskSize: maskSize,
8037
+ maskRepeat: "no-repeat",
8038
+ WebkitMaskRepeat: "no-repeat",
8039
+ maskPosition: "center",
8040
+ WebkitMaskPosition: "center"
8041
+ };
8042
+ return /* @__PURE__ */ jsx28("span", { "aria-hidden": true, className: mergeClasses15("block flex-none", className), style });
8043
+ }
8044
+
8045
+ // src/components/ui/metric.tsx
8046
+ import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
8047
+ var { makeStyles: makeStyles12, mergeClasses: mergeClasses16 } = fluentComponents;
8048
+ var useStyles12 = makeStyles12({
8049
+ root: {
8050
+ display: "grid",
8051
+ minWidth: 0,
8052
+ rowGap: "4px"
8053
+ },
8054
+ label: {
8055
+ color: "var(--winui-text-fill-secondary)",
8056
+ fontSize: "var(--fontSizeBase200)",
8057
+ lineHeight: "var(--lineHeightBase200)"
8058
+ },
8059
+ value: {
8060
+ alignItems: "baseline",
8061
+ color: "var(--winui-text-fill-primary)",
8062
+ columnGap: "4px",
8063
+ display: "flex",
8064
+ fontVariantNumeric: "tabular-nums",
8065
+ fontWeight: "var(--fontWeightSemibold)",
8066
+ minWidth: 0
8067
+ },
8068
+ title: {
8069
+ fontSize: "var(--fontSizeHero700)",
8070
+ lineHeight: "var(--lineHeightHero700)"
8071
+ },
8072
+ subtitle: {
8073
+ fontSize: "var(--fontSizeBase500)",
8074
+ lineHeight: "var(--lineHeightBase500)"
8075
+ },
8076
+ // A prefix or suffix is a unit or a sign, not part of the figure: one step
8077
+ // down the ramp and the secondary fill, so the figure stays the loudest thing.
8078
+ affix: {
8079
+ color: "var(--winui-text-fill-secondary)",
8080
+ fontSize: "var(--fontSizeBase300)",
8081
+ fontWeight: "var(--fontWeightRegular)",
8082
+ lineHeight: "var(--lineHeightBase300)"
8083
+ },
8084
+ accent: { color: "var(--winui-accent-text-fill-tertiary)" },
8085
+ success: { color: "var(--winui-system-fill-success)" },
8086
+ warning: { color: "var(--winui-system-fill-caution)" },
8087
+ danger: { color: "var(--winui-system-fill-critical)" },
8088
+ grid: {
8089
+ display: "grid",
8090
+ gap: "var(--fwt-metric-gap, 16px)",
8091
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(var(--fwt-metric-min-width, 160px), 100%), 1fr))"
8092
+ }
8093
+ });
8094
+ var formatMetricValue = (value, precision, locale) => typeof value === "number" ? new Intl.NumberFormat(locale, precision === void 0 ? void 0 : { minimumFractionDigits: precision, maximumFractionDigits: precision }).format(value) : value;
8095
+ function Metric({
8096
+ className,
8097
+ formatter,
8098
+ label,
8099
+ locale,
8100
+ precision,
8101
+ prefix,
8102
+ size = "title",
8103
+ suffix,
8104
+ tone = "default",
8105
+ value
8106
+ }) {
8107
+ const styles = useStyles12();
8108
+ const hasValue = value !== null && value !== void 0;
8109
+ const text = hasValue ? formatter ? formatter(value) : formatMetricValue(value, precision, locale) : "\u2014";
8110
+ return /* @__PURE__ */ jsxs14("div", { className: mergeClasses16(styles.root, className), children: [
8111
+ /* @__PURE__ */ jsx29("span", { className: styles.label, children: label }),
8112
+ /* @__PURE__ */ jsxs14("div", { className: mergeClasses16(styles.value, styles[size], tone !== "default" && styles[tone]), children: [
8113
+ prefix !== void 0 && /* @__PURE__ */ jsx29("span", { className: styles.affix, children: prefix }),
8114
+ /* @__PURE__ */ jsx29("span", { children: text }),
8115
+ suffix !== void 0 && /* @__PURE__ */ jsx29("span", { className: styles.affix, children: suffix })
8116
+ ] })
8117
+ ] });
8118
+ }
8119
+ function MetricGrid({ children, className, gap, minItemWidth }) {
8120
+ const styles = useStyles12();
8121
+ const style = {
8122
+ ...minItemWidth !== void 0 && { "--fwt-metric-min-width": typeof minItemWidth === "number" ? `${minItemWidth}px` : minItemWidth },
8123
+ ...gap !== void 0 && { "--fwt-metric-gap": typeof gap === "number" ? `${gap}px` : gap }
8124
+ };
8125
+ return /* @__PURE__ */ jsx29("div", { className: mergeClasses16(styles.grid, className), style, children });
8126
+ }
8127
+
8128
+ // src/components/ui/multiselect-combobox.tsx
8129
+ import { useState as useState8 } from "react";
8130
+ import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
8131
+ var { Option: Option3 } = fluentComponents;
8132
+ var sameValues = (left, right) => left.length === right.length && new Set(right).size === right.length && left.every((entry) => right.includes(entry));
8133
+ function MultiselectCombobox({
8134
+ ariaLabel,
8135
+ className,
8136
+ clearLabel,
8137
+ closedLabel = "",
8138
+ disabled = false,
8139
+ freeform = false,
8140
+ normalizeValue = (entry) => entry,
8141
+ onChange,
8142
+ options,
8143
+ placeholder,
8144
+ readOnly,
8145
+ renderOption,
8146
+ value
8147
+ }) {
8148
+ const [open, setOpen] = useState8(false);
8149
+ const [query, setQuery] = useState8("");
8150
+ const needle = query.trim().toLowerCase();
8151
+ const optionValues = new Set(options.map((option) => option.value));
8152
+ const completeOptions = [
8153
+ ...options,
8154
+ ...[...new Set(value)].filter((entry) => !optionValues.has(entry)).map((entry) => ({ value: entry, label: entry }))
8155
+ ];
8156
+ const visible = completeOptions.filter((option) => option.label.toLowerCase().includes(needle));
8157
+ const commit = (next) => {
8158
+ const normalized = [...new Set(next.map(normalizeValue).filter(Boolean))];
8159
+ if (!sameValues(normalized, value)) onChange(normalized);
8160
+ setQuery("");
8161
+ };
8162
+ return /* @__PURE__ */ jsxs15(
8163
+ Combobox,
8164
+ {
8165
+ "aria-label": ariaLabel,
8166
+ className,
8167
+ disabled,
8168
+ freeform,
8169
+ multiselect: true,
8170
+ onChange: (event) => setQuery(event.target.value),
8171
+ onKeyDown: freeform ? (event) => {
8172
+ if (event.key !== "Enter" || query.trim() === "") return;
8173
+ event.preventDefault();
8174
+ commit([...value, query]);
8175
+ } : void 0,
8176
+ onOpenChange: (_2, data) => {
8177
+ setOpen(data.open);
8178
+ setQuery("");
8179
+ },
8180
+ onOptionSelect: (_2, data) => commit(data.optionValue === "" ? [] : data.selectedOptions),
8181
+ placeholder,
8182
+ readOnly,
8183
+ selectedOptions: clearLabel && value.length === 0 ? [""] : [...value],
8184
+ value: open ? query : closedLabel,
8185
+ children: [
8186
+ clearLabel && needle === "" && /* @__PURE__ */ jsx30(Option3, { text: clearLabel, value: "", children: clearLabel }),
8187
+ visible.map((option) => /* @__PURE__ */ jsx30(Option3, { text: option.label, value: option.value, children: renderOption ? renderOption(option) : option.label }, option.value))
8188
+ ]
8189
+ }
8190
+ );
8191
+ }
8192
+ var valuesAsOptions = (values) => values.map((value) => ({ value, label: value }));
8193
+
8194
+ // src/components/ui/number-box.tsx
8195
+ import { forwardRef as forwardRef4, isValidElement } from "react";
8196
+ import { jsx as jsx31 } from "react/jsx-runtime";
8197
+ var { SpinButton } = fluentComponents;
8198
+ var stepPrecision = (step) => {
8199
+ const text = String(step);
8200
+ const exponent = /e-(\d+)$/.exec(text);
8201
+ if (exponent) return Number(exponent[1]);
8202
+ return text.split(".")[1]?.length ?? 0;
8203
+ };
8204
+ var THOUSANDS_GROUPED = /^-?\d{1,3}(,\d{3})+(\.\d+)?$/;
8205
+ var parseNumberText = (text) => {
8206
+ const trimmed = text.trim().replace(/−/g, "-").replace(/\s/g, "");
8207
+ if (trimmed === "") return null;
8208
+ const normalized = THOUSANDS_GROUPED.test(trimmed) ? trimmed.replace(/,/g, "") : /^-?\d*,\d+$/.test(trimmed) ? trimmed.replace(",", ".") : trimmed;
8209
+ const parsed = Number(normalized);
8210
+ return Number.isFinite(parsed) ? parsed : void 0;
8211
+ };
8212
+ var clampNumber = (value, { min, max, precision }) => {
8213
+ let next = value;
8214
+ if (max !== void 0 && next > max) next = max;
8215
+ if (min !== void 0 && next < min) next = min;
8216
+ return precision === void 0 ? next : Number(next.toFixed(Math.min(Math.max(precision, 0), 100)));
8217
+ };
8218
+ var resolveNumberChange = (data, bounds) => {
8219
+ const raw = data.value !== void 0 ? data.value : data.displayValue !== void 0 ? parseNumberText(data.displayValue) : void 0;
8220
+ return typeof raw === "number" ? clampNumber(raw, bounds) : raw;
8221
+ };
8222
+ var labelSlot = (slot, label) => {
8223
+ if (slot === void 0) return { "aria-label": label };
8224
+ if (slot === null || typeof slot !== "object" || isValidElement(slot) || Symbol.iterator in slot) return slot;
8225
+ return { "aria-label": label, ...slot };
8226
+ };
8227
+ var NumberBox = forwardRef4(function NumberBox2({ value, onChange, min, max, step, precision, decrementButton, incrementButton, ...rest }, ref) {
8228
+ const { t: t2 } = useTranslation();
8229
+ const bounds = { min, max, precision: precision ?? stepPrecision(step ?? 1) };
8230
+ const handleChange = (_event, data) => {
8231
+ const next = resolveNumberChange(data, bounds);
8232
+ if (next !== void 0) onChange(next);
8233
+ };
8234
+ return /* @__PURE__ */ jsx31(
8235
+ SpinButton,
8236
+ {
8237
+ ...rest,
8238
+ decrementButton: labelSlot(decrementButton, t2("numberBox.decrement")),
8239
+ incrementButton: labelSlot(incrementButton, t2("numberBox.increment")),
8240
+ max,
8241
+ min,
8242
+ onChange: handleChange,
8243
+ precision,
8244
+ ref,
8245
+ step,
8246
+ value
8247
+ }
8248
+ );
8249
+ });
8250
+
8251
+ // src/components/ui/open-link-label.tsx
8252
+ import { Open16Regular } from "@fluentui/react-icons";
8253
+ import { jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
8254
+ function OpenLinkLabel({ children }) {
8255
+ return /* @__PURE__ */ jsxs16("span", { className: "inline-flex items-center gap-1", children: [
8256
+ children,
8257
+ /* @__PURE__ */ jsx32(Open16Regular, { "aria-hidden": true, className: "block flex-none" })
8258
+ ] });
8259
+ }
8260
+
8261
+ // src/components/ui/outcome-toast.tsx
8262
+ import { createContext as createContext3, useCallback as useCallback5, useContext as useContext3, useId as useId3, useMemo as useMemo4, useRef as useRef6 } from "react";
8263
+ import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
8264
+ var { Spinner: Spinner4, Toast, Toaster, ToastTitle, useToastController } = fluentComponents;
7367
8265
  var TOAST_DISMISS_MS = 3e3;
7368
8266
  var OutcomeToastContext = createContext3(null);
8267
+ var ToastApiContext = createContext3(null);
7369
8268
  function OutcomeToastProvider({ children }) {
7370
- const toasterId = useId2();
7371
- const sequence = useRef5(0);
8269
+ return useContext3(OutcomeToastContext) ? children : /* @__PURE__ */ jsx33(ToasterHost, { children });
8270
+ }
8271
+ function ToasterHost({ children }) {
8272
+ const toasterId = useId3();
8273
+ const sequence = useRef6(0);
7372
8274
  const { dispatchToast, dismissToast, updateToast } = useToastController(toasterId);
7373
- const toastFor = useCallback4((toastId, message, pending) => /* @__PURE__ */ jsx25(Toast, { className: "cursor-pointer", onClick: () => dismissToast(toastId), children: /* @__PURE__ */ jsx25(ToastTitle, { media: pending ? /* @__PURE__ */ jsx25(Spinner2, { size: "tiny" }) : void 0, children: message }) }), [dismissToast]);
7374
- const nextToastId = useCallback4(() => `${toasterId}-${sequence.current++}`, [toasterId]);
7375
- const succeed = useCallback4((message) => {
8275
+ const toastFor = useCallback5((toastId, message, pending) => /* @__PURE__ */ jsx33(Toast, { className: "cursor-pointer", onClick: () => dismissToast(toastId), children: /* @__PURE__ */ jsx33(ToastTitle, { media: pending ? /* @__PURE__ */ jsx33(Spinner4, { size: "tiny" }) : void 0, children: message }) }), [dismissToast]);
8276
+ const nextToastId = useCallback5(() => `${toasterId}-${sequence.current++}`, [toasterId]);
8277
+ const succeed = useCallback5((message) => {
7376
8278
  const toastId = nextToastId();
7377
8279
  dispatchToast(toastFor(toastId, message, false), { intent: "success", toastId, timeout: TOAST_DISMISS_MS });
7378
8280
  }, [dispatchToast, nextToastId, toastFor]);
7379
- const start = useCallback4((pending) => {
8281
+ const start = useCallback5((pending) => {
7380
8282
  const toastId = nextToastId();
7381
8283
  dispatchToast(toastFor(toastId, pending, true), { toastId, timeout: -1 });
7382
8284
  return {
@@ -7389,21 +8291,37 @@ function OutcomeToastProvider({ children }) {
7389
8291
  settle: () => dismissToast(toastId)
7390
8292
  };
7391
8293
  }, [dismissToast, dispatchToast, nextToastId, toastFor, updateToast]);
7392
- const value = useMemo3(() => ({ start, succeed }), [start, succeed]);
7393
- return /* @__PURE__ */ jsxs12(OutcomeToastContext.Provider, { value, children: [
7394
- /* @__PURE__ */ jsx25(Toaster, { toasterId, position: "top-end" }),
8294
+ const value = useMemo4(() => ({ start, succeed }), [start, succeed]);
8295
+ const notify = useCallback5((intent, text, options) => {
8296
+ const toastId = nextToastId();
8297
+ dispatchToast(toastFor(toastId, text, false), { intent, toastId, timeout: options?.timeout ?? TOAST_DISMISS_MS });
8298
+ return () => dismissToast(toastId);
8299
+ }, [dismissToast, dispatchToast, nextToastId, toastFor]);
8300
+ const api = useMemo4(() => ({
8301
+ success: (text, options) => notify("success", text, options),
8302
+ error: (text, options) => notify("error", text, options),
8303
+ warning: (text, options) => notify("warning", text, options),
8304
+ info: (text, options) => notify("info", text, options)
8305
+ }), [notify]);
8306
+ return /* @__PURE__ */ jsx33(OutcomeToastContext.Provider, { value, children: /* @__PURE__ */ jsxs17(ToastApiContext.Provider, { value: api, children: [
8307
+ /* @__PURE__ */ jsx33(Toaster, { toasterId, position: "top-end" }),
7395
8308
  children
7396
- ] });
8309
+ ] }) });
7397
8310
  }
7398
8311
  var useOutcomeToasts = () => {
7399
8312
  const value = useContext3(OutcomeToastContext);
7400
8313
  if (!value) throw new Error("useOutcomeToasts requires an OutcomeToastProvider above it");
7401
8314
  return value;
7402
8315
  };
8316
+ var useToast = () => {
8317
+ const value = useContext3(ToastApiContext);
8318
+ if (!value) throw new Error("useToast requires an OutcomeToastProvider above it");
8319
+ return value;
8320
+ };
7403
8321
 
7404
8322
  // src/components/ui/panel.tsx
7405
- import { jsx as jsx26 } from "react/jsx-runtime";
7406
- var { Card, mergeClasses: mergeClasses11 } = fluentComponents;
8323
+ import { jsx as jsx34 } from "react/jsx-runtime";
8324
+ var { Card, mergeClasses: mergeClasses17 } = fluentComponents;
7407
8325
  var PANEL_INSET_CLASS = "p-[var(--fwt-panel-inset)]";
7408
8326
  var PANEL_BAND_CLASS = "px-[var(--fwt-panel-inset)] py-[var(--spacingVerticalSNudge)]";
7409
8327
  var PADDING_CLASS = {
@@ -7411,12 +8329,127 @@ var PADDING_CLASS = {
7411
8329
  flush: "!p-0"
7412
8330
  };
7413
8331
  function Panel({ className, padding = "content", ...props }) {
7414
- return /* @__PURE__ */ jsx26(Card, { ...props, className: mergeClasses11(PADDING_CLASS[padding], className) });
8332
+ return /* @__PURE__ */ jsx34(Card, { ...props, className: mergeClasses17(PADDING_CLASS[padding], className) });
8333
+ }
8334
+
8335
+ // src/components/ui/property-list.tsx
8336
+ import { jsx as jsx35, jsxs as jsxs18 } from "react/jsx-runtime";
8337
+ var { makeStyles: makeStyles13, mergeClasses: mergeClasses18, shorthands: shorthands2 } = fluentComponents;
8338
+ var NARROW = "@container fwt-property-list (width < 480px)";
8339
+ var MEDIUM = "@container fwt-property-list (480px <= width < 720px)";
8340
+ var useStyles13 = makeStyles13({
8341
+ container: {
8342
+ containerName: "fwt-property-list",
8343
+ containerType: "inline-size",
8344
+ minWidth: 0
8345
+ },
8346
+ // Cell lines are each cell's own right and bottom edge. The list is pulled
8347
+ // 1px past the frame on those two sides so the last column's and last row's
8348
+ // lines fall under the frame's clip and the frame's own border stands in.
8349
+ frame: {
8350
+ ...shorthands2.border("1px", "solid", "var(--winui-divider-stroke-default)"),
8351
+ borderRadius: "var(--winui-control-corner-radius)",
8352
+ overflow: "hidden"
8353
+ },
8354
+ list: {
8355
+ display: "grid",
8356
+ gridTemplateColumns: "repeat(var(--fwt-property-columns, 2), minmax(0, 1fr))",
8357
+ margin: 0,
8358
+ columnGap: "24px",
8359
+ rowGap: "12px",
8360
+ [NARROW]: { gridTemplateColumns: "minmax(0, 1fr)" }
8361
+ },
8362
+ listWrapsToTwo: {
8363
+ [MEDIUM]: { gridTemplateColumns: "repeat(2, minmax(0, 1fr))" }
8364
+ },
8365
+ listBordered: {
8366
+ columnGap: 0,
8367
+ rowGap: 0,
8368
+ marginRight: "-1px",
8369
+ marginBottom: "-1px"
8370
+ },
8371
+ item: {
8372
+ display: "grid",
8373
+ gridColumn: "span var(--fwt-span, 1)",
8374
+ minWidth: 0,
8375
+ rowGap: "2px",
8376
+ [NARROW]: { "&[data-wide]": { gridColumn: "1 / -1" } }
8377
+ },
8378
+ itemWrapsToTwo: {
8379
+ [MEDIUM]: { "&[data-wide]": { gridColumn: "1 / -1" } }
8380
+ },
8381
+ itemBordered: {
8382
+ borderBottom: "1px solid var(--winui-divider-stroke-default)",
8383
+ borderRight: "1px solid var(--winui-divider-stroke-default)",
8384
+ gridTemplateColumns: "var(--fwt-property-label-width, 120px) minmax(0, 1fr)",
8385
+ rowGap: 0
8386
+ },
8387
+ label: {
8388
+ color: "var(--winui-text-fill-secondary)",
8389
+ fontSize: "var(--fontSizeBase200)",
8390
+ lineHeight: "var(--lineHeightBase200)",
8391
+ margin: 0,
8392
+ minWidth: 0
8393
+ },
8394
+ value: {
8395
+ color: "var(--winui-text-fill-primary)",
8396
+ fontSize: "var(--fontSizeBase300)",
8397
+ lineHeight: "var(--lineHeightBase300)",
8398
+ margin: 0,
8399
+ minWidth: 0,
8400
+ overflowWrap: "anywhere"
8401
+ },
8402
+ labelBordered: {
8403
+ backgroundColor: "var(--winui-card-background-fill-secondary)",
8404
+ borderRight: "1px solid var(--winui-divider-stroke-default)",
8405
+ fontSize: "var(--fontSizeBase300)",
8406
+ lineHeight: "var(--lineHeightBase300)",
8407
+ padding: "8px 12px"
8408
+ },
8409
+ valueBordered: {
8410
+ backgroundColor: "var(--winui-card-background-fill-default)",
8411
+ padding: "8px 12px"
8412
+ }
8413
+ });
8414
+ function PropertyList({ bordered, className, columns = 2, items }) {
8415
+ const styles = useStyles13();
8416
+ const columnCount = Math.max(1, Math.floor(columns));
8417
+ const stepsDown = columnCount > 2;
8418
+ const list = /* @__PURE__ */ jsx35(
8419
+ "dl",
8420
+ {
8421
+ className: mergeClasses18(styles.list, stepsDown && styles.listWrapsToTwo, bordered && styles.listBordered),
8422
+ children: items.map((item, index) => {
8423
+ const span = Math.min(Math.max(1, Math.floor(item.span ?? 1)), columnCount);
8424
+ return /* @__PURE__ */ jsxs18(
8425
+ "div",
8426
+ {
8427
+ className: mergeClasses18(styles.item, stepsDown && styles.itemWrapsToTwo, bordered && styles.itemBordered),
8428
+ "data-wide": span > 1 ? "" : void 0,
8429
+ style: { "--fwt-span": span },
8430
+ children: [
8431
+ /* @__PURE__ */ jsx35("dt", { className: mergeClasses18(styles.label, bordered && styles.labelBordered), children: item.label }),
8432
+ /* @__PURE__ */ jsx35("dd", { className: mergeClasses18(styles.value, bordered && styles.valueBordered), children: item.value })
8433
+ ]
8434
+ },
8435
+ item.key ?? index
8436
+ );
8437
+ })
8438
+ }
8439
+ );
8440
+ return /* @__PURE__ */ jsx35(
8441
+ "div",
8442
+ {
8443
+ className: mergeClasses18(styles.container, bordered && styles.frame, className),
8444
+ style: { "--fwt-property-columns": columnCount },
8445
+ children: list
8446
+ }
8447
+ );
7415
8448
  }
7416
8449
 
7417
8450
  // src/components/ui/progress-ring.tsx
7418
- import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
7419
- var { makeStyles: makeStyles10, mergeClasses: mergeClasses12 } = fluentComponents;
8451
+ import { jsx as jsx36, jsxs as jsxs19 } from "react/jsx-runtime";
8452
+ var { makeStyles: makeStyles14, mergeClasses: mergeClasses19 } = fluentComponents;
7420
8453
  var DIAMETER = 32;
7421
8454
  var STROKE = 4;
7422
8455
  var RADIUS = (DIAMETER - STROKE) / 2;
@@ -7427,7 +8460,7 @@ var TONE_STROKE = {
7427
8460
  caution: "var(--winui-system-fill-caution)",
7428
8461
  critical: "var(--winui-system-fill-critical)"
7429
8462
  };
7430
- var useStyles10 = makeStyles10({
8463
+ var useStyles14 = makeStyles14({
7431
8464
  // A forced-colours palette repaints background and border colours but not an
7432
8465
  // SVG stroke, so each ring names its system colour itself: Highlight is what
7433
8466
  // HighContrast gives ProgressRingForegroundThemeBrush through
@@ -7446,19 +8479,19 @@ var useStyles10 = makeStyles10({
7446
8479
  }
7447
8480
  });
7448
8481
  function ProgressRing({ className, percent, size = 16, tone }) {
7449
- const styles = useStyles10();
8482
+ const styles = useStyles14();
7450
8483
  const filled = Math.max(0, Math.min(100, percent)) / 100;
7451
- return /* @__PURE__ */ jsxs13(
8484
+ return /* @__PURE__ */ jsxs19(
7452
8485
  "svg",
7453
8486
  {
7454
8487
  "aria-hidden": true,
7455
- className: mergeClasses12("block flex-none", className),
8488
+ className: mergeClasses19("block flex-none", className),
7456
8489
  height: size,
7457
8490
  style: { transform: "translateY(calc(50% - 0.5cap))" },
7458
8491
  viewBox: `0 0 ${DIAMETER} ${DIAMETER}`,
7459
8492
  width: size,
7460
8493
  children: [
7461
- /* @__PURE__ */ jsx27(
8494
+ /* @__PURE__ */ jsx36(
7462
8495
  "circle",
7463
8496
  {
7464
8497
  className: styles.track,
@@ -7470,7 +8503,7 @@ function ProgressRing({ className, percent, size = 16, tone }) {
7470
8503
  strokeWidth: TRACK_STROKE
7471
8504
  }
7472
8505
  ),
7473
- /* @__PURE__ */ jsx27(
8506
+ /* @__PURE__ */ jsx36(
7474
8507
  "circle",
7475
8508
  {
7476
8509
  className: styles.arc,
@@ -7492,9 +8525,9 @@ function ProgressRing({ className, percent, size = 16, tone }) {
7492
8525
 
7493
8526
  // src/components/ui/reorder-list.tsx
7494
8527
  import { ReOrderDotsVerticalRegular } from "@fluentui/react-icons";
7495
- import { useEffect as useEffect5, useMemo as useMemo4, useRef as useRef6, useState as useState6 } from "react";
7496
- import { jsx as jsx28 } from "react/jsx-runtime";
7497
- var { Button: Button5, makeStyles: makeStyles11, mergeClasses: mergeClasses13 } = fluentComponents;
8528
+ import { useEffect as useEffect5, useMemo as useMemo5, useRef as useRef7, useState as useState9 } from "react";
8529
+ import { jsx as jsx37 } from "react/jsx-runtime";
8530
+ var { Button: Button9, makeStyles: makeStyles15, mergeClasses: mergeClasses20 } = fluentComponents;
7498
8531
  var REORDER_OPACITY = 0.8;
7499
8532
  var DRAG_RETURN_MS = 200;
7500
8533
  var LIST_ATTRIBUTE = "data-reorder-list";
@@ -7504,7 +8537,7 @@ var EDGE_ATTRIBUTE = "data-reorder-edge";
7504
8537
  var HANDLE_ATTRIBUTE = "data-reorder-handle";
7505
8538
  var DRAG_Y = "--fwt-reorder-drag-y";
7506
8539
  var TRACKING_TRANSFORM = { transform: `translateY(var(${DRAG_Y}, 0px))` };
7507
- var useStyles11 = makeStyles11({
8540
+ var useStyles15 = makeStyles15({
7508
8541
  // A drag that starts on the handle travels over the rows, and without this
7509
8542
  // the browser reads that travel as a text selection across them.
7510
8543
  list: { [`&[${REORDERING_ATTRIBUTE}]`]: { userSelect: "none" } },
@@ -7593,11 +8626,11 @@ var siblingHandles = (handle) => {
7593
8626
  return [...list.querySelectorAll(`[${HANDLE_ATTRIBUTE}]`)].filter((element) => element.closest(`[${LIST_ATTRIBUTE}]`) === list && !element.hasAttribute("disabled"));
7594
8627
  };
7595
8628
  function useReorderList({ busy = false, disabled = false, length, onDrop, onDropError, onReorder }) {
7596
- const styles = useStyles11();
7597
- const gestureRef = useRef6(null);
7598
- const settleRef = useRef6(void 0);
7599
- const mountedRef = useRef6(true);
7600
- const [preview, setPreview] = useState6(null);
8629
+ const styles = useStyles15();
8630
+ const gestureRef = useRef7(null);
8631
+ const settleRef = useRef7(void 0);
8632
+ const mountedRef = useRef7(true);
8633
+ const [preview, setPreview] = useState9(null);
7601
8634
  const locked = busy || disabled || length < 2;
7602
8635
  const live = preview !== null && preview.from < length && preview.to < length ? preview : null;
7603
8636
  useEffect5(() => {
@@ -7607,8 +8640,8 @@ function useReorderList({ busy = false, disabled = false, length, onDrop, onDrop
7607
8640
  clearTimeout(settleRef.current);
7608
8641
  };
7609
8642
  }, []);
7610
- const offsets = useMemo4(() => live && slotOffsets(live.items, live.from, live.to), [live]);
7611
- const ranks = useMemo4(
8643
+ const offsets = useMemo5(() => live && slotOffsets(live.items, live.from, live.to), [live]);
8644
+ const ranks = useMemo5(
7612
8645
  () => live && moveItem(Array.from({ length }, (_2, index) => index), live.from, live.to),
7613
8646
  [length, live]
7614
8647
  );
@@ -7737,7 +8770,7 @@ function useReorderList({ busy = false, disabled = false, length, onDrop, onDrop
7737
8770
  return {
7738
8771
  [ITEM_ATTRIBUTE]: String(index),
7739
8772
  [EDGE_ATTRIBUTE]: !live ? void 0 : rank === 0 ? "first" : rank === length - 1 ? "last" : void 0,
7740
- className: mergeClasses13(
8773
+ className: mergeClasses20(
7741
8774
  grabbed && styles.lifted,
7742
8775
  // The row under the pointer is not travelling toward anything -- it is
7743
8776
  // where the pointer put it -- so it wears the drag opacity flat, and
@@ -7749,20 +8782,20 @@ function useReorderList({ busy = false, disabled = false, length, onDrop, onDrop
7749
8782
  style: !live ? void 0 : grabbed ? TRACKING_TRANSFORM : { transform: `translateY(${offsets?.get(index) ?? 0}px)` }
7750
8783
  };
7751
8784
  },
7752
- listProps: (className) => ({ [LIST_ATTRIBUTE]: "", className: mergeClasses13(styles.list, className), onKeyDown }),
8785
+ listProps: (className) => ({ [LIST_ATTRIBUTE]: "", className: mergeClasses20(styles.list, className), onKeyDown }),
7753
8786
  position
7754
8787
  };
7755
8788
  }
7756
8789
  function ReorderHandle({ label, ...gesture }) {
7757
- const styles = useStyles11();
7758
- return /* @__PURE__ */ jsx28(
7759
- Button5,
8790
+ const styles = useStyles15();
8791
+ return /* @__PURE__ */ jsx37(
8792
+ Button9,
7760
8793
  {
7761
8794
  appearance: "subtle",
7762
8795
  "aria-keyshortcuts": `${REORDER_CHORD}+ArrowUp ${REORDER_CHORD}+ArrowDown`,
7763
8796
  "aria-label": label,
7764
8797
  className: styles.handle,
7765
- icon: /* @__PURE__ */ jsx28(ReOrderDotsVerticalRegular, {}),
8798
+ icon: /* @__PURE__ */ jsx37(ReOrderDotsVerticalRegular, {}),
7766
8799
  size: "small",
7767
8800
  ...gesture
7768
8801
  }
@@ -7771,12 +8804,12 @@ function ReorderHandle({ label, ...gesture }) {
7771
8804
 
7772
8805
  // src/components/ui/resource-list.tsx
7773
8806
  import { AddRegular, ArrowClockwiseRegular } from "@fluentui/react-icons";
7774
- import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
7775
- var { Button: Button6, Spinner: Spinner3, Tooltip: Tooltip2, makeStyles: makeStyles12, mergeClasses: mergeClasses14 } = fluentComponents;
8807
+ import { jsx as jsx38, jsxs as jsxs20 } from "react/jsx-runtime";
8808
+ var { Button: Button10, Spinner: Spinner5, Tooltip: Tooltip3, makeStyles: makeStyles16, mergeClasses: mergeClasses21 } = fluentComponents;
7776
8809
  var DEFAULT_ROW_HEIGHT = "44px";
7777
8810
  var ROW_HEIGHT = "--fwt-resource-row-height";
7778
8811
  var EDGE_INSET = "var(--fwt-panel-inset)";
7779
- var useStyles12 = makeStyles12({
8812
+ var useStyles16 = makeStyles16({
7780
8813
  table: {
7781
8814
  "& .fui-TableRow": { height: `var(${ROW_HEIGHT})` },
7782
8815
  // By type rather than by position: a DataGrid row carries Tabster's focus
@@ -7809,12 +8842,12 @@ var useStyles12 = makeStyles12({
7809
8842
  emptyState: { padding: EDGE_INSET }
7810
8843
  });
7811
8844
  function ResourceListPanel({ className, rowHeight = DEFAULT_ROW_HEIGHT, style, ...props }) {
7812
- const styles = useStyles12();
7813
- return /* @__PURE__ */ jsx29(
8845
+ const styles = useStyles16();
8846
+ return /* @__PURE__ */ jsx38(
7814
8847
  Panel,
7815
8848
  {
7816
8849
  ...props,
7817
- className: mergeClasses14("grid min-w-0 !gap-0 overflow-hidden", styles.table, className),
8850
+ className: mergeClasses21("grid min-w-0 !gap-0 overflow-hidden", styles.table, className),
7818
8851
  padding: "flush",
7819
8852
  style: { ...style, [ROW_HEIGHT]: rowHeight }
7820
8853
  }
@@ -7823,12 +8856,12 @@ function ResourceListPanel({ className, rowHeight = DEFAULT_ROW_HEIGHT, style, .
7823
8856
  function ResourceListActions(props) {
7824
8857
  const { appearance, createDisabled = false, createLabel, createTrailingIcon, disabled = false, onRefresh, refreshLabel, refreshing = false } = props;
7825
8858
  const busy = disabled || createDisabled || refreshing;
7826
- const createButton = createLabel !== void 0 && /* @__PURE__ */ jsxs14(
7827
- Button6,
8859
+ const createButton = createLabel !== void 0 && /* @__PURE__ */ jsxs20(
8860
+ Button10,
7828
8861
  {
7829
8862
  appearance: "primary",
7830
8863
  disabled: busy,
7831
- icon: /* @__PURE__ */ jsx29(AddRegular, {}),
8864
+ icon: /* @__PURE__ */ jsx38(AddRegular, {}),
7832
8865
  onClick: "onCreate" in props ? props.onCreate : void 0,
7833
8866
  children: [
7834
8867
  createLabel,
@@ -7836,43 +8869,43 @@ function ResourceListActions(props) {
7836
8869
  ]
7837
8870
  }
7838
8871
  );
7839
- return /* @__PURE__ */ jsxs14("div", { "aria-busy": refreshing, className: "flex items-center gap-2 flex-none", children: [
7840
- /* @__PURE__ */ jsx29(Tooltip2, { content: refreshLabel, relationship: "label", children: /* @__PURE__ */ jsx29(
7841
- Button6,
8872
+ return /* @__PURE__ */ jsxs20("div", { "aria-busy": refreshing, className: "flex items-center gap-2 flex-none", children: [
8873
+ /* @__PURE__ */ jsx38(Tooltip3, { content: refreshLabel, relationship: "label", children: /* @__PURE__ */ jsx38(
8874
+ Button10,
7842
8875
  {
7843
8876
  appearance,
7844
8877
  "aria-label": refreshLabel,
7845
8878
  disabled,
7846
8879
  disabledFocusable: refreshing,
7847
- icon: refreshing ? /* @__PURE__ */ jsx29(Spinner3, { size: "tiny" }) : /* @__PURE__ */ jsx29(ArrowClockwiseRegular, {}),
8880
+ icon: refreshing ? /* @__PURE__ */ jsx38(Spinner5, { size: "tiny" }) : /* @__PURE__ */ jsx38(ArrowClockwiseRegular, {}),
7848
8881
  onClick: onRefresh
7849
8882
  }
7850
8883
  ) }),
7851
8884
  createButton && (props.createTrigger === void 0 ? createButton : props.createTrigger(createButton)),
7852
- /* @__PURE__ */ jsx29("span", { "aria-live": "polite", className: "sr-only", children: refreshing ? `${refreshLabel}\u2026` : "" })
8885
+ /* @__PURE__ */ jsx38("span", { "aria-live": "polite", className: "sr-only", children: refreshing ? `${refreshLabel}\u2026` : "" })
7853
8886
  ] });
7854
8887
  }
7855
8888
  function ResourceListEmptyState({ children }) {
7856
- const styles = useStyles12();
7857
- return /* @__PURE__ */ jsx29(EmptyStateLine, { className: styles.emptyState, children });
8889
+ const styles = useStyles16();
8890
+ return /* @__PURE__ */ jsx38(EmptyStateLine, { className: styles.emptyState, children });
7858
8891
  }
7859
8892
 
7860
8893
  // src/components/ui/row-title.tsx
7861
- import { jsx as jsx30 } from "react/jsx-runtime";
7862
- var { mergeClasses: mergeClasses15 } = fluentComponents;
8894
+ import { jsx as jsx39 } from "react/jsx-runtime";
8895
+ var { mergeClasses: mergeClasses22 } = fluentComponents;
7863
8896
  var rowTitleClass = "winui-focus-rect text-fui-fg1 no-underline hover:underline";
7864
8897
  function RowTitleButton({ children, onClick, ...trigger }) {
7865
- return /* @__PURE__ */ jsx30("button", { className: mergeClasses15(rowTitleClass, "block bg-transparent border-0 cursor-pointer leading-[var(--lineHeightBase300)] min-w-0 max-w-full truncate p-0 text-fui-base300 text-left"), onClick, type: "button", ...trigger, children });
8898
+ return /* @__PURE__ */ jsx39("button", { className: mergeClasses22(rowTitleClass, "block bg-transparent border-0 cursor-pointer leading-[var(--lineHeightBase300)] min-w-0 max-w-full truncate p-0 text-fui-base300 text-left"), onClick, type: "button", ...trigger, children });
7866
8899
  }
7867
8900
 
7868
8901
  // src/components/ui/secret-input.tsx
7869
- import { useState as useState7 } from "react";
7870
- import { jsx as jsx31 } from "react/jsx-runtime";
8902
+ import { useState as useState10 } from "react";
8903
+ import { jsx as jsx40 } from "react/jsx-runtime";
7871
8904
  function SecretInput({ onChange, revealed = false, value, ...rest }) {
7872
- const [guardLocked, setGuardLocked] = useState7(true);
8905
+ const [guardLocked, setGuardLocked] = useState10(true);
7873
8906
  const hasValue = String(value ?? "").length > 0;
7874
8907
  const unlock = () => setGuardLocked(false);
7875
- return /* @__PURE__ */ jsx31(
8908
+ return /* @__PURE__ */ jsx40(
7876
8909
  Input,
7877
8910
  {
7878
8911
  ...rest,
@@ -7913,12 +8946,12 @@ function SecretInput({ onChange, revealed = false, value, ...rest }) {
7913
8946
  }
7914
8947
 
7915
8948
  // src/components/ui/section-header.tsx
7916
- import { jsx as jsx32, jsxs as jsxs15 } from "react/jsx-runtime";
7917
- var { InfoButton, Text: Text4, mergeClasses: mergeClasses16 } = fluentComponents;
8949
+ import { jsx as jsx41, jsxs as jsxs21 } from "react/jsx-runtime";
8950
+ var { InfoButton, Text: Text7, mergeClasses: mergeClasses23 } = fluentComponents;
7918
8951
  var TITLE_SIZE = { 2: 500, 3: 400, 4: 300 };
7919
8952
  function SectionHeader({ actions, description, info, level, title, titleId, truncate = false }) {
7920
- const headingText = /* @__PURE__ */ jsx32(
7921
- Text4,
8953
+ const headingText = /* @__PURE__ */ jsx41(
8954
+ Text7,
7922
8955
  {
7923
8956
  as: `h${level}`,
7924
8957
  className: description === void 0 ? "m-0 min-w-0" : "m-0",
@@ -7930,25 +8963,25 @@ function SectionHeader({ actions, description, info, level, title, titleId, trun
7930
8963
  children: title
7931
8964
  }
7932
8965
  );
7933
- const heading = info === void 0 ? headingText : /* @__PURE__ */ jsxs15("div", { className: "inline-flex items-center gap-1 min-w-0", children: [
8966
+ const heading = info === void 0 ? headingText : /* @__PURE__ */ jsxs21("div", { className: "inline-flex items-center gap-1 min-w-0", children: [
7934
8967
  headingText,
7935
- /* @__PURE__ */ jsx32(InfoButton, { info })
8968
+ /* @__PURE__ */ jsx41(InfoButton, { info })
7936
8969
  ] });
7937
- const block = description === void 0 ? heading : /* @__PURE__ */ jsxs15("div", { className: mergeClasses16(TIGHT_STACK_CLASS, "min-w-0"), children: [
8970
+ const block = description === void 0 ? heading : /* @__PURE__ */ jsxs21("div", { className: mergeClasses23(TIGHT_STACK_CLASS, "min-w-0"), children: [
7938
8971
  heading,
7939
- /* @__PURE__ */ jsx32(Text4, { className: "text-fui-fg2", size: 200, children: description })
8972
+ /* @__PURE__ */ jsx41(Text7, { className: "text-fui-fg2", size: 200, children: description })
7940
8973
  ] });
7941
8974
  if (actions === void 0) return block;
7942
- return /* @__PURE__ */ jsxs15("div", { className: mergeClasses16(HEADER_ROW_CLASS, "gap-3"), children: [
8975
+ return /* @__PURE__ */ jsxs21("div", { className: mergeClasses23(HEADER_ROW_CLASS, "gap-3"), children: [
7943
8976
  block,
7944
- /* @__PURE__ */ jsx32("div", { className: "flex items-center gap-2 flex-none", children: actions })
8977
+ /* @__PURE__ */ jsx41("div", { className: "flex items-center gap-2 flex-none", children: actions })
7945
8978
  ] });
7946
8979
  }
7947
8980
 
7948
8981
  // src/components/ui/settings-card.tsx
7949
- import { useId as useId3, useState as useState8 } from "react";
7950
- import { Fragment as Fragment2, jsx as jsx33, jsxs as jsxs16 } from "react/jsx-runtime";
7951
- var { Text: Text5, makeStyles: makeStyles13, mergeClasses: mergeClasses17, shorthands } = fluentComponents;
8982
+ import { useId as useId4, useState as useState11 } from "react";
8983
+ import { Fragment as Fragment3, jsx as jsx42, jsxs as jsxs22 } from "react/jsx-runtime";
8984
+ var { Text: Text8, makeStyles: makeStyles17, mergeClasses: mergeClasses24, shorthands: shorthands3 } = fluentComponents;
7952
8985
  var ICON_MARGIN_START = "2px";
7953
8986
  var ICON_MARGIN_END = "20px";
7954
8987
  var ICON_SIZE = "24px";
@@ -7958,7 +8991,7 @@ var ICON_COLUMN_VAR = "var(--fwt-settings-icon-column, 0px)";
7958
8991
  var WRAPPED = "@container fwt-settings-row (width < 476px)";
7959
8992
  var WRAPPED_WITH_ICON = "@container fwt-settings-row (286px <= width < 476px)";
7960
8993
  var WRAPPED_NO_ICON = "@container fwt-settings-row (width < 286px)";
7961
- var useStyles13 = makeStyles13({
8994
+ var useStyles17 = makeStyles17({
7962
8995
  // Containment sits one level out of the row so the query measures the same
7963
8996
  // box the toolkit's trigger reads (this element carries no padding or
7964
8997
  // border), and so the wrapped state can style the row itself -- a container
@@ -7987,7 +9020,7 @@ var useStyles13 = makeStyles13({
7987
9020
  borderRightWidth: "1px",
7988
9021
  borderBottomWidth: "1px",
7989
9022
  borderLeftWidth: "1px",
7990
- ...shorthands.borderColor("var(--winui-card-stroke-default)"),
9023
+ ...shorthands3.borderColor("var(--winui-card-stroke-default)"),
7991
9024
  borderRadius: "var(--winui-control-corner-radius)",
7992
9025
  boxSizing: "border-box",
7993
9026
  color: "var(--winui-text-fill-primary)",
@@ -8030,21 +9063,19 @@ var useStyles13 = makeStyles13({
8030
9063
  // and the dark one does not.
8031
9064
  // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/CommonStyles/Common_themeresources_any.xaml#L382-L390
8032
9065
  // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/CommonStyles/Common_themeresources_any.xaml#L186-L191
8033
- borderTopColor: "var(--winui-control-stroke-default)",
9066
+ // base.css picks the two stops per scheme, where `data-fwt-theme` can
9067
+ // override the system scheme (local adaptation, UPSTREAM.md).
9068
+ borderTopColor: "var(--fwt-elevation-edge-top)",
8034
9069
  borderRightColor: "var(--winui-control-stroke-default)",
8035
- borderBottomColor: "var(--winui-control-stroke-secondary)",
8036
- borderLeftColor: "var(--winui-control-stroke-default)",
8037
- "@media (prefers-color-scheme: dark)": {
8038
- borderTopColor: "var(--winui-control-stroke-secondary)",
8039
- borderBottomColor: "var(--winui-control-stroke-default)"
8040
- }
9070
+ borderBottomColor: "var(--fwt-elevation-edge-bottom)",
9071
+ borderLeftColor: "var(--winui-control-stroke-default)"
8041
9072
  },
8042
9073
  // `:has` alongside each pointer state: the expander's own press lands on the
8043
9074
  // overlay inside the row, and a row that reacts only to a press on itself
8044
9075
  // would hold its rest fill while the reader is holding it down.
8045
9076
  "&:active, &:has(> :active)": {
8046
9077
  backgroundColor: "var(--winui-control-fill-tertiary)",
8047
- ...shorthands.borderColor("var(--winui-control-stroke-default)")
9078
+ ...shorthands3.borderColor("var(--winui-control-stroke-default)")
8048
9079
  },
8049
9080
  // The system focus visual: a 2px FocusStrokeColorOuter ring with a 1px
8050
9081
  // FocusStrokeColorInner ring immediately inside it, around the border box
@@ -8132,7 +9163,7 @@ var useStyles13 = makeStyles13({
8132
9163
  expanderHeader: {
8133
9164
  position: "relative",
8134
9165
  backgroundColor: "var(--winui-card-background-fill-default)",
8135
- ...shorthands.borderColor("var(--winui-card-stroke-default)"),
9166
+ ...shorthands3.borderColor("var(--winui-card-stroke-default)"),
8136
9167
  fontFamily: "inherit",
8137
9168
  fontSize: "inherit",
8138
9169
  paddingInlineEnd: "4px",
@@ -8182,7 +9213,7 @@ var useStyles13 = makeStyles13({
8182
9213
  // The row paints the ring; see the interactive style above.
8183
9214
  ":focus-visible": { outline: "none" },
8184
9215
  background: "none",
8185
- ...shorthands.borderWidth(0),
9216
+ ...shorthands3.borderWidth(0),
8186
9217
  cursor: "pointer",
8187
9218
  "&:disabled": { cursor: "not-allowed" },
8188
9219
  inset: 0,
@@ -8317,48 +9348,48 @@ var useStyles13 = makeStyles13({
8317
9348
  readoutDisabled: { color: "var(--winui-text-fill-disabled)" }
8318
9349
  });
8319
9350
  function CardText({ description, header, icon, id }) {
8320
- const styles = useStyles13();
8321
- return /* @__PURE__ */ jsxs16(Fragment2, { children: [
8322
- icon !== void 0 && /* @__PURE__ */ jsx33("span", { "aria-hidden": true, className: styles.icon, ...{ [ICON_MARKER]: "" }, children: icon }),
8323
- /* @__PURE__ */ jsxs16("span", { className: styles.text, children: [
8324
- /* @__PURE__ */ jsx33(Text5, { block: true, id, children: header }),
8325
- description !== void 0 && /* @__PURE__ */ jsx33(Text5, { block: true, size: 200, className: styles.description, children: description })
9351
+ const styles = useStyles17();
9352
+ return /* @__PURE__ */ jsxs22(Fragment3, { children: [
9353
+ icon !== void 0 && /* @__PURE__ */ jsx42("span", { "aria-hidden": true, className: styles.icon, ...{ [ICON_MARKER]: "" }, children: icon }),
9354
+ /* @__PURE__ */ jsxs22("span", { className: styles.text, children: [
9355
+ /* @__PURE__ */ jsx42(Text8, { block: true, id, children: header }),
9356
+ description !== void 0 && /* @__PURE__ */ jsx42(Text8, { block: true, size: 200, className: styles.description, children: description })
8326
9357
  ] })
8327
9358
  ] });
8328
9359
  }
8329
9360
  function SettingsSwitch({ checked, disabled, label, onChange }) {
8330
- const styles = useStyles13();
9361
+ const styles = useStyles17();
8331
9362
  const { t: t2 } = useTranslation();
8332
- return /* @__PURE__ */ jsxs16("span", { className: styles.switchRow, children: [
8333
- /* @__PURE__ */ jsx33(Text5, { className: disabled === true ? styles.readoutDisabled : void 0, children: t2(checked ? "common.on" : "common.off") }),
8334
- /* @__PURE__ */ jsx33(Switch, { "aria-label": label, checked, disabled, onChange: (_2, data) => onChange(data.checked) })
9363
+ return /* @__PURE__ */ jsxs22("span", { className: styles.switchRow, children: [
9364
+ /* @__PURE__ */ jsx42(Text8, { className: disabled === true ? styles.readoutDisabled : void 0, children: t2(checked ? "common.on" : "common.off") }),
9365
+ /* @__PURE__ */ jsx42(Switch, { "aria-label": label, checked, disabled, onChange: (_2, data) => onChange(data.checked) })
8335
9366
  ] });
8336
9367
  }
8337
9368
  function SettingsCard({ action, description, header, icon }) {
8338
- const styles = useStyles13();
8339
- return /* @__PURE__ */ jsx33("div", { className: styles.row, children: /* @__PURE__ */ jsxs16("div", { className: styles.card, children: [
8340
- /* @__PURE__ */ jsx33(CardText, { description, header, icon }),
8341
- action !== void 0 && /* @__PURE__ */ jsx33("span", { className: styles.action, children: action })
9369
+ const styles = useStyles17();
9370
+ return /* @__PURE__ */ jsx42("div", { className: styles.row, children: /* @__PURE__ */ jsxs22("div", { className: styles.card, children: [
9371
+ /* @__PURE__ */ jsx42(CardText, { description, header, icon }),
9372
+ action !== void 0 && /* @__PURE__ */ jsx42("span", { className: styles.action, children: action })
8342
9373
  ] }) });
8343
9374
  }
8344
9375
  function SettingsExpander({ action, children, defaultOpen = false, description, disclosureDisabled = false, header, icon, revealOn, toggledOn }) {
8345
- const styles = useStyles13();
8346
- const [open, setOpen] = useState8(defaultOpen);
8347
- const [toggleWas, setToggleWas] = useState8(toggledOn);
9376
+ const styles = useStyles17();
9377
+ const [open, setOpen] = useState11(defaultOpen);
9378
+ const [toggleWas, setToggleWas] = useState11(toggledOn);
8348
9379
  if (toggledOn !== void 0 && toggledOn !== toggleWas) {
8349
9380
  setToggleWas(toggledOn);
8350
9381
  setOpen(toggledOn);
8351
9382
  }
8352
- const [revealWas, setRevealWas] = useState8(revealOn);
9383
+ const [revealWas, setRevealWas] = useState11(revealOn);
8353
9384
  if (revealOn !== revealWas) {
8354
9385
  setRevealWas(revealOn);
8355
9386
  if (revealOn === true) setOpen(true);
8356
9387
  }
8357
- const contentId = useId3();
8358
- const headerId = useId3();
8359
- return /* @__PURE__ */ jsxs16("div", { className: styles.row, children: [
8360
- /* @__PURE__ */ jsxs16("div", { className: mergeClasses17(styles.card, !disclosureDisabled && styles.interactive, styles.expanderHeader, open && styles.expanderHeaderOpen), children: [
8361
- /* @__PURE__ */ jsx33(
9388
+ const contentId = useId4();
9389
+ const headerId = useId4();
9390
+ return /* @__PURE__ */ jsxs22("div", { className: styles.row, children: [
9391
+ /* @__PURE__ */ jsxs22("div", { className: mergeClasses24(styles.card, !disclosureDisabled && styles.interactive, styles.expanderHeader, open && styles.expanderHeaderOpen), children: [
9392
+ /* @__PURE__ */ jsx42(
8362
9393
  "button",
8363
9394
  {
8364
9395
  "aria-controls": contentId,
@@ -8370,112 +9401,48 @@ function SettingsExpander({ action, children, defaultOpen = false, description,
8370
9401
  type: "button"
8371
9402
  }
8372
9403
  ),
8373
- /* @__PURE__ */ jsx33(CardText, { description, header, icon, id: headerId }),
8374
- action !== void 0 && /* @__PURE__ */ jsx33("span", { className: styles.action, children: action }),
8375
- /* @__PURE__ */ jsx33("span", { "aria-hidden": true, className: styles.chevron, children: /* @__PURE__ */ jsx33("svg", { className: mergeClasses17(styles.chevronGlyph, open && styles.chevronOpen), width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx33("path", { d: "M3.15 5.65c.2-.2.5-.2.7 0L8 9.79l4.15-4.14a.5.5 0 0 1 .7.7l-4.5 4.5a.5.5 0 0 1-.7 0l-4.5-4.5a.5.5 0 0 1 0-.7Z" }) }) })
9404
+ /* @__PURE__ */ jsx42(CardText, { description, header, icon, id: headerId }),
9405
+ action !== void 0 && /* @__PURE__ */ jsx42("span", { className: styles.action, children: action }),
9406
+ /* @__PURE__ */ jsx42("span", { "aria-hidden": true, className: styles.chevron, children: /* @__PURE__ */ jsx42("svg", { className: mergeClasses24(styles.chevronGlyph, open && styles.chevronOpen), width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx42("path", { d: "M3.15 5.65c.2-.2.5-.2.7 0L8 9.79l4.15-4.14a.5.5 0 0 1 .7.7l-4.5 4.5a.5.5 0 0 1-.7 0l-4.5-4.5a.5.5 0 0 1 0-.7Z" }) }) })
8376
9407
  ] }),
8377
- /* @__PURE__ */ jsx33("div", { className: mergeClasses17(styles.contentFrame, open && styles.contentFrameOpen), children: /* @__PURE__ */ jsx33("div", { className: styles.contentClip, children: /* @__PURE__ */ jsx33("div", { "aria-labelledby": headerId, className: styles.content, id: contentId, inert: !open, role: "group", children }) }) })
9408
+ /* @__PURE__ */ jsx42("div", { className: mergeClasses24(styles.contentFrame, open && styles.contentFrameOpen), children: /* @__PURE__ */ jsx42("div", { className: styles.contentClip, children: /* @__PURE__ */ jsx42("div", { "aria-labelledby": headerId, className: styles.content, id: contentId, inert: !open, role: "group", children }) }) })
8378
9409
  ] });
8379
9410
  }
8380
9411
 
8381
9412
  // src/components/ui/switch-setting.tsx
8382
- import { jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
9413
+ import { jsx as jsx43, jsxs as jsxs23 } from "react/jsx-runtime";
8383
9414
  var { InfoButton: InfoButton2 } = fluentComponents;
8384
9415
  function SwitchSetting({ checked, description, label, onChange }) {
8385
- return /* @__PURE__ */ jsxs17("span", { className: "inline-flex items-center gap-1", children: [
8386
- /* @__PURE__ */ jsx34(Switch, { checked, label, onChange: (_2, data) => onChange(data.checked) }),
8387
- /* @__PURE__ */ jsx34(InfoButton2, { info: description })
9416
+ return /* @__PURE__ */ jsxs23("span", { className: "inline-flex items-center gap-1", children: [
9417
+ /* @__PURE__ */ jsx43(Switch, { checked, label, onChange: (_2, data) => onChange(data.checked) }),
9418
+ /* @__PURE__ */ jsx43(InfoButton2, { info: description })
8388
9419
  ] });
8389
9420
  }
8390
9421
 
8391
- // src/components/ui/table-actions.tsx
8392
- import { jsx as jsx35 } from "react/jsx-runtime";
8393
- var { TableCell, TableHeaderCell, makeStyles: makeStyles14, mergeClasses: mergeClasses18 } = fluentComponents;
8394
- var useStyles14 = makeStyles14({
8395
- trailing: {
8396
- textAlign: "right",
8397
- "& .fui-TableHeaderCell__button": { justifyContent: "flex-end" }
8398
- },
8399
- centred: {
8400
- textAlign: "center",
8401
- "& .fui-TableHeaderCell__button": { justifyContent: "center" }
8402
- }
8403
- });
8404
- function TableTrailingHeader({ className, ...props }) {
8405
- const styles = useStyles14();
8406
- return /* @__PURE__ */ jsx35(TableHeaderCell, { ...props, className: mergeClasses18(styles.trailing, className) });
8407
- }
8408
- function TableTrailingCell({ className, ...props }) {
8409
- const styles = useStyles14();
8410
- return /* @__PURE__ */ jsx35(TableCell, { ...props, className: mergeClasses18(styles.trailing, className) });
8411
- }
8412
- function TableCentredHeader({ className, ...props }) {
8413
- const styles = useStyles14();
8414
- return /* @__PURE__ */ jsx35(TableHeaderCell, { ...props, className: mergeClasses18(styles.centred, className) });
8415
- }
8416
- function TableCentredCell({ className, ...props }) {
8417
- const styles = useStyles14();
8418
- return /* @__PURE__ */ jsx35(TableCell, { ...props, className: mergeClasses18(styles.centred, className) });
8419
- }
8420
- var stopRowSelection = { onClick: (event) => event.stopPropagation() };
8421
- var ACTION_GAP = "var(--spacingHorizontalXS)";
8422
- var ACTION_BUTTON_SIZE = "24px";
8423
- var TABLE_ACTIONS_WIDTH = `calc(3 * ${ACTION_BUTTON_SIZE} + 2 * ${ACTION_GAP} + var(--spacingHorizontalS) + var(--fwt-panel-inset))`;
8424
- function TableActions({ children }) {
8425
- return /* @__PURE__ */ jsx35("div", { className: "flex items-center justify-end", style: { gap: ACTION_GAP }, ...stopRowSelection, children });
8426
- }
8427
-
8428
- // src/components/ui/table-columns.tsx
8429
- import { jsx as jsx36 } from "react/jsx-runtime";
8430
- function TableColumns({ widths }) {
8431
- return /* @__PURE__ */ jsx36("colgroup", { children: widths.map((width, index) => /* @__PURE__ */ jsx36("col", { style: width === null ? void 0 : { width } }, index)) });
8432
- }
8433
-
8434
- // src/components/ui/tooltip-icon-button.tsx
8435
- import { jsx as jsx37 } from "react/jsx-runtime";
8436
- var { Button: Button7, Tooltip: Tooltip3, mergeClasses: mergeClasses19 } = fluentComponents;
8437
- function TooltipIconButton(props) {
8438
- return props.to === void 0 ? /* @__PURE__ */ jsx37(IconButton, { ...props }) : /* @__PURE__ */ jsx37(AddressedIconButton, { ...props, to: props.to });
8439
- }
8440
- function AddressedIconButton({ to, ...props }) {
8441
- const address = useRouteAddress(to);
8442
- return /* @__PURE__ */ jsx37(IconButton, { ...props, ...address });
8443
- }
8444
- function IconButton({ className, danger = false, disabled = false, disabledFocusable = false, href, icon, label, onClick }) {
8445
- const dangerClasses = useDangerActionClasses();
8446
- const shared = {
8447
- appearance: "subtle",
8448
- "aria-label": label,
8449
- className: mergeClasses19(danger && dangerClasses.button, className),
8450
- disabled,
8451
- disabledFocusable,
8452
- icon,
8453
- onClick,
8454
- size: "small"
8455
- };
8456
- return /* @__PURE__ */ jsx37(Tooltip3, { content: label, relationship: "label", children: href === void 0 ? /* @__PURE__ */ jsx37(Button7, { ...shared }) : /* @__PURE__ */ jsx37(Button7, { ...shared, as: "a", href }) });
8457
- }
9422
+ // src/components/ui/tab-view.tsx
9423
+ import { CaretLeft12Filled, CaretRight12Filled, Dismiss12Regular } from "@fluentui/react-icons";
9424
+ import { useCallback as useCallback7, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef9, useState as useState13 } from "react";
8458
9425
 
8459
9426
  // src/components/ui/truncation-tooltip.tsx
8460
- import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef7, useState as useState9 } from "react";
8461
- import { jsx as jsx38 } from "react/jsx-runtime";
9427
+ import { useCallback as useCallback6, useEffect as useEffect6, useRef as useRef8, useState as useState12 } from "react";
9428
+ import { jsx as jsx44 } from "react/jsx-runtime";
8462
9429
  var { Tooltip: Tooltip4 } = fluentComponents;
8463
9430
  var OVERFLOW_TOLERANCE_PX = 1;
8464
9431
  var collapseWhitespace = (text) => text.replace(/\s+/gu, " ").trim();
8465
9432
  var hidesContent = (node, content) => node.scrollWidth - node.clientWidth > OVERFLOW_TOLERANCE_PX || collapseWhitespace(node.textContent ?? "") !== collapseWhitespace(content);
8466
9433
  var useTruncation = (content, relationship) => {
8467
- const [truncated, setTruncated] = useState9(false);
8468
- const [visible, setVisible] = useState9(false);
8469
- const nodeRef = useRef7(null);
8470
- const contentRef = useRef7(content);
8471
- const measure2 = useCallback5(() => {
9434
+ const [truncated, setTruncated] = useState12(false);
9435
+ const [visible, setVisible] = useState12(false);
9436
+ const nodeRef = useRef8(null);
9437
+ const contentRef = useRef8(content);
9438
+ const measure2 = useCallback6(() => {
8472
9439
  const node = nodeRef.current;
8473
9440
  if (!node) return;
8474
9441
  const hides = hidesContent(node, contentRef.current);
8475
9442
  setTruncated(hides);
8476
9443
  if (!hides) setVisible(false);
8477
9444
  }, []);
8478
- const measureRef = useCallback5((node) => {
9445
+ const measureRef = useCallback6((node) => {
8479
9446
  nodeRef.current = node;
8480
9447
  if (!node) return;
8481
9448
  measure2();
@@ -8514,37 +9481,549 @@ var useTruncation = (content, relationship) => {
8514
9481
  };
8515
9482
  function TruncationTooltip({ children, content, relationship }) {
8516
9483
  const { measureRef, tooltipProps } = useTruncation(content, relationship);
8517
- return /* @__PURE__ */ jsx38(Tooltip4, { ...tooltipProps, children: children(measureRef) });
9484
+ return /* @__PURE__ */ jsx44(Tooltip4, { ...tooltipProps, children: children(measureRef) });
9485
+ }
9486
+
9487
+ // src/components/ui/tab-view.tsx
9488
+ import { jsx as jsx45, jsxs as jsxs24 } from "react/jsx-runtime";
9489
+ var { makeStyles: makeStyles18, mergeClasses: mergeClasses25 } = fluentComponents;
9490
+ var useStyles18 = makeStyles18({
9491
+ // TabViewHeaderPadding: 8px above the tabs.
9492
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L239
9493
+ // The strip's bottom line is TabViewBorderBrush (CardStrokeColorDefault),
9494
+ // drawn here under the scroller so that the selected tab, which reaches the
9495
+ // strip's last pixel row, paints over it and interrupts it.
9496
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L36-L37
9497
+ root: {
9498
+ alignItems: "flex-end",
9499
+ display: "flex",
9500
+ maxWidth: "100%",
9501
+ minWidth: 0,
9502
+ paddingTop: "8px",
9503
+ position: "relative",
9504
+ "&::after": {
9505
+ backgroundColor: "var(--winui-card-stroke-default)",
9506
+ bottom: 0,
9507
+ content: '""',
9508
+ height: "1px",
9509
+ left: 0,
9510
+ pointerEvents: "none",
9511
+ position: "absolute",
9512
+ right: 0,
9513
+ zIndex: 0
9514
+ }
9515
+ },
9516
+ scroller: {
9517
+ alignSelf: "stretch",
9518
+ display: "flex",
9519
+ flexGrow: 0,
9520
+ flexShrink: 1,
9521
+ minWidth: 0,
9522
+ overflowX: "auto",
9523
+ overflowY: "hidden",
9524
+ position: "relative",
9525
+ scrollbarWidth: "none",
9526
+ zIndex: 1,
9527
+ "&::-webkit-scrollbar": { display: "none" },
9528
+ "@media (prefers-reduced-motion: no-preference)": { scrollBehavior: "smooth" }
9529
+ },
9530
+ tablist: {
9531
+ alignItems: "flex-end",
9532
+ display: "flex",
9533
+ flexWrap: "nowrap",
9534
+ minWidth: "min-content"
9535
+ },
9536
+ // TabViewItemMinHeight 32, MinWidth 100, MaxWidth 240; the item's own
9537
+ // corners are OverlayCornerRadius on top only (TopCornerRadiusFilterConverter).
9538
+ // Every item holds a 1px border (TabViewItemBorderThickness) so selecting
9539
+ // one, which paints it, moves nothing.
9540
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L242-L244
9541
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L296-L297
9542
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L560-L562
9543
+ item: {
9544
+ alignItems: "stretch",
9545
+ borderBottomWidth: 0,
9546
+ borderLeft: "1px solid transparent",
9547
+ borderRight: "1px solid transparent",
9548
+ borderTop: "1px solid transparent",
9549
+ borderTopLeftRadius: "var(--winui-overlay-corner-radius)",
9550
+ borderTopRightRadius: "var(--winui-overlay-corner-radius)",
9551
+ boxSizing: "border-box",
9552
+ display: "flex",
9553
+ flexGrow: 0,
9554
+ flexShrink: 1,
9555
+ maxWidth: "240px",
9556
+ minHeight: "32px",
9557
+ minWidth: "100px",
9558
+ position: "relative",
9559
+ // TabSeparator: 1px DividerStrokeColorDefault inset 8px top and bottom,
9560
+ // gone under the pointer, on the selected tab and on the tab before it.
9561
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L553
9562
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L266
9563
+ "&::after": {
9564
+ backgroundColor: "var(--winui-divider-stroke-default)",
9565
+ bottom: "8px",
9566
+ content: '""',
9567
+ pointerEvents: "none",
9568
+ position: "absolute",
9569
+ right: "-1px",
9570
+ top: "8px",
9571
+ width: "1px"
9572
+ },
9573
+ "&:last-child::after": { display: "none" },
9574
+ "&:hover::after": { opacity: 0 },
9575
+ "&:has(+ [data-selected])::after": { opacity: 0 },
9576
+ "&:hover": { backgroundColor: "var(--winui-subtle-fill-secondary)" },
9577
+ "&:active": { backgroundColor: "var(--winui-subtle-fill-tertiary)" },
9578
+ // The selected tab: TabViewItemHeaderBackgroundSelected, a stroke on top
9579
+ // and sides in CardStrokeColorDefault, no bottom edge. It holds its fill
9580
+ // through the pointer states.
9581
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L325-L345
9582
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L72-L80
9583
+ "&[data-selected]": {
9584
+ backgroundColor: "var(--winui-solid-background-fill-tertiary)",
9585
+ borderLeftColor: "var(--winui-card-stroke-default)",
9586
+ borderRightColor: "var(--winui-card-stroke-default)",
9587
+ borderTopColor: "var(--winui-card-stroke-default)",
9588
+ zIndex: 1
9589
+ },
9590
+ "&[data-selected]::after": { opacity: 0 },
9591
+ "&[data-disabled]": { backgroundColor: "transparent" },
9592
+ // WinUI's CloseButtonOverlayMode Auto: the close button shows on the
9593
+ // selected tab and under the pointer, or always where there is no hover.
9594
+ "&:hover [data-tab-close], &:focus-within [data-tab-close]": { opacity: 1, pointerEvents: "auto" },
9595
+ "@media (hover: none)": { "& [data-tab-close]": { opacity: 1, pointerEvents: "auto" } }
9596
+ },
9597
+ // TabViewItemHeaderPaddingWithCloseButton 8,3,4,3 / WithoutCloseButton
9598
+ // 8,3,8,3; the close button's 4px margin supplies the right-hand 4.
9599
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L253-L254
9600
+ tab: {
9601
+ alignItems: "center",
9602
+ appearance: "none",
9603
+ backgroundColor: "transparent",
9604
+ borderRadius: "inherit",
9605
+ border: "none",
9606
+ // The foreground ramp: rest and pointer-over TextFillColorSecondary,
9607
+ // pressed Tertiary, selected Primary.
9608
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L12-L21
9609
+ color: "var(--winui-text-fill-secondary)",
9610
+ columnGap: "10px",
9611
+ cursor: "pointer",
9612
+ display: "flex",
9613
+ flexBasis: 0,
9614
+ flexGrow: 1,
9615
+ flexShrink: 1,
9616
+ // TabViewItemHeaderFontSize 12.
9617
+ fontFamily: "inherit",
9618
+ fontSize: "12px",
9619
+ lineHeight: "16px",
9620
+ minWidth: 0,
9621
+ padding: "3px 8px 3px 8px",
9622
+ textAlign: "start",
9623
+ "&:active:not(:disabled)": { color: "var(--winui-text-fill-tertiary)" },
9624
+ '&[aria-selected="true"], &[aria-selected="true"]:active': { color: "var(--winui-text-fill-primary)" },
9625
+ "&:disabled": { color: "var(--winui-text-fill-disabled)", cursor: "not-allowed" },
9626
+ // The system focus visual, as the library's other selectors draw it.
9627
+ "&:focus-visible": {
9628
+ boxShadow: "inset 0 0 0 1px var(--winui-focus-stroke-inner)",
9629
+ outline: "2px solid var(--winui-focus-stroke-outer)",
9630
+ outlineOffset: "-2px"
9631
+ }
9632
+ },
9633
+ tabWithClose: { paddingRight: 0 },
9634
+ // The icon sits in a 16px box with a 10px gap before the label.
9635
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L246-L247
9636
+ icon: {
9637
+ alignItems: "center",
9638
+ display: "inline-flex",
9639
+ flexShrink: 0,
9640
+ fontSize: "16px",
9641
+ height: "16px",
9642
+ justifyContent: "center",
9643
+ width: "16px"
9644
+ },
9645
+ label: {
9646
+ flexBasis: 0,
9647
+ flexGrow: 1,
9648
+ minWidth: 0,
9649
+ overflow: "hidden",
9650
+ textOverflow: "ellipsis",
9651
+ whiteSpace: "nowrap"
9652
+ },
9653
+ // TabViewItemHeaderCloseButtonWidth 32, Height 24, glyph 12, margin 4,0,0,0,
9654
+ // ControlCornerRadius; the fills are the subtle ramp, the glyph Primary
9655
+ // (Secondary while pressed).
9656
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L248-L252
9657
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView.xaml#L172-L187
9658
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L47-L60
9659
+ close: {
9660
+ alignItems: "center",
9661
+ alignSelf: "center",
9662
+ appearance: "none",
9663
+ backgroundColor: "transparent",
9664
+ borderRadius: "var(--winui-control-corner-radius)",
9665
+ border: "none",
9666
+ color: "var(--winui-text-fill-primary)",
9667
+ cursor: "pointer",
9668
+ display: "inline-flex",
9669
+ flexShrink: 0,
9670
+ fontSize: "12px",
9671
+ height: "24px",
9672
+ justifyContent: "center",
9673
+ margin: "0 4px",
9674
+ opacity: 0,
9675
+ padding: 0,
9676
+ pointerEvents: "none",
9677
+ width: "32px",
9678
+ "&:hover": { backgroundColor: "var(--winui-subtle-fill-secondary)" },
9679
+ "&:active": {
9680
+ backgroundColor: "var(--winui-subtle-fill-tertiary)",
9681
+ color: "var(--winui-text-fill-secondary)"
9682
+ },
9683
+ "&:focus-visible": {
9684
+ boxShadow: "inset 0 0 0 1px var(--winui-focus-stroke-inner)",
9685
+ outline: "2px solid var(--winui-focus-stroke-outer)",
9686
+ outlineOffset: "0",
9687
+ opacity: 1,
9688
+ pointerEvents: "auto"
9689
+ },
9690
+ "&:disabled": { color: "var(--winui-text-fill-disabled)", cursor: "not-allowed" }
9691
+ },
9692
+ closeShown: { opacity: 1, pointerEvents: "auto" },
9693
+ // Scroll buttons: 32x24, glyph 8, ControlCornerRadius, the subtle ramp, with
9694
+ // 8,0,3,3 / 3,0,8,3 container padding.
9695
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L255-L260
9696
+ // https://github.com/microsoft/microsoft-ui-xaml/blob/188f602b27cdb47572b28c380e9c087b02e1ccee/controls/dev/TabView/TabView_themeresources.xaml#L34-L41
9697
+ scrollButtonBox: {
9698
+ alignSelf: "flex-end",
9699
+ flexShrink: 0,
9700
+ position: "relative",
9701
+ zIndex: 1
9702
+ },
9703
+ scrollBoxLeft: { padding: "0 3px 3px 8px" },
9704
+ scrollBoxRight: { padding: "0 8px 3px 3px" },
9705
+ scrollButton: {
9706
+ alignItems: "center",
9707
+ appearance: "none",
9708
+ backgroundColor: "transparent",
9709
+ borderRadius: "var(--winui-control-corner-radius)",
9710
+ border: "none",
9711
+ color: "var(--winui-text-fill-secondary)",
9712
+ cursor: "pointer",
9713
+ display: "inline-flex",
9714
+ fontSize: "8px",
9715
+ height: "24px",
9716
+ justifyContent: "center",
9717
+ padding: "3px 7px",
9718
+ width: "32px",
9719
+ "&:hover:not(:disabled)": { backgroundColor: "var(--winui-subtle-fill-secondary)" },
9720
+ "&:active:not(:disabled)": { backgroundColor: "var(--winui-subtle-fill-tertiary)" },
9721
+ "&:disabled": { color: "var(--winui-text-fill-disabled)", cursor: "default" },
9722
+ "&:focus-visible": {
9723
+ boxShadow: "inset 0 0 0 1px var(--winui-focus-stroke-inner)",
9724
+ outline: "2px solid var(--winui-focus-stroke-outer)",
9725
+ outlineOffset: "0"
9726
+ }
9727
+ },
9728
+ // TabStripFooter: beside the tabs, after them, on the strip's baseline.
9729
+ footer: {
9730
+ alignItems: "center",
9731
+ alignSelf: "stretch",
9732
+ display: "flex",
9733
+ flexShrink: 0,
9734
+ minHeight: "32px",
9735
+ paddingBottom: "3px",
9736
+ paddingInline: "4px",
9737
+ position: "relative",
9738
+ zIndex: 1
9739
+ }
9740
+ });
9741
+ var SCROLL_STEP_PX = 200;
9742
+ function TabView({ ariaLabel, className, footer, items, onClose, onSelect, selectedValue }) {
9743
+ const styles = useStyles18();
9744
+ const { t: t2 } = useTranslation();
9745
+ const scrollerRef = useRef9(null);
9746
+ const [overflow, setOverflow] = useState13({ backward: false, forward: false, scrollable: false });
9747
+ const enabledValues = items.filter((item) => item.disabled !== true).map((item) => item.value);
9748
+ const tabStop = selectedValue !== null && enabledValues.includes(selectedValue) ? selectedValue : enabledValues[0];
9749
+ const measure2 = useCallback7(() => {
9750
+ const scroller = scrollerRef.current;
9751
+ if (!scroller) return;
9752
+ const scrollable = scroller.scrollWidth - scroller.clientWidth > 1;
9753
+ const backward = scrollable && scroller.scrollLeft > 0;
9754
+ const forward = scrollable && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth - 1;
9755
+ setOverflow((previous) => previous.scrollable === scrollable && previous.backward === backward && previous.forward === forward ? previous : { backward, forward, scrollable });
9756
+ }, []);
9757
+ useLayoutEffect4(() => {
9758
+ measure2();
9759
+ const scroller = scrollerRef.current;
9760
+ if (!scroller || typeof ResizeObserver === "undefined") return;
9761
+ const observer = new ResizeObserver(measure2);
9762
+ observer.observe(scroller);
9763
+ const list = scroller.firstElementChild;
9764
+ if (list) observer.observe(list);
9765
+ return () => observer.disconnect();
9766
+ }, [measure2, items]);
9767
+ useEffect7(() => {
9768
+ const selected = scrollerRef.current?.querySelector('[role="tab"][aria-selected="true"]');
9769
+ selected?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
9770
+ }, [selectedValue]);
9771
+ const scrollBy = (direction) => scrollerRef.current?.scrollBy?.({ left: direction * SCROLL_STEP_PX });
9772
+ const closeIfClosable = (value) => {
9773
+ const item = items.find((candidate) => candidate.value === value);
9774
+ if (item?.closable !== true || item.disabled === true) return false;
9775
+ onClose?.(value);
9776
+ return true;
9777
+ };
9778
+ const handleKeyDown = (event) => {
9779
+ const target = event.target;
9780
+ const owner = target.closest("[data-tab-value]");
9781
+ if (!owner) return;
9782
+ const value = owner.dataset.tabValue;
9783
+ if (event.key === "F4" && event.ctrlKey || event.key === "Delete") {
9784
+ if (closeIfClosable(value)) event.preventDefault();
9785
+ return;
9786
+ }
9787
+ if (!target.matches('[role="tab"]') || enabledValues.length === 0) return;
9788
+ const current = enabledValues.indexOf(value);
9789
+ let next;
9790
+ switch (event.key) {
9791
+ case "ArrowRight":
9792
+ next = enabledValues[(current + 1) % enabledValues.length];
9793
+ break;
9794
+ case "ArrowLeft":
9795
+ next = enabledValues[(current - 1 + enabledValues.length) % enabledValues.length];
9796
+ break;
9797
+ case "Home":
9798
+ next = enabledValues[0];
9799
+ break;
9800
+ case "End":
9801
+ next = enabledValues[enabledValues.length - 1];
9802
+ break;
9803
+ default:
9804
+ return;
9805
+ }
9806
+ event.preventDefault();
9807
+ const nextTab = event.currentTarget.querySelector(`[data-tab-value="${CSS.escape(next)}"] [role="tab"]`);
9808
+ nextTab?.focus();
9809
+ if (next !== selectedValue) onSelect(next);
9810
+ };
9811
+ return /* @__PURE__ */ jsxs24("div", { className: mergeClasses25(styles.root, className), children: [
9812
+ overflow.scrollable ? /* @__PURE__ */ jsx45("div", { className: mergeClasses25(styles.scrollButtonBox, styles.scrollBoxLeft), children: /* @__PURE__ */ jsx45(
9813
+ "button",
9814
+ {
9815
+ "aria-label": t2("tabView.scrollBackward"),
9816
+ className: styles.scrollButton,
9817
+ disabled: !overflow.backward,
9818
+ onClick: () => scrollBy(-1),
9819
+ tabIndex: -1,
9820
+ type: "button",
9821
+ children: /* @__PURE__ */ jsx45(CaretLeft12Filled, { fontSize: 8 })
9822
+ }
9823
+ ) }) : null,
9824
+ /* @__PURE__ */ jsx45("div", { className: styles.scroller, onScroll: measure2, ref: scrollerRef, children: /* @__PURE__ */ jsx45("div", { "aria-label": ariaLabel, "aria-orientation": "horizontal", className: styles.tablist, onKeyDown: handleKeyDown, role: "tablist", children: items.map((item) => {
9825
+ const selected = item.value === selectedValue;
9826
+ const disabled = item.disabled === true;
9827
+ const closable = item.closable === true;
9828
+ const closeLabel = item.closeLabel ?? t2("tabView.close");
9829
+ return /* @__PURE__ */ jsxs24(
9830
+ "div",
9831
+ {
9832
+ className: styles.item,
9833
+ "data-disabled": disabled ? "" : void 0,
9834
+ "data-selected": selected ? "" : void 0,
9835
+ "data-tab-value": item.value,
9836
+ onAuxClick: (event) => {
9837
+ if (event.button !== 1 || !closable) return;
9838
+ event.preventDefault();
9839
+ closeIfClosable(item.value);
9840
+ },
9841
+ onMouseDown: (event) => {
9842
+ if (event.button === 1) event.preventDefault();
9843
+ },
9844
+ role: "presentation",
9845
+ children: [
9846
+ /* @__PURE__ */ jsxs24(
9847
+ "button",
9848
+ {
9849
+ "aria-selected": selected,
9850
+ className: mergeClasses25(styles.tab, closable && styles.tabWithClose),
9851
+ disabled,
9852
+ onClick: () => {
9853
+ if (!selected) onSelect(item.value);
9854
+ },
9855
+ role: "tab",
9856
+ tabIndex: item.value === tabStop ? 0 : -1,
9857
+ type: "button",
9858
+ children: [
9859
+ item.icon === void 0 ? null : /* @__PURE__ */ jsx45("span", { "aria-hidden": "true", className: styles.icon, children: item.icon }),
9860
+ typeof item.header === "string" ? /* @__PURE__ */ jsx45(TruncationTooltip, { content: item.header, relationship: "description", children: (measureRef) => /* @__PURE__ */ jsx45("span", { className: styles.label, ref: measureRef, children: item.header }) }) : /* @__PURE__ */ jsx45("span", { className: styles.label, children: item.header })
9861
+ ]
9862
+ }
9863
+ ),
9864
+ closable ? /* @__PURE__ */ jsx45(
9865
+ "button",
9866
+ {
9867
+ "aria-label": closeLabel,
9868
+ className: mergeClasses25(styles.close, selected && styles.closeShown),
9869
+ "data-tab-close": "",
9870
+ disabled,
9871
+ onClick: () => closeIfClosable(item.value),
9872
+ tabIndex: -1,
9873
+ type: "button",
9874
+ children: /* @__PURE__ */ jsx45(Dismiss12Regular, { fontSize: 12 })
9875
+ }
9876
+ ) : null
9877
+ ]
9878
+ },
9879
+ item.value
9880
+ );
9881
+ }) }) }),
9882
+ overflow.scrollable ? /* @__PURE__ */ jsx45("div", { className: mergeClasses25(styles.scrollButtonBox, styles.scrollBoxRight), children: /* @__PURE__ */ jsx45(
9883
+ "button",
9884
+ {
9885
+ "aria-label": t2("tabView.scrollForward"),
9886
+ className: styles.scrollButton,
9887
+ disabled: !overflow.forward,
9888
+ onClick: () => scrollBy(1),
9889
+ tabIndex: -1,
9890
+ type: "button",
9891
+ children: /* @__PURE__ */ jsx45(CaretRight12Filled, { fontSize: 8 })
9892
+ }
9893
+ ) }) : null,
9894
+ footer === void 0 ? null : /* @__PURE__ */ jsx45("div", { className: styles.footer, children: footer })
9895
+ ] });
9896
+ }
9897
+
9898
+ // src/components/ui/tooltip-icon-button.tsx
9899
+ import { jsx as jsx46 } from "react/jsx-runtime";
9900
+ var { Button: Button11, Tooltip: Tooltip5, mergeClasses: mergeClasses26 } = fluentComponents;
9901
+ function TooltipIconButton(props) {
9902
+ return props.to === void 0 ? /* @__PURE__ */ jsx46(IconButton, { ...props }) : /* @__PURE__ */ jsx46(AddressedIconButton, { ...props, to: props.to });
9903
+ }
9904
+ function AddressedIconButton({ to, ...props }) {
9905
+ const address = useRouteAddress(to);
9906
+ return /* @__PURE__ */ jsx46(IconButton, { ...props, ...address });
9907
+ }
9908
+ function IconButton({ className, danger = false, disabled = false, disabledFocusable = false, href, icon, label, onClick, ...trigger }) {
9909
+ const dangerClasses = useDangerActionClasses();
9910
+ const shared = {
9911
+ ...trigger,
9912
+ appearance: "subtle",
9913
+ "aria-label": label,
9914
+ className: mergeClasses26(danger && dangerClasses.button, className),
9915
+ disabled,
9916
+ disabledFocusable,
9917
+ icon,
9918
+ onClick,
9919
+ size: "small"
9920
+ };
9921
+ return /* @__PURE__ */ jsx46(Tooltip5, { content: label, relationship: "label", children: href === void 0 ? /* @__PURE__ */ jsx46(Button11, { ...shared }) : /* @__PURE__ */ jsx46(Button11, { ...shared, as: "a", href }) });
9922
+ }
9923
+
9924
+ // src/components/ui/use-confirm.tsx
9925
+ import { createContext as createContext4, useCallback as useCallback8, useContext as useContext4, useRef as useRef10, useState as useState14 } from "react";
9926
+ import { jsx as jsx47, jsxs as jsxs25 } from "react/jsx-runtime";
9927
+ var ConfirmContext = createContext4(null);
9928
+ var errorText2 = (error) => error instanceof Error ? error.message : String(error);
9929
+ function ConfirmDialogProvider({ children }) {
9930
+ const nextId = useRef10(0);
9931
+ const [queue, setQueue] = useState14([]);
9932
+ const [open, setOpen] = useState14(true);
9933
+ const [busy, setBusy] = useState14(false);
9934
+ const [error, setError] = useState14(null);
9935
+ const head = queue[0];
9936
+ const confirm = useCallback8((options) => new Promise((resolve) => {
9937
+ setQueue((current) => [...current, { id: nextId.current++, options, resolve }]);
9938
+ }), []);
9939
+ const finish = (confirmed) => {
9940
+ if (!open) return;
9941
+ head?.resolve(confirmed);
9942
+ setOpen(false);
9943
+ };
9944
+ const accept = async () => {
9945
+ if (!head) return;
9946
+ const { onConfirm } = head.options;
9947
+ if (!onConfirm) {
9948
+ finish(true);
9949
+ return;
9950
+ }
9951
+ setError(null);
9952
+ setBusy(true);
9953
+ try {
9954
+ await onConfirm();
9955
+ setBusy(false);
9956
+ finish(true);
9957
+ } catch (caught) {
9958
+ setBusy(false);
9959
+ setError(errorText2(caught));
9960
+ }
9961
+ };
9962
+ const advance = () => {
9963
+ setQueue((current) => current.slice(1));
9964
+ setError(null);
9965
+ setOpen(true);
9966
+ };
9967
+ return /* @__PURE__ */ jsxs25(ConfirmContext.Provider, { value: confirm, children: [
9968
+ children,
9969
+ head && /* @__PURE__ */ jsx47(
9970
+ ConfirmDialog,
9971
+ {
9972
+ actionIntent: head.options.intent,
9973
+ actionLabel: head.options.actionLabel,
9974
+ busy,
9975
+ cancelLabel: head.options.cancelLabel,
9976
+ error,
9977
+ message: head.options.message,
9978
+ onConfirm: () => {
9979
+ void accept();
9980
+ },
9981
+ onDismissError: () => setError(null),
9982
+ onExited: advance,
9983
+ onOpenChange: (next) => {
9984
+ if (!next) finish(false);
9985
+ },
9986
+ open,
9987
+ title: head.options.title
9988
+ },
9989
+ head.id
9990
+ )
9991
+ ] });
8518
9992
  }
9993
+ var useConfirm = () => {
9994
+ const value = useContext4(ConfirmContext);
9995
+ if (!value) throw new Error("useConfirm requires a ConfirmDialogProvider above it");
9996
+ return value;
9997
+ };
8519
9998
 
8520
9999
  // src/components/ui/use-dialog-invocation.ts
8521
- import { useCallback as useCallback6, useMemo as useMemo5, useRef as useRef8, useState as useState10 } from "react";
10000
+ import { useCallback as useCallback9, useMemo as useMemo6, useRef as useRef11, useState as useState15 } from "react";
8522
10001
  var useDialogInvocation = () => {
8523
- const nextKey = useRef8(0);
8524
- const [invocation, setInvocation] = useState10(null);
8525
- const [isOpen, setIsOpen] = useState10(false);
8526
- const open = useCallback6((value) => {
10002
+ const nextKey = useRef11(0);
10003
+ const [invocation, setInvocation] = useState15(null);
10004
+ const [isOpen, setIsOpen] = useState15(false);
10005
+ const open = useCallback9((value) => {
8527
10006
  setInvocation({ key: nextKey.current++, value });
8528
10007
  setIsOpen(true);
8529
10008
  }, []);
8530
- const close = useCallback6(() => setIsOpen(false), []);
8531
- return useMemo5(() => ({ close, invocation, isOpen, open }), [close, invocation, isOpen, open]);
10009
+ const close = useCallback9(() => setIsOpen(false), []);
10010
+ return useMemo6(() => ({ close, invocation, isOpen, open }), [close, invocation, isOpen, open]);
8532
10011
  };
8533
10012
 
8534
10013
  // src/components/ui/use-discard-guard.tsx
8535
- import { useCallback as useCallback7, useRef as useRef9, useState as useState11 } from "react";
8536
- import { jsx as jsx39 } from "react/jsx-runtime";
10014
+ import { useCallback as useCallback10, useRef as useRef12, useState as useState16 } from "react";
10015
+ import { jsx as jsx48 } from "react/jsx-runtime";
8537
10016
  var useDiscardGuard = ({ onClose, values }) => {
8538
10017
  const { t: t2 } = useTranslation();
8539
10018
  const prompt = useDialogInvocation();
8540
- const discarding = useRef9(false);
8541
- const [openedWith] = useState11(() => JSON.stringify(values));
10019
+ const discarding = useRef12(false);
10020
+ const [openedWith] = useState16(() => JSON.stringify(values));
8542
10021
  const dirty = JSON.stringify(values) !== openedWith;
8543
- const requestClose = useCallback7(() => {
10022
+ const requestClose = useCallback10(() => {
8544
10023
  if (dirty) prompt.open();
8545
10024
  else onClose();
8546
10025
  }, [dirty, onClose, prompt]);
8547
- const discardConfirmation = prompt.invocation && /* @__PURE__ */ jsx39(
10026
+ const discardConfirmation = prompt.invocation && /* @__PURE__ */ jsx48(
8548
10027
  ConfirmDialog,
8549
10028
  {
8550
10029
  actionLabel: t2("common.discard.discard"),
@@ -8572,13 +10051,13 @@ var useDiscardGuard = ({ onClose, values }) => {
8572
10051
 
8573
10052
  // src/shell/app-shell.tsx
8574
10053
  import { NavigationRegular } from "@fluentui/react-icons";
8575
- import { useLayoutEffect as useLayoutEffect4, useRef as useRef10, useState as useState13 } from "react";
10054
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef13, useState as useState18 } from "react";
8576
10055
 
8577
10056
  // src/shell/page-frames.tsx
8578
- import { useState as useState12 } from "react";
10057
+ import { useState as useState17 } from "react";
8579
10058
  var usePageFrames = (page, pageKey) => {
8580
- const [current, setCurrent] = useState12({ id: 0, key: pageKey, node: page });
8581
- const [leaving, setLeaving] = useState12(null);
10059
+ const [current, setCurrent] = useState17({ id: 0, key: pageKey, node: page });
10060
+ const [leaving, setLeaving] = useState17(null);
8582
10061
  if (current.key !== pageKey) {
8583
10062
  setLeaving({ id: current.id, node: current.node });
8584
10063
  setCurrent({ id: current.id + 1, key: pageKey, node: page });
@@ -8594,14 +10073,14 @@ var usePageFrames = (page, pageKey) => {
8594
10073
  var pageFrameClassName = (frame) => frame.leaving ? "fwt-page-leaving" : frame.id > 0 ? "fwt-page-entering" : "";
8595
10074
 
8596
10075
  // src/shell/app-shell.tsx
8597
- import { jsx as jsx40, jsxs as jsxs18 } from "react/jsx-runtime";
8598
- var { Button: Button8, DrawerBody, OverlayDrawer } = fluentComponents;
10076
+ import { jsx as jsx49, jsxs as jsxs26 } from "react/jsx-runtime";
10077
+ var { Button: Button12, DrawerBody, OverlayDrawer } = fluentComponents;
8599
10078
  var MAIN_ID = "fwt-main";
8600
10079
  function AppShell({ children, labels, narrowHeader, narrowHeaderActions, navigation, pageKey, workspace = false }) {
8601
- const [navigationOpen, setNavigationOpen] = useState13(false);
8602
- const firstFrameRef = useRef10(null);
8603
- const entranceStarted = useRef10(false);
8604
- useLayoutEffect4(() => {
10080
+ const [navigationOpen, setNavigationOpen] = useState18(false);
10081
+ const firstFrameRef = useRef13(null);
10082
+ const entranceStarted = useRef13(false);
10083
+ useLayoutEffect5(() => {
8605
10084
  if (entranceStarted.current) return;
8606
10085
  if (prefersReducedMotion()) return;
8607
10086
  const frame = firstFrameRef.current;
@@ -8613,19 +10092,19 @@ function AppShell({ children, labels, narrowHeader, narrowHeaderActions, navigat
8613
10092
  { duration: PAGE_ENTER_MS, easing: PAGE_ENTER_EASING, fill: "forwards" }
8614
10093
  );
8615
10094
  }, []);
8616
- const page = /* @__PURE__ */ jsx40(
10095
+ const page = /* @__PURE__ */ jsx49(
8617
10096
  ScrollArea,
8618
10097
  {
8619
10098
  axes: "vertical",
8620
10099
  className: "h-full min-h-0",
8621
10100
  contentClassName: workspace ? "h-full" : "min-h-full",
8622
10101
  noTabIndex: true,
8623
- children: /* @__PURE__ */ jsx40("div", { className: `${workspace ? SCROLLPORT_FILL_CLASS : ""} p-[22px_var(--fwt-page-inset)_var(--fwt-page-inset)] max-[680px]:p-4`, children })
10102
+ children: /* @__PURE__ */ jsx49("div", { className: `${workspace ? SCROLLPORT_FILL_CLASS : ""} p-[22px_var(--fwt-page-inset)_var(--fwt-page-inset)] max-[680px]:p-4`, children })
8624
10103
  }
8625
10104
  );
8626
10105
  const frames = usePageFrames(page, pageKey);
8627
- return /* @__PURE__ */ jsxs18(OutcomeToastProvider, { children: [
8628
- /* @__PURE__ */ jsx40(
10106
+ return /* @__PURE__ */ jsxs26(OutcomeToastProvider, { children: [
10107
+ /* @__PURE__ */ jsx49(
8629
10108
  "a",
8630
10109
  {
8631
10110
  className: "fixed left-3 top-3 z-[100000] -translate-y-20 rounded-md bg-fui-bg1 px-3 py-2 text-fui-fg1 shadow-lg focus:translate-y-0",
@@ -8633,22 +10112,22 @@ function AppShell({ children, labels, narrowHeader, narrowHeaderActions, navigat
8633
10112
  children: labels.skipToContent
8634
10113
  }
8635
10114
  ),
8636
- /* @__PURE__ */ jsxs18("div", { className: "grid grid-cols-[clamp(240px,18vw,290px)_minmax(0,1fr)] grid-rows-[minmax(0,1fr)] h-[100dvh] min-h-0 max-[900px]:grid-cols-1 max-[900px]:grid-rows-[58px_minmax(0,1fr)]", children: [
8637
- /* @__PURE__ */ jsx40("div", { className: "min-h-0 max-[900px]:hidden", children: navigation({}) }),
8638
- /* @__PURE__ */ jsxs18("header", { className: "hidden max-[900px]:flex items-center gap-3 border-b border-b-solid border-fui-divider px-4", children: [
8639
- /* @__PURE__ */ jsx40(
8640
- Button8,
10115
+ /* @__PURE__ */ jsxs26("div", { className: "grid grid-cols-[clamp(240px,18vw,290px)_minmax(0,1fr)] grid-rows-[minmax(0,1fr)] h-[100dvh] min-h-0 max-[900px]:grid-cols-1 max-[900px]:grid-rows-[58px_minmax(0,1fr)]", children: [
10116
+ /* @__PURE__ */ jsx49("div", { className: "min-h-0 max-[900px]:hidden", children: navigation({}) }),
10117
+ /* @__PURE__ */ jsxs26("header", { className: "hidden max-[900px]:flex items-center gap-3 border-b border-b-solid border-fui-divider px-4", children: [
10118
+ /* @__PURE__ */ jsx49(
10119
+ Button12,
8641
10120
  {
8642
10121
  appearance: "subtle",
8643
10122
  "aria-label": labels.openNavigation,
8644
- icon: /* @__PURE__ */ jsx40(NavigationRegular, {}),
10123
+ icon: /* @__PURE__ */ jsx49(NavigationRegular, {}),
8645
10124
  onClick: () => setNavigationOpen(true)
8646
10125
  }
8647
10126
  ),
8648
10127
  narrowHeader,
8649
- narrowHeaderActions !== void 0 && /* @__PURE__ */ jsx40("div", { className: "ml-auto flex items-center gap-2", children: narrowHeaderActions })
10128
+ narrowHeaderActions !== void 0 && /* @__PURE__ */ jsx49("div", { className: "ml-auto flex items-center gap-2", children: narrowHeaderActions })
8650
10129
  ] }),
8651
- /* @__PURE__ */ jsx40("div", { className: "grid grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] min-h-0", children: frames.map((frame) => /* @__PURE__ */ jsx40(
10130
+ /* @__PURE__ */ jsx49("div", { className: "grid grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] min-h-0", children: frames.map((frame) => /* @__PURE__ */ jsx49(
8652
10131
  "div",
8653
10132
  {
8654
10133
  "aria-hidden": frame.leaving || void 0,
@@ -8664,7 +10143,7 @@ function AppShell({ children, labels, narrowHeader, narrowHeaderActions, navigat
8664
10143
  frame.id
8665
10144
  )) })
8666
10145
  ] }),
8667
- /* @__PURE__ */ jsx40(
10146
+ /* @__PURE__ */ jsx49(
8668
10147
  OverlayDrawer,
8669
10148
  {
8670
10149
  "aria-label": labels.navigation,
@@ -8672,33 +10151,33 @@ function AppShell({ children, labels, narrowHeader, narrowHeaderActions, navigat
8672
10151
  onOpenChange: (_2, data) => setNavigationOpen(data.open),
8673
10152
  open: navigationOpen,
8674
10153
  position: "start",
8675
- children: /* @__PURE__ */ jsx40(DrawerBody, { className: "!p-0", children: navigation({ onNavigate: () => setNavigationOpen(false) }) })
10154
+ children: /* @__PURE__ */ jsx49(DrawerBody, { className: "!p-0", children: navigation({ onNavigate: () => setNavigationOpen(false) }) })
8676
10155
  }
8677
10156
  )
8678
10157
  ] });
8679
10158
  }
8680
10159
 
8681
10160
  // src/shell/gradient-background.tsx
8682
- import { jsx as jsx41 } from "react/jsx-runtime";
10161
+ import { jsx as jsx50 } from "react/jsx-runtime";
8683
10162
  function GradientBackground({ children }) {
8684
- return /* @__PURE__ */ jsx41("div", { className: "fwt-gradient-background", children });
10163
+ return /* @__PURE__ */ jsx50("div", { className: "fwt-gradient-background", children });
8685
10164
  }
8686
10165
 
8687
10166
  // src/shell/navigation-pane.tsx
8688
- import { DismissRegular as DismissRegular3 } from "@fluentui/react-icons";
8689
- import { useRef as useRef11 } from "react";
8690
- import { jsx as jsx42, jsxs as jsxs19 } from "react/jsx-runtime";
10167
+ import { DismissRegular as DismissRegular4 } from "@fluentui/react-icons";
10168
+ import { useRef as useRef14 } from "react";
10169
+ import { jsx as jsx51, jsxs as jsxs27 } from "react/jsx-runtime";
8691
10170
  var {
8692
- Button: Button9,
10171
+ Button: Button13,
8693
10172
  NavDrawer,
8694
10173
  NavDrawerBody,
8695
10174
  NavDrawerFooter,
8696
10175
  NavDrawerHeader,
8697
10176
  NavItem,
8698
10177
  NavSectionHeader,
8699
- makeStyles: makeStyles15
10178
+ makeStyles: makeStyles19
8700
10179
  } = fluentComponents;
8701
- var useStyles15 = makeStyles15({
10180
+ var useStyles19 = makeStyles19({
8702
10181
  // 36px is a floor rather than a fixed height, so a two-line label lengthens
8703
10182
  // the row instead of being cut. The left-pane template's 40px icon column
8704
10183
  // plus the presenter's 4 puts the label at 44; Fluent's icon slot is 20px
@@ -8720,14 +10199,14 @@ var useStyles15 = makeStyles15({
8720
10199
  });
8721
10200
  var NAV_INDICATOR_INSET = 0;
8722
10201
  function PaneLink({ item, onNavigate, pending, href }) {
8723
- const styles = useStyles15();
10202
+ const styles = useStyles19();
8724
10203
  const address = useRouteAddress(href);
8725
10204
  const handleClick = (event) => {
8726
10205
  const followsInThisView = event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
8727
10206
  address.onClick(event);
8728
10207
  if (followsInThisView) onNavigate?.();
8729
10208
  };
8730
- return /* @__PURE__ */ jsx42(
10209
+ return /* @__PURE__ */ jsx51(
8731
10210
  NavItem,
8732
10211
  {
8733
10212
  as: "a",
@@ -8743,11 +10222,11 @@ function PaneLink({ item, onNavigate, pending, href }) {
8743
10222
  );
8744
10223
  }
8745
10224
  function PaneItem({ item, onNavigate, pendingValue }) {
8746
- const styles = useStyles15();
10225
+ const styles = useStyles19();
8747
10226
  if (item.href !== void 0) {
8748
- return /* @__PURE__ */ jsx42(PaneLink, { href: item.href, item, onNavigate, pending: pendingValue === item.value });
10227
+ return /* @__PURE__ */ jsx51(PaneLink, { href: item.href, item, onNavigate, pending: pendingValue === item.value });
8749
10228
  }
8750
- return /* @__PURE__ */ jsx42(NavItem, { className: styles.item, "data-nav-value": item.value, icon: item.icon, value: item.value, children: item.label });
10229
+ return /* @__PURE__ */ jsx51(NavItem, { className: styles.item, "data-nav-value": item.value, icon: item.icon, value: item.value, children: item.label });
8751
10230
  }
8752
10231
  function NavigationPane({
8753
10232
  closeLabel = "Close navigation",
@@ -8760,10 +10239,10 @@ function NavigationPane({
8760
10239
  pendingValue,
8761
10240
  selectedValue
8762
10241
  }) {
8763
- const bodyRef = useRef11(null);
8764
- const footerRef = useRef11(null);
10242
+ const bodyRef = useRef14(null);
10243
+ const footerRef = useRef14(null);
8765
10244
  const allItems = [...groups.flatMap((group) => group.items), ...footerItems];
8766
- return /* @__PURE__ */ jsxs19(
10245
+ return /* @__PURE__ */ jsxs27(
8767
10246
  NavDrawer,
8768
10247
  {
8769
10248
  "aria-label": label,
@@ -8778,21 +10257,21 @@ function NavigationPane({
8778
10257
  surfaceMotion: null,
8779
10258
  type: "inline",
8780
10259
  children: [
8781
- /* @__PURE__ */ jsx42(NavDrawerHeader, { className: "!bg-transparent !px-5 !py-4", children: /* @__PURE__ */ jsxs19("div", { className: "flex items-center min-h-10", children: [
10260
+ /* @__PURE__ */ jsx51(NavDrawerHeader, { className: "!bg-transparent !px-5 !py-4", children: /* @__PURE__ */ jsxs27("div", { className: "flex items-center min-h-10", children: [
8782
10261
  header,
8783
- !onNavigate && headerActions !== void 0 && /* @__PURE__ */ jsx42("div", { className: "ml-auto flex items-center gap-2", children: headerActions }),
8784
- onNavigate && /* @__PURE__ */ jsx42(Button9, { appearance: "subtle", "aria-label": closeLabel, className: "!ml-auto", icon: /* @__PURE__ */ jsx42(DismissRegular3, {}), onClick: onNavigate })
10262
+ !onNavigate && headerActions !== void 0 && /* @__PURE__ */ jsx51("div", { className: "ml-auto flex items-center gap-2", children: headerActions }),
10263
+ onNavigate && /* @__PURE__ */ jsx51(Button13, { appearance: "subtle", "aria-label": closeLabel, className: "!ml-auto", icon: /* @__PURE__ */ jsx51(DismissRegular4, {}), onClick: onNavigate })
8785
10264
  ] }) }),
8786
- /* @__PURE__ */ jsx42(NavDrawerBody, { className: "!bg-transparent overflow-hidden !p-0", children: /* @__PURE__ */ jsx42(ScrollArea, { axes: "vertical", className: "h-full min-h-0", contentClassName: "px-[10px]", noTabIndex: true, children: /* @__PURE__ */ jsxs19("div", { className: "relative", ref: bodyRef, children: [
8787
- /* @__PURE__ */ jsx42(NavSelectionIndicator, { containerRef: bodyRef, inset: NAV_INDICATOR_INSET, otherListIs: "below", selectedValue }),
8788
- groups.map((group, groupIndex) => group.items.length === 0 ? null : /* @__PURE__ */ jsxs19("div", { children: [
8789
- group.label !== void 0 && /* @__PURE__ */ jsx42(NavSectionHeader, { children: group.label }),
8790
- /* @__PURE__ */ jsx42("div", { className: "grid gap-1", children: group.items.map((item) => /* @__PURE__ */ jsx42(PaneItem, { item, onNavigate, pendingValue }, item.value)) })
10265
+ /* @__PURE__ */ jsx51(NavDrawerBody, { className: "!bg-transparent overflow-hidden !p-0", children: /* @__PURE__ */ jsx51(ScrollArea, { axes: "vertical", className: "h-full min-h-0", contentClassName: "px-[10px]", noTabIndex: true, children: /* @__PURE__ */ jsxs27("div", { className: "relative", ref: bodyRef, children: [
10266
+ /* @__PURE__ */ jsx51(NavSelectionIndicator, { containerRef: bodyRef, inset: NAV_INDICATOR_INSET, otherListIs: "below", selectedValue }),
10267
+ groups.map((group, groupIndex) => group.items.length === 0 ? null : /* @__PURE__ */ jsxs27("div", { children: [
10268
+ group.label !== void 0 && /* @__PURE__ */ jsx51(NavSectionHeader, { children: group.label }),
10269
+ /* @__PURE__ */ jsx51("div", { className: "grid gap-1", children: group.items.map((item) => /* @__PURE__ */ jsx51(PaneItem, { item, onNavigate, pendingValue }, item.value)) })
8791
10270
  ] }, groupIndex))
8792
10271
  ] }) }) }),
8793
- footerItems.length > 0 && /* @__PURE__ */ jsx42(NavDrawerFooter, { className: "!bg-transparent !gap-y-1 !px-[10px] !py-3", children: /* @__PURE__ */ jsxs19("div", { className: "grid gap-y-1 relative w-full", ref: footerRef, children: [
8794
- /* @__PURE__ */ jsx42(NavSelectionIndicator, { containerRef: footerRef, inset: NAV_INDICATOR_INSET, otherListIs: "above", selectedValue }),
8795
- footerItems.map((item) => /* @__PURE__ */ jsx42(PaneItem, { item, onNavigate, pendingValue }, item.value))
10272
+ footerItems.length > 0 && /* @__PURE__ */ jsx51(NavDrawerFooter, { className: "!bg-transparent !gap-y-1 !px-[10px] !py-3", children: /* @__PURE__ */ jsxs27("div", { className: "grid gap-y-1 relative w-full", ref: footerRef, children: [
10273
+ /* @__PURE__ */ jsx51(NavSelectionIndicator, { containerRef: footerRef, inset: NAV_INDICATOR_INSET, otherListIs: "above", selectedValue }),
10274
+ footerItems.map((item) => /* @__PURE__ */ jsx51(PaneItem, { item, onNavigate, pendingValue }, item.value))
8796
10275
  ] }) })
8797
10276
  ]
8798
10277
  }
@@ -8800,9 +10279,9 @@ function NavigationPane({
8800
10279
  }
8801
10280
 
8802
10281
  // src/shell/navigation-progress.tsx
8803
- import { jsx as jsx43 } from "react/jsx-runtime";
10282
+ import { jsx as jsx52 } from "react/jsx-runtime";
8804
10283
  function NavigationProgress({ active }) {
8805
- return /* @__PURE__ */ jsx43("div", { "aria-hidden": "true", className: "fwt-navigation-progress", "data-active": active });
10284
+ return /* @__PURE__ */ jsx52("div", { "aria-hidden": "true", className: "fwt-navigation-progress", "data-active": active });
8806
10285
  }
8807
10286
  export {
8808
10287
  AppLoadingScreen,
@@ -8823,8 +10302,11 @@ export {
8823
10302
  CodeBlock,
8824
10303
  Combobox,
8825
10304
  ConfirmDialog,
10305
+ ConfirmDialogProvider,
10306
+ ConfirmFlyout,
8826
10307
  ContentLoadingScreen,
8827
10308
  DashboardPageHeader,
10309
+ DataTable,
8828
10310
  DialogShell,
8829
10311
  Dropdown,
8830
10312
  EXPAND_ANIMATION_MS,
@@ -8832,6 +10314,8 @@ export {
8832
10314
  EmptyStateLine,
8833
10315
  ErrorShell,
8834
10316
  ErrorStack,
10317
+ FileDropZone,
10318
+ FilePickerButton,
8835
10319
  GradientBackground,
8836
10320
  HEADER_ROW_CLASS,
8837
10321
  HttpMethodBadge,
@@ -8843,10 +10327,13 @@ export {
8843
10327
  Input,
8844
10328
  LISTBOX_POSITIONING,
8845
10329
  MaskedIcon,
10330
+ Metric,
10331
+ MetricGrid,
8846
10332
  MultiselectCombobox,
8847
10333
  NavSelectionIndicator,
8848
10334
  NavigationPane,
8849
10335
  NavigationProgress,
10336
+ NumberBox,
8850
10337
  OpenLinkLabel,
8851
10338
  OutcomeMessageBar,
8852
10339
  OutcomeToastProvider,
@@ -8869,8 +10356,10 @@ export {
8869
10356
  POPUP_SLIDE_EASING,
8870
10357
  POPUP_SLIDE_MS,
8871
10358
  POP_IN_OFFSET_PX,
10359
+ Pager,
8872
10360
  Panel,
8873
10361
  ProgressRing,
10362
+ PropertyList,
8874
10363
  REPOSITION_ANIMATION_MS,
8875
10364
  REPOSITION_DELETE_DELAY_MS,
8876
10365
  REPOSITION_EASING,
@@ -8895,6 +10384,7 @@ export {
8895
10384
  THEME_MODE_ATTRIBUTE,
8896
10385
  TIGHT_STACK_CLASS,
8897
10386
  TWO_COLUMN_FORM_CLASS,
10387
+ TabView,
8898
10388
  TableActions,
8899
10389
  TableCentredCell,
8900
10390
  TableCentredHeader,
@@ -8908,21 +10398,32 @@ export {
8908
10398
  WinuiStringsProvider,
8909
10399
  applyThemeMode,
8910
10400
  baseFontStack,
10401
+ clampNumber,
8911
10402
  copyOutcomeIcon,
8912
10403
  copyToClipboard,
8913
10404
  fluentComponents,
10405
+ formatFileSize,
10406
+ formatMetricValue,
8914
10407
  infoLabelSlot,
10408
+ matchesAccept,
8915
10409
  monospaceStack,
8916
10410
  moveItem,
8917
10411
  pageFrameClassName,
10412
+ pagerPageCount,
10413
+ pagerPageWindow,
10414
+ parseNumberText,
8918
10415
  progressIndeterminateCss,
8919
10416
  refuseToggle,
10417
+ resolveNumberChange,
8920
10418
  resolveThemeMode,
10419
+ stepPrecision,
8921
10420
  stopRowSelection,
8922
10421
  useBadgeHue,
10422
+ useConfirm,
8923
10423
  useCopyLabel,
8924
10424
  useCopyToClipboard,
8925
10425
  useDangerActionClasses,
10426
+ useDangerFillClass,
8926
10427
  useDangerTextClass,
8927
10428
  useDialogInvocation,
8928
10429
  useDiscardGuard,
@@ -8931,6 +10432,7 @@ export {
8931
10432
  useReorderList,
8932
10433
  useRouteAddress,
8933
10434
  useScrollAreaHost,
10435
+ useToast,
8934
10436
  useTruncation,
8935
10437
  useWinuiTheme,
8936
10438
  valuesAsOptions,