@happyvertical/smrt-svelte 0.55.5 → 0.55.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/AGENTS.md +17 -2
  2. package/README.md +83 -4
  3. package/agents/workspace.md +202 -5
  4. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -1
  5. package/dist/browser-ai/adapters/stt/dictation-source.js +8 -2
  6. package/dist/browser-ai/adapters/stt/factory.d.ts +6 -3
  7. package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -1
  8. package/dist/browser-ai/adapters/stt/factory.js +19 -9
  9. package/dist/browser-ai/adapters/stt/index.d.ts +3 -1
  10. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  11. package/dist/browser-ai/adapters/stt/index.js +3 -1
  12. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts +111 -0
  13. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/local-speech-model.js +377 -0
  15. package/dist/browser-ai/adapters/stt/local-speech.d.ts +82 -0
  16. package/dist/browser-ai/adapters/stt/local-speech.d.ts.map +1 -0
  17. package/dist/browser-ai/adapters/stt/local-speech.js +317 -0
  18. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts +2 -0
  19. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/local-speech.worker.js +32 -0
  21. package/dist/browser-ai/adapters/stt/transformers-env.d.ts +8 -0
  22. package/dist/browser-ai/adapters/stt/transformers-env.d.ts.map +1 -0
  23. package/dist/browser-ai/adapters/stt/transformers-env.js +17 -0
  24. package/dist/browser-ai/adapters/stt/types.d.ts +54 -9
  25. package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -1
  26. package/dist/browser-ai/capabilities/speech-probe.d.ts +29 -0
  27. package/dist/browser-ai/capabilities/speech-probe.d.ts.map +1 -0
  28. package/dist/browser-ai/capabilities/speech-probe.js +19 -0
  29. package/dist/browser-ai/core/errors.d.ts +9 -0
  30. package/dist/browser-ai/core/errors.d.ts.map +1 -1
  31. package/dist/browser-ai/core/errors.js +12 -0
  32. package/dist/browser-ai/index.d.ts +1 -0
  33. package/dist/browser-ai/index.d.ts.map +1 -1
  34. package/dist/browser-ai/index.js +1 -0
  35. package/dist/components/app/AppShell.svelte +587 -33
  36. package/dist/components/app/AppShell.svelte.d.ts +75 -4
  37. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  38. package/dist/components/app/DockToggles.svelte +0 -1
  39. package/dist/components/app/dock-toggle.d.ts +7 -1
  40. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  41. package/dist/components/app/index.d.ts +2 -1
  42. package/dist/components/app/index.d.ts.map +1 -1
  43. package/dist/components/app/slot-item.d.ts +21 -0
  44. package/dist/components/app/slot-item.d.ts.map +1 -0
  45. package/dist/components/app/slot-item.js +1 -0
  46. package/dist/components/audit/AuditList.svelte +51 -10
  47. package/dist/components/audit/AuditList.svelte.d.ts +13 -1
  48. package/dist/components/audit/AuditList.svelte.d.ts.map +1 -1
  49. package/dist/components/audit/RecordHistory.svelte +168 -10
  50. package/dist/components/audit/RecordHistory.svelte.d.ts +11 -1
  51. package/dist/components/audit/RecordHistory.svelte.d.ts.map +1 -1
  52. package/dist/components/audit/index.d.ts +1 -1
  53. package/dist/components/audit/index.d.ts.map +1 -1
  54. package/dist/components/audit/types.d.ts +18 -0
  55. package/dist/components/audit/types.d.ts.map +1 -1
  56. package/dist/components/forms/AddressInput.svelte +2 -15
  57. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
  58. package/dist/components/forms/DateRangeInput.svelte +2 -15
  59. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
  60. package/dist/components/forms/DateTimeInput.svelte +8 -15
  61. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
  62. package/dist/components/forms/MeasurementInput.svelte +2 -15
  63. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
  64. package/dist/components/forms/MoneyInput.svelte +8 -25
  65. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
  66. package/dist/components/forms/NumberInput.svelte +7 -15
  67. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
  68. package/dist/components/forms/PhoneInput.svelte +7 -15
  69. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
  70. package/dist/components/forms/SelectInput.svelte +34 -40
  71. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
  72. package/dist/components/forms/TextInput.svelte +20 -37
  73. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
  74. package/dist/components/forms/TextareaInput.svelte +19 -35
  75. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
  76. package/dist/components/sortable/Sortable.svelte +6 -33
  77. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -1
  78. package/dist/components/sortable/announce.d.ts +5 -0
  79. package/dist/components/sortable/announce.d.ts.map +1 -0
  80. package/dist/components/sortable/announce.js +33 -0
  81. package/dist/components/workspace/admin-shell/AdminShell.svelte +424 -58
  82. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +27 -2
  83. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  84. package/dist/components/workspace/admin-shell/ShellBrand.svelte +86 -0
  85. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts +18 -0
  86. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts.map +1 -0
  87. package/dist/components/workspace/admin-shell/ShellIconButton.svelte +77 -0
  88. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts +26 -0
  89. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts.map +1 -0
  90. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +88 -16
  91. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -1
  92. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte +531 -0
  93. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts +26 -0
  94. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts.map +1 -0
  95. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte +57 -0
  96. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts +22 -0
  97. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts.map +1 -0
  98. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte +290 -0
  99. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts +32 -0
  100. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts.map +1 -0
  101. package/dist/components/workspace/admin-shell/TenantNav.svelte +67 -2
  102. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -0
  103. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  104. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +67 -0
  105. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -1
  106. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +129 -1
  107. package/dist/components/workspace/admin-shell/layout.d.ts +122 -0
  108. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -1
  109. package/dist/components/workspace/admin-shell/layout.js +410 -10
  110. package/dist/components/workspace/admin-shell/settings.js +4 -4
  111. package/dist/components/workspace/admin-shell/shell-icons.d.ts +45 -0
  112. package/dist/components/workspace/admin-shell/shell-icons.d.ts.map +1 -0
  113. package/dist/components/workspace/admin-shell/shell-icons.js +66 -0
  114. package/dist/components/workspace/admin-shell/slot-labels.d.ts +33 -0
  115. package/dist/components/workspace/admin-shell/slot-labels.d.ts.map +1 -0
  116. package/dist/components/workspace/admin-shell/slot-labels.js +33 -0
  117. package/dist/components/workspace/admin-shell/slots.d.ts +50 -0
  118. package/dist/components/workspace/admin-shell/slots.d.ts.map +1 -0
  119. package/dist/components/workspace/admin-shell/slots.js +77 -0
  120. package/dist/components/workspace/admin-shell/state.svelte.d.ts +35 -0
  121. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  122. package/dist/components/workspace/admin-shell/state.svelte.js +111 -0
  123. package/dist/components/workspace/admin-shell/types.d.ts +101 -1
  124. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  125. package/dist/components/workspace/index.d.ts +9 -2
  126. package/dist/components/workspace/index.d.ts.map +1 -1
  127. package/dist/components/workspace/index.js +6 -0
  128. package/dist/i18n/strings.workspace.d.ts +49 -0
  129. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  130. package/dist/i18n/strings.workspace.js +49 -0
  131. package/package.json +30 -19
  132. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +0 -51
  133. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +0 -1
  134. package/dist/browser-ai/adapters/stt/whisper-wasm.js +0 -380
@@ -3,17 +3,25 @@ import '@happyvertical/smrt-ui/themes/styles/all.css';
3
3
  import '@happyvertical/smrt-ui/themes/styles/base.css';
4
4
  import '@happyvertical/smrt-ui/themes/styles/fonts.css';
5
5
  import type { DataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
6
- import { type Snippet } from 'svelte';
6
+ import { type Component, type Snippet } from 'svelte';
7
7
  import type { User } from '../../state/app-state.js';
8
8
  import type { WebMcpProviderConfig } from '../../web/webmcp-provider.js';
9
9
  import { type ShellLayout } from '../workspace/admin-shell/layout.js';
10
- import { type ShellNavGroup, type ShellNavItem, type ShellPanelDefaults } from '../workspace/admin-shell/types.js';
10
+ import { type ShellSlot } from '../workspace/admin-shell/slots.js';
11
+ import { type AdminShellProps, type ShellNavGroup, type ShellNavItem, type ShellPanelDefaults, type ShellSectionActionsContext } from '../workspace/admin-shell/types.js';
11
12
  import type { DockToggle } from './dock-toggle.js';
13
+ import type { ShellSlotItem } from './slot-item.js';
12
14
  interface Props {
13
15
  /** Application name shown in the shell brand and app panel. */
14
16
  title?: string;
15
17
  /** Secondary line under the application name in the shell brand. */
16
18
  subtitle?: string;
19
+ /** Logo shown before the title in the shell brand (and as the compact rail mark). */
20
+ logoSrc?: string;
21
+ /** Alternative text for the logo (decorative by default: the title names the app). */
22
+ logoAlt?: string;
23
+ /** Where the brand links to (e.g. the app's home); unlinked when omitted. */
24
+ homeHref?: string;
17
25
  /** localStorage key for the user's shell layout preferences. */
18
26
  storageKey?: string;
19
27
  /** Navigation rendered in the tenant (left) panel. */
@@ -65,7 +73,7 @@ interface Props {
65
73
  */
66
74
  dock?: Snippet<[DataSurfaceRegistry]>;
67
75
  /**
68
- * Icon buttons rendered at the right of the shell header, each toggling
76
+ * Icon buttons, each placed in a shell `slot` (default `header.end`), each toggling
69
77
  * the `ShellDockTool` with the same id open and closed. `aria-pressed` and
70
78
  * `aria-expanded` follow the dock, focus moves into the dock when it
71
79
  * opens and back to the button when it closes (Escape included). The
@@ -73,9 +81,72 @@ interface Props {
73
81
  * drive the same dock from code with `useShellDock()`.
74
82
  */
75
83
  dockToggles?: DockToggle[];
84
+ /**
85
+ * Host content for any shell slot (`header.start|center|end`,
86
+ * `footer.start|center|end`, `leftSidebar.header|footer`,
87
+ * `rightSidebar.header|footer`). Rendered before dock toggles placed in the
88
+ * same slot. Each snippet is one movable item with id `slot:<slot>`; use
89
+ * `slotItems` for individually movable content.
90
+ */
91
+ slots?: Partial<Record<ShellSlot, Snippet>>;
92
+ /**
93
+ * Host items with stable ids, each in a default slot. Users can move them
94
+ * between slots in the layout editor (`ShellLayout.placements`). Rendered
95
+ * after `slots` content and before dock toggles in the same slot.
96
+ */
97
+ slotItems?: ShellSlotItem[];
98
+ /**
99
+ * Opt in to editing the layout in place. Adds the built-in shell item
100
+ * `item:layout-edit` (a pencil toggle, default slot `header.end`, or
101
+ * `{ slot }`); while on, slots render as labelled drop zones, items and
102
+ * navigation get grips, section headings get overlay icons and a floating
103
+ * toolbar, and hidden regions render as strips with a show control. Hosts
104
+ * and assistants drive it with `useShellLayout()` (`editing`,
105
+ * `setEditing`). Off (the default) leaves existing apps unchanged.
106
+ *
107
+ * `{ floating: true }` instead renders the toggle as a fixed round button
108
+ * in the top-right corner of the shell: not a placeable item (no slot, no
109
+ * grip), so hidden regions never displace it. The header and right sidebar
110
+ * reserve room for it so it covers none of their controls.
111
+ */
112
+ layoutEditing?: boolean | {
113
+ slot?: ShellSlot;
114
+ floating?: boolean;
115
+ };
116
+ /**
117
+ * Host icon buttons for each navigation section while the layout is edited
118
+ * (e.g. an Options gear or Help), rendered in the section's overlay icons
119
+ * and its floating toolbar. Receives the section id, label, whether it is
120
+ * user-created, and `editing`.
121
+ */
122
+ sectionActions?: Snippet<[ShellSectionActionsContext]>;
123
+ /**
124
+ * `'items'` (default) lists every section's entries in the left sidebar.
125
+ * `'sections'` lists only the sections, one icon link each, to the
126
+ * section's own page (`ShellNavGroup.href`, else `sectionHref`, else its
127
+ * first entry); render the entries there with `ShellSectionMenu`. In edit
128
+ * mode the sidebar edits sections (grip, rename, hide, icon); entries are
129
+ * edited on the section page.
130
+ */
131
+ navMode?: 'items' | 'sections';
132
+ /** Section page for groups without an `href` (e.g. user-created ones). */
133
+ sectionHref?: (sectionId: string) => string | undefined;
134
+ /** Renders host icon names for sections and entries. */
135
+ iconComponent?: Component<{
136
+ name: string;
137
+ size?: number;
138
+ }>;
139
+ /**
140
+ * Edge toggle buttons and WASD drop-down panels (see `AdminShell`).
141
+ * Default `false`: regions are laid out inline and no toggles or hotkeys
142
+ * exist. Pass `true` (or a per-edge map) for the previous behaviour.
143
+ */
144
+ edgeToggles?: AdminShellProps['edgeToggles'];
145
+ /** Keyboard shortcuts; default follows `edgeToggles` (see `AdminShell`). */
146
+ hotkeys?: boolean;
76
147
  children: Snippet;
77
148
  }
78
- declare const AppShell: import("svelte").Component<Props, {}, "">;
149
+ declare const AppShell: Component<Props, {}, "">;
79
150
  type AppShell = ReturnType<typeof AppShell>;
80
151
  export default AppShell;
81
152
  //# sourceMappingURL=AppShell.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,8CAA8C,CAAC;AACtD,OAAO,+CAA+C,CAAC;AACvD,OAAO,gDAAgD,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,QAAQ,CAAC;AAG/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGzE,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,oCAAoC,CAAC;AAM5C,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,UAAU,KAAK;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;IACxC,yFAAyF;IACzF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sEAAsE;IACtE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,gFAAgF;IAChF,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,iDAAiD;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+JD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"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"}
@@ -66,7 +66,6 @@ function press(event: MouseEvent, toggle: DockToggle): void {
66
66
  display: flex;
67
67
  align-items: center;
68
68
  gap: var(--smrt-spacing-1);
69
- margin-inline-start: auto;
70
69
  }
71
70
 
72
71
  .smrt-dock-toggles__button {
@@ -1,4 +1,5 @@
1
- /** A header button that toggles one dock tool. */
1
+ import type { ShellSlot } from '../workspace/admin-shell/slots.js';
2
+ /** A button that toggles one dock tool. */
2
3
  export interface DockToggle {
3
4
  /** Id of the `ShellDockTool` to toggle. */
4
5
  tool: string;
@@ -10,5 +11,10 @@ export interface DockToggle {
10
11
  * label's first letter stands in.
11
12
  */
12
13
  icon?: string;
14
+ /**
15
+ * Shell slot the button renders in. Default `header.end`. If that slot's
16
+ * region is hidden the shell moves it along the slot fallback chain.
17
+ */
18
+ slot?: ShellSlot;
13
19
  }
14
20
  //# sourceMappingURL=dock-toggle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dock-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/app/dock-toggle.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
1
+ {"version":3,"file":"dock-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/app/dock-toggle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB"}
@@ -5,7 +5,7 @@
5
5
  * server or `$app/*` imports. Pair with the server `load`/`actions` from
6
6
  * `@happyvertical/smrt-app-runtime/sveltekit`.
7
7
  */
8
- export { type ShellLayout, ShellLayoutController, ShellLayoutEditor, tryUseShellLayout, useShellLayout, } from '../workspace/index.js';
8
+ export { type ShellLayout, ShellLayoutController, ShellLayoutEditor, type ShellSectionActionsContext, tryUseShellLayout, useShellLayout, } from '../workspace/index.js';
9
9
  export { default as AppShell } from './AppShell.svelte';
10
10
  export type { DockToggle } from './dock-toggle.js';
11
11
  export { default as OwnerSetupForm } from './OwnerSetupForm.svelte';
@@ -13,4 +13,5 @@ export type { OwnerSetupData, OwnerSetupFormResult, } from './owner-setup-types.
13
13
  export { default as RuntimeDiagnosticsWebMcp } from './RuntimeDiagnosticsWebMcp.svelte';
14
14
  export { createRuntimeDiagnosticsWebMcpTool, type RegisterRuntimeDiagnosticsWebMcpOptions, RUNTIME_DIAGNOSTICS_ENDPOINT, RUNTIME_DIAGNOSTICS_WEBMCP_TOOL_NAME, type RuntimeDiagnosticsWebMcpOwner, registerRuntimeDiagnosticsWebMcp, } from './runtime-diagnostics-webmcp.js';
15
15
  export { default as ShellSettingsPage } from './ShellSettingsPage.svelte';
16
+ export type { ShellSlotItem } from './slot-item.js';
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EACL,kCAAkC,EAClC,KAAK,uCAAuC,EAC5C,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,0BAA0B,EAC/B,iBAAiB,EACjB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EACL,kCAAkC,EAClC,KAAK,uCAAuC,EAC5C,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ShellSlot } from '../workspace/admin-shell/slots.js';
3
+ /**
4
+ * A host item that lives in a shell slot and that users can move to another
5
+ * slot from the layout editor (`ShellLayout.placements`). Prefer this over the
6
+ * `slots` prop when the content should be individually movable.
7
+ */
8
+ export interface ShellSlotItem {
9
+ /**
10
+ * Stable id; keep it unchanged across releases because a stored layout
11
+ * refers to it. Must not collide with `dock:` or `slot:` ids.
12
+ */
13
+ id: string;
14
+ /** Accessible, user-facing name shown in the layout editor. */
15
+ label: string;
16
+ /** Default slot; a user's layout can move it elsewhere. */
17
+ slot: ShellSlot;
18
+ /** The content. */
19
+ render: Snippet;
20
+ }
21
+ //# sourceMappingURL=slot-item.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-item.d.ts","sourceRoot":"","sources":["../../../src/components/app/slot-item.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,IAAI,EAAE,SAAS,CAAC;IAChB,mBAAmB;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -4,7 +4,14 @@ import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
4
  import { Button } from '@happyvertical/smrt-ui/ui';
5
5
  import { M } from '../../i18n/strings.audit.js';
6
6
  import RecordHistory from './RecordHistory.svelte';
7
- import type { AuditHistoryEntry, AuditListFilter } from './types.js';
7
+ import type {
8
+ AuditEntryLabel,
9
+ AuditFieldLabel,
10
+ AuditHistoryEntry,
11
+ AuditListFilter,
12
+ AuditResourceHref,
13
+ AuditValueFormatter,
14
+ } from './types.js';
8
15
 
9
16
  interface Props {
10
17
  /** Authorized data only. For server paging use onfilter to reload the authorized page. */
@@ -15,8 +22,31 @@ interface Props {
15
22
  error?: string | null;
16
23
  /** Requests an authorized server page for the submitted filters instead of filtering locally. */
17
24
  onfilter?: (filter: AuditListFilter) => void;
25
+ /** Shows the built-in person, record and date filters. */
26
+ showFilters?: boolean;
27
+ /** Resolves friendly resource text for each authorized entry. */
28
+ resourceLabel?: AuditEntryLabel;
29
+ /** Resolves an already-authorized local or HTTP(S) destination for each entry. */
30
+ resourceHref?: AuditResourceHref;
31
+ /** Resolves friendly action text for each authorized entry. */
32
+ actionLabel?: AuditEntryLabel;
33
+ /** Resolves friendly field text for each authorized entry change. */
34
+ fieldLabel?: AuditFieldLabel;
35
+ /** Formats before/after values as escaped text. */
36
+ formatValue?: AuditValueFormatter;
18
37
  }
19
- let { entries, loading = false, error = null, onfilter }: Props = $props();
38
+ let {
39
+ entries,
40
+ loading = false,
41
+ error = null,
42
+ onfilter,
43
+ showFilters = true,
44
+ resourceLabel,
45
+ resourceHref,
46
+ actionLabel,
47
+ fieldLabel,
48
+ formatValue,
49
+ }: Props = $props();
20
50
  const { t } = useI18n();
21
51
  let profileId = $state('');
22
52
  let resourceId = $state('');
@@ -51,14 +81,25 @@ function apply() {
51
81
  </script>
52
82
 
53
83
  <section aria-label={t(M['ui.audit.list'])}>
54
- <Form onsubmit={apply} class="smrt-audit-filters">
55
- <FormGroup label={t(M['ui.audit.person'])}><Input bind:value={profileId} /></FormGroup>
56
- <FormGroup label={t(M['ui.audit.record'])}><Input bind:value={resourceId} /></FormGroup>
57
- <FormGroup label={t(M['ui.audit.from'])}><Input type="date" bind:value={from} max={to || undefined} /></FormGroup>
58
- <FormGroup label={t(M['ui.audit.to'])}><Input type="date" bind:value={to} min={from || undefined} /></FormGroup>
59
- <Button type="submit" disabled={loading}>{t(M['ui.audit.filter'])}</Button>
60
- </Form>
61
- <RecordHistory entries={visible} {loading} {error} />
84
+ {#if showFilters}
85
+ <Form onsubmit={apply} class="smrt-audit-filters">
86
+ <FormGroup label={t(M['ui.audit.person'])}><Input bind:value={profileId} /></FormGroup>
87
+ <FormGroup label={t(M['ui.audit.record'])}><Input bind:value={resourceId} /></FormGroup>
88
+ <FormGroup label={t(M['ui.audit.from'])}><Input type="date" bind:value={from} max={to || undefined} /></FormGroup>
89
+ <FormGroup label={t(M['ui.audit.to'])}><Input type="date" bind:value={to} min={from || undefined} /></FormGroup>
90
+ <Button type="submit" disabled={loading}>{t(M['ui.audit.filter'])}</Button>
91
+ </Form>
92
+ {/if}
93
+ <RecordHistory
94
+ entries={visible}
95
+ {loading}
96
+ {error}
97
+ {resourceLabel}
98
+ {resourceHref}
99
+ {actionLabel}
100
+ {fieldLabel}
101
+ {formatValue}
102
+ />
62
103
  </section>
63
104
 
64
105
  <style>
@@ -1,4 +1,4 @@
1
- import type { AuditHistoryEntry, AuditListFilter } from './types.js';
1
+ import type { AuditEntryLabel, AuditFieldLabel, AuditHistoryEntry, AuditListFilter, AuditResourceHref, AuditValueFormatter } from './types.js';
2
2
  interface Props {
3
3
  /** Authorized data only. For server paging use onfilter to reload the authorized page. */
4
4
  entries: AuditHistoryEntry[];
@@ -8,6 +8,18 @@ interface Props {
8
8
  error?: string | null;
9
9
  /** Requests an authorized server page for the submitted filters instead of filtering locally. */
10
10
  onfilter?: (filter: AuditListFilter) => void;
11
+ /** Shows the built-in person, record and date filters. */
12
+ showFilters?: boolean;
13
+ /** Resolves friendly resource text for each authorized entry. */
14
+ resourceLabel?: AuditEntryLabel;
15
+ /** Resolves an already-authorized local or HTTP(S) destination for each entry. */
16
+ resourceHref?: AuditResourceHref;
17
+ /** Resolves friendly action text for each authorized entry. */
18
+ actionLabel?: AuditEntryLabel;
19
+ /** Resolves friendly field text for each authorized entry change. */
20
+ fieldLabel?: AuditFieldLabel;
21
+ /** Formats before/after values as escaped text. */
22
+ formatValue?: AuditValueFormatter;
11
23
  }
12
24
  declare const AuditList: import("svelte").Component<Props, {}, "">;
13
25
  type AuditList = ReturnType<typeof AuditList>;
@@ -1 +1 @@
1
- {"version":3,"file":"AuditList.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/audit/AuditList.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGrE,UAAU,KAAK;IACb,0FAA0F;IAC1F,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,sFAAsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;CAC9C;AA0DD,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AuditList.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/audit/AuditList.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAK;IACb,0FAA0F;IAC1F,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,sFAAsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,kFAAkF;IAClF,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,mDAAmD;IACnD,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAuED,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -1,7 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
3
  import { M } from '../../i18n/strings.audit.js';
4
- import type { AuditHistoryEntry } from './types.js';
4
+ import type {
5
+ AuditEntryLabel,
6
+ AuditFieldLabel,
7
+ AuditHistoryEntry,
8
+ AuditResourceHref,
9
+ AuditValueFormatter,
10
+ } from './types.js';
5
11
 
6
12
  interface Props {
7
13
  /** Authorized entries. This component never fetches or authorizes records. */
@@ -16,6 +22,16 @@ interface Props {
16
22
  error?: string | null;
17
23
  /** Overrides the translated accessible label for the history section. */
18
24
  label?: string;
25
+ /** Resolves friendly resource text for each authorized entry. */
26
+ resourceLabel?: AuditEntryLabel;
27
+ /** Resolves an already-authorized local or HTTP(S) destination for each entry. */
28
+ resourceHref?: AuditResourceHref;
29
+ /** Resolves friendly action text for each authorized entry. */
30
+ actionLabel?: AuditEntryLabel;
31
+ /** Resolves friendly field text for each authorized entry change. */
32
+ fieldLabel?: AuditFieldLabel;
33
+ /** Formats before/after values as escaped text. */
34
+ formatValue?: AuditValueFormatter;
19
35
  }
20
36
  let {
21
37
  entries,
@@ -24,6 +40,11 @@ let {
24
40
  loading = false,
25
41
  error = null,
26
42
  label,
43
+ resourceLabel,
44
+ resourceHref,
45
+ actionLabel,
46
+ fieldLabel,
47
+ formatValue,
27
48
  }: Props = $props();
28
49
  const { locale, t } = useI18n();
29
50
  const visible = $derived(
@@ -35,8 +56,87 @@ const visible = $derived(
35
56
  )
36
57
  .toSorted((a, b) => Date.parse(b.occurredAt) - Date.parse(a.occurredAt)),
37
58
  );
38
- function display(value: unknown): string {
39
- return typeof value === 'string' ? value : (JSON.stringify(value) ?? '');
59
+ function defaultDisplay(value: unknown): string {
60
+ if (typeof value === 'string') return value;
61
+ try {
62
+ return JSON.stringify(value) ?? '';
63
+ } catch {
64
+ try {
65
+ return String(value);
66
+ } catch {
67
+ return '';
68
+ }
69
+ }
70
+ }
71
+ function entryLabel(
72
+ resolver: AuditEntryLabel | undefined,
73
+ entry: AuditHistoryEntry,
74
+ fallback: string,
75
+ ): string {
76
+ if (!resolver) return fallback;
77
+ try {
78
+ const resolved = resolver(entry);
79
+ return typeof resolved === 'string' && resolved.trim()
80
+ ? resolved
81
+ : fallback;
82
+ } catch {
83
+ return fallback;
84
+ }
85
+ }
86
+ function changedFieldLabel(field: string, entry: AuditHistoryEntry): string {
87
+ if (!fieldLabel) return field;
88
+ try {
89
+ const resolved = fieldLabel(field, entry);
90
+ return typeof resolved === 'string' && resolved.trim() ? resolved : field;
91
+ } catch {
92
+ return field;
93
+ }
94
+ }
95
+ function display(
96
+ value: unknown,
97
+ entry: AuditHistoryEntry,
98
+ field: string,
99
+ side: 'before' | 'after',
100
+ ): string {
101
+ if (formatValue) {
102
+ try {
103
+ const resolved = formatValue(value, { entry, field, side });
104
+ if (typeof resolved === 'string') return resolved;
105
+ } catch {
106
+ // Presentation callbacks cannot make an authorized history list fail.
107
+ }
108
+ }
109
+ return defaultDisplay(value);
110
+ }
111
+ function hasUrlControlCharacter(value: string): boolean {
112
+ return Array.from(value).some((character) => {
113
+ const codePoint = character.codePointAt(0) ?? 0;
114
+ return codePoint <= 0x1f || codePoint === 0x7f;
115
+ });
116
+ }
117
+ function safeResourceHref(entry: AuditHistoryEntry): string | null {
118
+ if (!resourceHref) return null;
119
+ let resolved: unknown;
120
+ try {
121
+ resolved = resourceHref(entry);
122
+ } catch {
123
+ return null;
124
+ }
125
+ if (typeof resolved !== 'string') return null;
126
+ const href = resolved.trim();
127
+ if (
128
+ !href ||
129
+ href.startsWith('//') ||
130
+ href.includes('\\') ||
131
+ hasUrlControlCharacter(href)
132
+ )
133
+ return null;
134
+ try {
135
+ const url = new URL(href, 'https://smrt.invalid');
136
+ return url.protocol === 'http:' || url.protocol === 'https:' ? href : null;
137
+ } catch {
138
+ return null;
139
+ }
40
140
  }
41
141
  function date(value: string): string {
42
142
  const parsed = new Date(value);
@@ -50,17 +150,27 @@ function date(value: string): string {
50
150
  {#if error}<p role="alert">{error}</p>{/if}
51
151
  {#if loading}<p role="status">{t(M['ui.audit.loading'])}</p>{/if}
52
152
  {#if !loading && visible.length === 0}<p>{t(M['ui.audit.empty'])}</p>{/if}
53
- <ol>
153
+ <ol role="list">
54
154
  {#each visible as entry (entry.id)}
155
+ {@const resolvedResourceLabel = entryLabel(resourceLabel, entry, `${entry.resourceType} / ${entry.resourceId}`)}
156
+ {@const resolvedResourceHref = safeResourceHref(entry)}
55
157
  <li>
56
- <p><strong>{entry.actorLabel ?? entry.profileId}</strong> · {entry.action} · {entry.resourceType} / {entry.resourceId}</p>
158
+ <p>
159
+ <strong>{entry.actorLabel ?? entry.profileId}</strong>
160
+ · {entryLabel(actionLabel, entry, entry.action)} ·
161
+ {#if resolvedResourceHref}<a href={resolvedResourceHref}>{resolvedResourceLabel}</a>{:else}{resolvedResourceLabel}{/if}
162
+ </p>
57
163
  <time datetime={entry.occurredAt}>{date(entry.occurredAt)}</time>
58
164
  {#if entry.onBehalfOfId}<p>{t(M['ui.audit.behalf'], { actor: entry.onBehalfOfId })}</p>{/if}
59
165
  {#if entry.reason}<p>{t(M['ui.audit.reason'], { reason: entry.reason })}</p>{/if}
60
166
  {#if entry.changes && Object.keys(entry.changes).length}
61
- <ul>
167
+ <ul role="list">
62
168
  {#each Object.entries(entry.changes) as [field, change] (field)}
63
- <li>{t(M['ui.audit.change'], { field, before: display(change.before), after: display(change.after) })}</li>
169
+ <li>{t(M['ui.audit.change'], {
170
+ field: changedFieldLabel(field, entry),
171
+ before: display(change.before, entry, field, 'before'),
172
+ after: display(change.after, entry, field, 'after'),
173
+ })}</li>
64
174
  {/each}
65
175
  </ul>
66
176
  {/if}
@@ -70,7 +180,55 @@ function date(value: string): string {
70
180
  </section>
71
181
 
72
182
  <style>
73
- .smrt-record-history ol { display: grid; gap: var(--smrt-spacing-4); padding-inline-start: var(--smrt-spacing-6); }
74
- .smrt-record-history p { margin-block: var(--smrt-spacing-1); overflow-wrap: anywhere; }
75
- .smrt-record-history time { color: var(--smrt-color-on-surface-variant); }
183
+ .smrt-record-history > ol {
184
+ display: grid;
185
+ margin: 0;
186
+ padding: 0;
187
+ list-style: none;
188
+ }
189
+
190
+ .smrt-record-history > ol > li {
191
+ min-inline-size: 0;
192
+ padding-block: var(--smrt-spacing-3);
193
+ border-block-end: 1px solid var(--smrt-color-outline-variant);
194
+ overflow-wrap: anywhere;
195
+ }
196
+
197
+ .smrt-record-history > ol > li:first-child { padding-block-start: 0; }
198
+ .smrt-record-history > ol > li:last-child {
199
+ padding-block-end: 0;
200
+ border-block-end: 0;
201
+ }
202
+
203
+ .smrt-record-history p { margin-block: var(--smrt-spacing-1); }
204
+ .smrt-record-history time {
205
+ display: block;
206
+ color: var(--smrt-color-on-surface-variant);
207
+ font: var(--smrt-typography-label-medium-font);
208
+ }
209
+
210
+ .smrt-record-history a {
211
+ color: var(--smrt-color-on-surface);
212
+ text-decoration-color: var(--smrt-color-primary);
213
+ text-decoration-thickness: .08em;
214
+ text-underline-offset: .18em;
215
+ }
216
+
217
+ .smrt-record-history a:hover { text-decoration-thickness: .14em; }
218
+ .smrt-record-history a:focus-visible {
219
+ border-radius: var(--smrt-radius-extra-small, .25rem);
220
+ outline: 2px solid var(--smrt-color-primary);
221
+ outline-offset: 2px;
222
+ }
223
+
224
+ .smrt-record-history ul {
225
+ display: grid;
226
+ gap: var(--smrt-spacing-1);
227
+ margin-block: var(--smrt-spacing-2) 0;
228
+ padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
229
+ border-inline-start: 2px solid var(--smrt-color-outline);
230
+ border-radius: var(--smrt-radius-small, .375rem);
231
+ background: var(--smrt-color-surface-container-low);
232
+ list-style: none;
233
+ }
76
234
  </style>
@@ -1,4 +1,4 @@
1
- import type { AuditHistoryEntry } from './types.js';
1
+ import type { AuditEntryLabel, AuditFieldLabel, AuditHistoryEntry, AuditResourceHref, AuditValueFormatter } from './types.js';
2
2
  interface Props {
3
3
  /** Authorized entries. This component never fetches or authorizes records. */
4
4
  entries: AuditHistoryEntry[];
@@ -12,6 +12,16 @@ interface Props {
12
12
  error?: string | null;
13
13
  /** Overrides the translated accessible label for the history section. */
14
14
  label?: string;
15
+ /** Resolves friendly resource text for each authorized entry. */
16
+ resourceLabel?: AuditEntryLabel;
17
+ /** Resolves an already-authorized local or HTTP(S) destination for each entry. */
18
+ resourceHref?: AuditResourceHref;
19
+ /** Resolves friendly action text for each authorized entry. */
20
+ actionLabel?: AuditEntryLabel;
21
+ /** Resolves friendly field text for each authorized entry change. */
22
+ fieldLabel?: AuditFieldLabel;
23
+ /** Formats before/after values as escaped text. */
24
+ formatValue?: AuditValueFormatter;
15
25
  }
16
26
  declare const RecordHistory: import("svelte").Component<Props, {}, "">;
17
27
  type RecordHistory = ReturnType<typeof RecordHistory>;
@@ -1 +1 @@
1
- {"version":3,"file":"RecordHistory.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/audit/RecordHistory.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,UAAU,KAAK;IACb,8EAA8E;IAC9E,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,wFAAwF;IACxF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA8DD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"RecordHistory.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/audit/RecordHistory.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAGpB,UAAU,KAAK;IACb,8EAA8E;IAC9E,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,wFAAwF;IACxF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,kFAAkF;IAClF,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,mDAAmD;IACnD,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AA4JD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export { default as AuditList } from './AuditList.svelte';
2
2
  export { default as RecordHistory } from './RecordHistory.svelte';
3
- export type { AuditHistoryEntry, AuditListFilter } from './types.js';
3
+ export type { AuditEntryLabel, AuditFieldLabel, AuditHistoryEntry, AuditListFilter, AuditResourceHref, AuditValueFormatContext, AuditValueFormatter, } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/audit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/audit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
@@ -20,4 +20,22 @@ export interface AuditListFilter {
20
20
  from: string;
21
21
  to: string;
22
22
  }
23
+ /** Resolves presentation text for one already-authorized audit entry. */
24
+ export type AuditEntryLabel = (entry: AuditHistoryEntry) => string;
25
+ /**
26
+ * Resolves an already-authorized destination for an audit entry.
27
+ *
28
+ * The component accepts local paths and explicit HTTP(S) URLs only. Consumers
29
+ * must still authorize the referenced resource before exposing its href.
30
+ */
31
+ export type AuditResourceHref = (entry: AuditHistoryEntry) => string | null | undefined;
32
+ /** Resolves a presentation label for one changed field. */
33
+ export type AuditFieldLabel = (field: string, entry: AuditHistoryEntry) => string;
34
+ export interface AuditValueFormatContext {
35
+ entry: AuditHistoryEntry;
36
+ field: string;
37
+ side: 'before' | 'after';
38
+ }
39
+ /** Formats an audit value as text. Formatter output is never rendered as HTML. */
40
+ export type AuditValueFormatter = (value: unknown, context: AuditValueFormatContext) => string;
23
41
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/audit/types.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/audit/types.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,yEAAyE;AACzE,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,iBAAiB,KACrB,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAE/B,2DAA2D;AAC3D,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,iBAAiB,KACrB,MAAM,CAAC;AAEZ,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC1B;AAED,kFAAkF;AAClF,MAAM,MAAM,mBAAmB,GAAG,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,uBAAuB,KAC7B,MAAM,CAAC"}