@jini-ai/ui 0.4.4 → 0.4.6

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 (165) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +20 -0
  3. package/dist/core/theme/default.d.ts +4 -0
  4. package/dist/core/theme/default.js +11 -0
  5. package/dist/core/theme/stylesheet.d.ts +8 -0
  6. package/dist/core/theme/stylesheet.js +14 -0
  7. package/dist/core/theme/types.d.ts +39 -0
  8. package/dist/core/theme/types.js +2 -0
  9. package/dist/core/theme/validation.d.ts +15 -0
  10. package/dist/core/theme/validation.js +55 -0
  11. package/dist/features/admin-widgets/components/Select/Select.d.ts +11 -3
  12. package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +8 -13
  13. package/dist/features/admin-widgets/components/Select/Select.hooks.js +37 -215
  14. package/dist/features/admin-widgets/components/Select/Select.js +4 -3
  15. package/dist/features/appearance/constants.js +1 -1
  16. package/dist/features/connectors/rules.d.ts +1 -6
  17. package/dist/features/connectors/types.d.ts +1 -6
  18. package/dist/features/connectors/types.js +1 -6
  19. package/dist/features/execution/http-adapter.d.ts +111 -0
  20. package/dist/features/execution/http-adapter.js +160 -0
  21. package/dist/features/execution/index.d.ts +4 -0
  22. package/dist/features/execution/index.js +2 -0
  23. package/dist/features/execution/react/hooks/use-execution-credential.hooks.d.ts +232 -0
  24. package/dist/features/execution/react/hooks/use-execution-credential.hooks.js +217 -0
  25. package/dist/features/html-editor/canvas-style.d.ts +1 -1
  26. package/dist/features/html-editor/canvas-style.js +2 -2
  27. package/dist/features/i18n/dictionaries/settings-dialog.ar.js +2 -0
  28. package/dist/features/i18n/dictionaries/settings-dialog.bn.js +2 -0
  29. package/dist/features/i18n/dictionaries/settings-dialog.de.js +2 -0
  30. package/dist/features/i18n/dictionaries/settings-dialog.en.js +2 -0
  31. package/dist/features/i18n/dictionaries/settings-dialog.es.js +2 -0
  32. package/dist/features/i18n/dictionaries/settings-dialog.fa.js +2 -0
  33. package/dist/features/i18n/dictionaries/settings-dialog.fr.js +2 -0
  34. package/dist/features/i18n/dictionaries/settings-dialog.hi.js +2 -0
  35. package/dist/features/i18n/dictionaries/settings-dialog.hu.js +2 -0
  36. package/dist/features/i18n/dictionaries/settings-dialog.id.js +2 -0
  37. package/dist/features/i18n/dictionaries/settings-dialog.it.js +2 -0
  38. package/dist/features/i18n/dictionaries/settings-dialog.ja.js +2 -0
  39. package/dist/features/i18n/dictionaries/settings-dialog.ko.js +2 -0
  40. package/dist/features/i18n/dictionaries/settings-dialog.pl.js +2 -0
  41. package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +2 -0
  42. package/dist/features/i18n/dictionaries/settings-dialog.ru.js +2 -0
  43. package/dist/features/i18n/dictionaries/settings-dialog.th.js +2 -0
  44. package/dist/features/i18n/dictionaries/settings-dialog.tr.js +2 -0
  45. package/dist/features/i18n/dictionaries/settings-dialog.uk.js +2 -0
  46. package/dist/features/i18n/dictionaries/settings-dialog.ur.js +2 -0
  47. package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +2 -0
  48. package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +2 -0
  49. package/dist/features/i18n/dictionary-translator.d.ts +5 -2
  50. package/dist/features/i18n/dictionary-translator.js +1 -1
  51. package/dist/features/i18n/index.d.ts +1 -1
  52. package/dist/features/i18n/index.js +1 -1
  53. package/dist/features/i18n/template-i18n.d.ts +11 -0
  54. package/dist/features/i18n/template-i18n.js +12 -0
  55. package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +1 -1
  56. package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +1 -1
  57. package/dist/features/lexical-rich-text-editor/deserialize.d.ts +0 -5
  58. package/dist/features/lexical-rich-text-editor/deserialize.js +0 -5
  59. package/dist/features/lexical-rich-text-editor/mention-node.d.ts +5 -17
  60. package/dist/features/lexical-rich-text-editor/mention-node.js +5 -17
  61. package/dist/features/lexical-rich-text-editor/mention-parser.d.ts +2 -4
  62. package/dist/features/lexical-rich-text-editor/serialize.d.ts +0 -4
  63. package/dist/features/lexical-rich-text-editor/serialize.js +0 -4
  64. package/dist/features/lexical-rich-text-editor/types.d.ts +3 -13
  65. package/dist/features/lexical-rich-text-editor/types.js +3 -13
  66. package/dist/features/mcp-ui/early-message-buffer.d.ts +1 -2
  67. package/dist/features/mcp-ui/early-message-buffer.js +1 -2
  68. package/dist/features/mcp-ui/escape.d.ts +4 -0
  69. package/dist/features/mcp-ui/escape.js +2 -9
  70. package/dist/features/mcp-ui/index.d.ts +1 -0
  71. package/dist/features/mcp-ui/index.js +1 -0
  72. package/dist/features/mcp-ui/protocol.d.ts +2 -5
  73. package/dist/features/mcp-ui/protocol.js +2 -5
  74. package/dist/features/mcp-ui/resource.d.ts +47 -7
  75. package/dist/features/mcp-ui/resource.js +5 -8
  76. package/dist/features/mcp-ui/secret-card.d.ts +115 -0
  77. package/dist/features/mcp-ui/secret-card.js +137 -0
  78. package/dist/features/mcp-ui/surfaces/confirmation.js +5 -0
  79. package/dist/features/mcp-ui/surfaces/fields.d.ts +5 -2
  80. package/dist/features/mcp-ui/surfaces/fields.js +3 -1
  81. package/dist/features/mcp-ui/surfaces/form.d.ts +4 -1
  82. package/dist/features/mcp-ui/surfaces/form.js +65 -3
  83. package/dist/features/mcp-ui/surfaces/outcome.d.ts +2 -0
  84. package/dist/features/mcp-ui/surfaces/text-input.d.ts +14 -6
  85. package/dist/features/mcp-ui/surfaces/text-input.js +16 -2
  86. package/dist/features/mcp-ui/surfaces/tokens.js +1 -1
  87. package/dist/features/memory/react/components/MemoryConnectedPanel.d.ts +1 -1
  88. package/dist/features/memory/react/components/MemoryConnectedPanel.js +0 -1
  89. package/dist/features/panel-kit/helpers/format-timestamp.d.ts +5 -3
  90. package/dist/features/panel-kit/helpers/format-timestamp.js +13 -4
  91. package/dist/features/panel-kit/helpers/permissions.js +2 -0
  92. package/dist/features/panel-kit/helpers/resolve-active-tab-id.js +2 -0
  93. package/dist/features/panel-kit/helpers/standing-draft-local-backup.d.ts +51 -0
  94. package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +77 -0
  95. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +8 -0
  96. package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +10 -0
  97. package/dist/features/panel-kit/hooks/use-async-action.hooks.d.ts +1 -0
  98. package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.d.ts +1 -0
  99. package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -0
  100. package/dist/features/panel-kit/hooks/use-serial-writes.hooks.d.ts +8 -0
  101. package/dist/features/panel-kit/hooks/use-serial-writes.hooks.js +3 -0
  102. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.d.ts +211 -0
  103. package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +382 -0
  104. package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.d.ts +179 -0
  105. package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +300 -0
  106. package/dist/features/panel-kit/index.d.ts +7 -1
  107. package/dist/features/panel-kit/index.js +5 -1
  108. package/dist/features/resource-dashboard/types.d.ts +8 -41
  109. package/dist/features/resource-dashboard/types.js +3 -26
  110. package/dist/features/tab-strip/constants.d.ts +3 -4
  111. package/dist/features/tab-strip/constants.js +3 -4
  112. package/dist/features/tab-strip/dependencies.d.ts +1 -6
  113. package/dist/features/tab-strip/dependencies.js +1 -6
  114. package/dist/features/tab-strip/react/components/TabBar.d.ts +33 -1
  115. package/dist/features/tab-strip/react/components/TabBar.hooks.d.ts +57 -5
  116. package/dist/features/tab-strip/react/components/TabBar.hooks.js +63 -5
  117. package/dist/features/tab-strip/react/components/TabBar.js +12 -1
  118. package/dist/features/tab-strip/rules.d.ts +4 -17
  119. package/dist/features/tab-strip/rules.js +4 -17
  120. package/dist/features/tab-strip/types.d.ts +2 -13
  121. package/dist/icon-name.d.ts +1 -1
  122. package/dist/icon-name.js +1 -0
  123. package/dist/index.d.ts +42 -417
  124. package/dist/index.js +42 -430
  125. package/dist/react/components/CustomSelect.d.ts +6 -2
  126. package/dist/react/components/CustomSelect.js +20 -91
  127. package/dist/react/components/Icon.d.ts +6 -0
  128. package/dist/react/components/Icon.js +12 -0
  129. package/dist/react/components/select-popover.d.ts +106 -0
  130. package/dist/react/components/select-popover.js +210 -0
  131. package/dist/renderers/sandbox-scripts.d.ts +29 -0
  132. package/dist/renderers/sandbox-scripts.js +127 -0
  133. package/dist/renderers/sandboxed-document.js +3 -86
  134. package/dist/renderers/srcdoc/build.d.ts +10 -17
  135. package/dist/renderers/srcdoc/build.js +31 -234
  136. package/dist/styles/variables.css +2 -2
  137. package/dist/theme/apply.d.ts +1 -1
  138. package/dist/theme/apply.js +2 -2
  139. package/dist/theme/color-scheme.d.ts +1 -1
  140. package/dist/theme/default.d.ts +1 -3
  141. package/dist/theme/default.js +2 -10
  142. package/dist/theme/index.d.ts +3 -3
  143. package/dist/theme/index.js +2 -2
  144. package/dist/theme/stylesheet.d.ts +1 -7
  145. package/dist/theme/stylesheet.js +2 -13
  146. package/dist/theme/types.d.ts +1 -38
  147. package/dist/theme/types.js +2 -1
  148. package/dist/theme/validation.d.ts +1 -14
  149. package/dist/theme/validation.js +2 -54
  150. package/dist/utils/endpoint-policy.d.ts +21 -39
  151. package/dist/utils/endpoint-policy.js +19 -107
  152. package/dist/utils/file-system-errors.d.ts +1 -2
  153. package/dist/utils/file-system-errors.js +1 -2
  154. package/dist/utils/ime-composing.d.ts +0 -3
  155. package/dist/utils/ime-composing.js +0 -3
  156. package/dist/utils/platform.d.ts +1 -4
  157. package/dist/utils/platform.js +1 -4
  158. package/dist/utils/smooth-scroll-to-top.d.ts +1 -3
  159. package/dist/utils/smooth-scroll-to-top.js +3 -13
  160. package/dist/utils/timezone.js +1 -5
  161. package/dist/utils/uuid.d.ts +0 -3
  162. package/dist/utils/uuid.js +0 -3
  163. package/dist/utils/visual-stability.d.ts +1 -3
  164. package/dist/utils/visual-stability.js +1 -3
  165. package/package.json +74 -15
@@ -1,6 +1,6 @@
1
- export type { AdminTheme, AdminThemeColors, AdminThemeTarget, AdminThemeDocument, ColorScheme, ColorSchemePreference, MatchMediaPort } from './types.js';
2
- export { defaultAdminTheme } from './default.js';
3
- export { validateAdminTheme } from './validation.js';
1
+ export type { AdminTheme, AdminThemeColors, AdminThemeTarget, AdminThemeDocument, ColorScheme, ColorSchemePreference, MatchMediaPort } from '../core/theme/types.js';
2
+ export { defaultAdminTheme } from '../core/theme/default.js';
3
+ export { validateAdminTheme } from '../core/theme/validation.js';
4
4
  export { applyAdminTheme } from './apply.js';
5
5
  export { resolveColorScheme } from './color-scheme.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
- export { defaultAdminTheme } from './default.js';
2
- export { validateAdminTheme } from './validation.js';
1
+ export { defaultAdminTheme } from '../core/theme/default.js';
2
+ export { validateAdminTheme } from '../core/theme/validation.js';
3
3
  export { applyAdminTheme } from './apply.js';
4
4
  export { resolveColorScheme } from './color-scheme.js';
5
5
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,2 @@
1
- import type { AdminTheme, ColorScheme } from './types.js';
2
- /** Internal document adapter: isolated previews cannot inherit the admin's stylesheet. */
3
- export declare function renderAdminThemeVariables({ theme }: {
4
- readonly theme: AdminTheme;
5
- }, { colorScheme }?: {
6
- readonly colorScheme?: ColorScheme;
7
- }): string;
1
+ export * from '../core/theme/stylesheet.js';
8
2
  //# sourceMappingURL=stylesheet.d.ts.map
@@ -1,14 +1,3 @@
1
- import { adminThemeColorKeys, validateAdminTheme } from './validation.js';
2
- import { defaultAdminTheme } from './default.js';
3
- /** Internal document adapter: isolated previews cannot inherit the admin's stylesheet. */
4
- export function renderAdminThemeVariables({ theme }, { colorScheme = 'light' } = {}) {
5
- if (colorScheme !== 'light' && colorScheme !== 'dark')
6
- throw new TypeError('Invalid resolved color scheme');
7
- const checked = validateAdminTheme({ theme });
8
- if (!checked.valid)
9
- throw new TypeError(checked.errors.join('; '));
10
- const values = adminThemeColorKeys.map((key) => `--jini-${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}:${theme[colorScheme][key]};`);
11
- values.push(`--jini-font-body:${theme.fonts.body};`, `--jini-font-heading:${theme.fonts.heading};`, `--jini-font-mono:${theme.fonts.mono ?? defaultAdminTheme.fonts.mono};`, `--jini-radius:${theme.radius ?? defaultAdminTheme.radius};`);
12
- return `:root{color-scheme:${colorScheme};${values.join('')}}`;
13
- }
1
+ // Compatibility path; the shared implementation lives in core.
2
+ export * from '../core/theme/stylesheet.js';
14
3
  //# sourceMappingURL=stylesheet.js.map
@@ -1,39 +1,2 @@
1
- /** The intentionally small palette every admin theme supplies in both schemes. */
2
- export interface AdminThemeColors {
3
- readonly primary: string;
4
- readonly primaryInk: string;
5
- readonly bg: string;
6
- readonly surface: string;
7
- readonly text: string;
8
- readonly muted: string;
9
- readonly border: string;
10
- readonly danger: string;
11
- readonly success: string;
12
- readonly warning: string;
13
- }
14
- /** Product fonts and palette are data. Font loading URLs are stylesheet URLs. */
15
- export interface AdminTheme {
16
- readonly name: string;
17
- readonly fonts: {
18
- readonly body: string;
19
- readonly heading: string;
20
- readonly mono?: string;
21
- readonly load?: readonly string[];
22
- };
23
- readonly light: AdminThemeColors;
24
- readonly dark: AdminThemeColors;
25
- readonly radius?: string;
26
- /** Unitless spacing multiplier, from 0.5 to 2. */
27
- readonly density?: number;
28
- }
29
- export type ColorScheme = 'light' | 'dark';
30
- export type ColorSchemePreference = ColorScheme | 'system';
31
- export type MatchMediaPort = (query: string) => {
32
- readonly matches: boolean;
33
- addEventListener?: (type: 'change', listener: () => void) => void;
34
- removeEventListener?: (type: 'change', listener: () => void) => void;
35
- };
36
- /** Only these DOM capabilities are needed; no ambient document is consulted. */
37
- export type AdminThemeTarget = Pick<HTMLElement, 'style' | 'getAttribute' | 'setAttribute' | 'removeAttribute'>;
38
- export type AdminThemeDocument = Pick<Document, 'head' | 'createElement' | 'baseURI'>;
1
+ export * from '../core/theme/types.js';
39
2
  //# sourceMappingURL=types.d.ts.map
@@ -1,2 +1,3 @@
1
- export {};
1
+ // Compatibility path; the shared implementation lives in core.
2
+ export * from '../core/theme/types.js';
2
3
  //# sourceMappingURL=types.js.map
@@ -1,15 +1,2 @@
1
- import type { AdminTheme } from './types.js';
2
- export declare const adminThemeColorKeys: readonly ["primary", "primaryInk", "bg", "surface", "text", "muted", "border", "danger", "success", "warning"];
3
- type Validation = {
4
- readonly valid: true;
5
- readonly theme: AdminTheme;
6
- } | {
7
- readonly valid: false;
8
- readonly errors: readonly string[];
9
- };
10
- /** Validate serializable theme data before any DOM or font-loading effect. */
11
- export declare function validateAdminTheme({ theme }: {
12
- readonly theme: unknown;
13
- }): Validation;
14
- export {};
1
+ export * from '../core/theme/validation.js';
15
2
  //# sourceMappingURL=validation.d.ts.map
@@ -1,55 +1,3 @@
1
- export const adminThemeColorKeys = ['primary', 'primaryInk', 'bg', 'surface', 'text', 'muted', 'border', 'danger', 'success', 'warning'];
2
- const record = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
3
- const safeValue = (value) => typeof value === 'string' && value.trim().length > 0
4
- && value.length <= 512 && !/[;{}\x00-\x1f<>\\]/.test(value) && !/url\s*\(|\/\*|\*\//i.test(value);
5
- // Concrete palette values, deliberately excluding dependent var()/url() expressions.
6
- const component = '(?:[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:%|deg|rad|grad|turn)?|none)';
7
- const components = new RegExp(`^(?:rgb|rgba|hsl|hsla|oklch|oklab|lab|lch)\\(\\s*${component}(?:\\s*,\\s*|\\s+)${component}(?:\\s*,\\s*|\\s+)${component}(?:(?:\\s*[,/]\\s*)${component})?\\s*\\)$`, 'i');
8
- const colorValue = (value) => safeValue(value)
9
- && (/^#[\da-f]{3}(?:[\da-f]{1}|[\da-f]{3}|[\da-f]{5})?$/i.test(value)
10
- || components.test(value)
11
- || /^(?:transparent|black|white|red|green|blue|gray|grey)$/i.test(value));
12
- /** Validate serializable theme data before any DOM or font-loading effect. */
13
- export function validateAdminTheme({ theme }) {
14
- const errors = [];
15
- if (!record(theme))
16
- return { valid: false, errors: ['Theme must be an object'] };
17
- if (!safeValue(theme.name))
18
- errors.push('name must be a nonempty safe string');
19
- const fonts = theme.fonts;
20
- if (!record(fonts) || !safeValue(fonts.body) || !safeValue(fonts.heading) || (fonts.mono !== undefined && !safeValue(fonts.mono))) {
21
- errors.push('fonts must provide body and heading font stacks');
22
- }
23
- if (record(fonts) && fonts.load !== undefined) {
24
- if (!Array.isArray(fonts.load) || fonts.load.length > 16 || !fonts.load.every((url) => {
25
- if (typeof url !== 'string' || url.length > 4096 || /[\x00-\x20<>]/.test(url))
26
- return false;
27
- try {
28
- return new URL(url, 'https://font-base.invalid').protocol === 'https:';
29
- }
30
- catch {
31
- return false;
32
- }
33
- }))
34
- errors.push('fonts.load must contain relative or HTTPS stylesheet URLs');
35
- }
36
- for (const scheme of ['light', 'dark']) {
37
- const colors = theme[scheme];
38
- if (!record(colors)) {
39
- errors.push(`${scheme} must be a complete palette`);
40
- continue;
41
- }
42
- for (const key of adminThemeColorKeys)
43
- if (!Object.hasOwn(colors, key) || !colorValue(colors[key]))
44
- errors.push(`${scheme}.${key} must be a concrete CSS color`);
45
- for (const key of Object.keys(colors))
46
- if (!adminThemeColorKeys.includes(key))
47
- errors.push(`${scheme}.${key} is not a theme color`);
48
- }
49
- if (theme.radius !== undefined && (typeof theme.radius !== 'string' || !/^(?:0|\d+(?:\.\d+)?(?:px|rem|em))$/.test(theme.radius)))
50
- errors.push('radius must be a nonnegative CSS length');
51
- if (theme.density !== undefined && (typeof theme.density !== 'number' || !Number.isFinite(theme.density) || theme.density < 0.5 || theme.density > 2))
52
- errors.push('density must be between 0.5 and 2');
53
- return errors.length ? { valid: false, errors } : { valid: true, theme: theme };
54
- }
1
+ // Compatibility path; the shared implementation lives in core.
2
+ export * from '../core/theme/validation.js';
55
3
  //# sourceMappingURL=validation.js.map
@@ -2,37 +2,25 @@
2
2
  * One place that decides whether an operator-supplied endpoint URL is
3
3
  * acceptable, shared by every tab that takes one.
4
4
  *
5
- * ## Why this is a copy rather than an import
6
- *
7
- * `@jini-ai/agent-runtime`'s `providers/connection-guard.ts` already implements
8
- * this policy, and it stays the richer implementation: it adds
9
- * `validateBaseUrlResolved`, which resolves DNS and re-checks every address the
10
- * host would actually connect to, catching `internal.example.com -> 10.0.0.5`
11
- * that no string inspection can. That function needs `node:dns`, and this file
12
- * has ZERO imports of its own precisely so it can run in a browser bundle
13
- * — the same reasoning `features/execution/types.ts` gives for mirroring that
14
- * package's shapes instead of depending on it. Importing it here would pull a
15
- * Node-only module into every host that renders a settings dialog.
16
- *
17
- * So the split is deliberate and layered, not accidental duplication:
18
- *
19
- * - **Here (sync, browser-safe):** scheme allow-list plus a literal-hostname
20
- * block-list. Runs in render logic, on every keystroke, in any runtime.
21
- * - **`connection-guard.ts` (async, Node):** the same check plus DNS
22
- * resolution, run where a connection is actually attempted.
23
- *
24
- * The predicates below are ported from that file's synchronous half verbatim
25
- * in behavior. **If one side's block-list changes, change the other.** A gap
26
- * between them is a request the UI accepted and the runtime then refused, or
27
- * worse, the reverse.
28
- *
29
- * That instruction is now enforced rather than merely written down:
30
- * `__tests__/utils/endpoint-policy.parity.test.ts` runs both implementations
31
- * over the cross-product of address forms and whitespace padding and fails on
32
- * any disagreement. It exists because the sentence above was not enough — the
33
- * two drifted on Unicode whitespace (this side trimmed, the other did not), and
34
- * a hand-written 46-URL audit corpus rated them identical because it happened
35
- * not to contain a padded case. A corpus only proves the axis it samples.
5
+ * ## Why the literal policy uses a browser-safe platform leaf
6
+ *
7
+ * `@jini-ai/platform/net/endpoint-policy` owns hostname normalization and
8
+ * classification for both this UI and the runtime's connection guard. Importing
9
+ * the Node `platform/net` barrel would bring `node:net` into browser settings
10
+ * dialogs, so this entry has no Node effects or dependencies.
11
+ *
12
+ * The split remains deliberate and layered:
13
+ * - Here (sync, browser-safe): scheme allow-list plus the shared literal-host
14
+ * block-list, run on every keystroke.
15
+ * - `connection-guard.ts` (async, Node): the same hostname policy plus DNS
16
+ * resolution, re-checking every address the host actually connects to. That
17
+ * catches `internal.example.com -> 10.0.0.5`, which string inspection cannot.
18
+ *
19
+ * `__tests__/utils/endpoint-policy.parity.test.ts` still checks the URL wrappers
20
+ * across address forms and whitespace padding. They previously drifted on
21
+ * Unicode whitespace (this side trimmed, the other did not), and a hand-written
22
+ * 46-URL audit corpus missed the padded case. A corpus only proves the axis it
23
+ * samples, so URL parsing and trimming remain independently characterized.
36
24
  *
37
25
  * ## What this is for
38
26
  *
@@ -46,14 +34,8 @@
46
34
  * Loopback is allowed on purpose — local model servers (Ollama and friends)
47
35
  * are a first-class configuration, not an attack.
48
36
  */
49
- /** True for `localhost`, `::1`, `127.0.0.0/8`, and their IPv4-mapped-IPv6 forms. Allowed — local model servers live here. */
50
- export declare function isLoopbackEndpointHost({ hostname }: {
51
- hostname: string;
52
- }): boolean;
53
- /** True for RFC1918/link-local/CGNAT/multicast/unspecified/unique-local-IPv6 literals — private space a credentialed request must not be steered into. */
54
- export declare function isBlockedEndpointHost({ hostname }: {
55
- hostname: string;
56
- }): boolean;
37
+ import { isLoopbackApiHost as isLoopbackEndpointHost, isBlockedExternalApiHostname as isBlockedEndpointHost } from '@jini-ai/platform/net/endpoint-policy';
38
+ export { isLoopbackEndpointHost, isBlockedEndpointHost };
57
39
  /**
58
40
  * Whether `raw` is an endpoint this UI will accept: an absolute `http(s)` URL
59
41
  * whose literal hostname is not private address space.
@@ -2,37 +2,25 @@
2
2
  * One place that decides whether an operator-supplied endpoint URL is
3
3
  * acceptable, shared by every tab that takes one.
4
4
  *
5
- * ## Why this is a copy rather than an import
5
+ * ## Why the literal policy uses a browser-safe platform leaf
6
6
  *
7
- * `@jini-ai/agent-runtime`'s `providers/connection-guard.ts` already implements
8
- * this policy, and it stays the richer implementation: it adds
9
- * `validateBaseUrlResolved`, which resolves DNS and re-checks every address the
10
- * host would actually connect to, catching `internal.example.com -> 10.0.0.5`
11
- * that no string inspection can. That function needs `node:dns`, and this file
12
- * has ZERO imports of its own precisely so it can run in a browser bundle
13
- * — the same reasoning `features/execution/types.ts` gives for mirroring that
14
- * package's shapes instead of depending on it. Importing it here would pull a
15
- * Node-only module into every host that renders a settings dialog.
7
+ * `@jini-ai/platform/net/endpoint-policy` owns hostname normalization and
8
+ * classification for both this UI and the runtime's connection guard. Importing
9
+ * the Node `platform/net` barrel would bring `node:net` into browser settings
10
+ * dialogs, so this entry has no Node effects or dependencies.
16
11
  *
17
- * So the split is deliberate and layered, not accidental duplication:
12
+ * The split remains deliberate and layered:
13
+ * - Here (sync, browser-safe): scheme allow-list plus the shared literal-host
14
+ * block-list, run on every keystroke.
15
+ * - `connection-guard.ts` (async, Node): the same hostname policy plus DNS
16
+ * resolution, re-checking every address the host actually connects to. That
17
+ * catches `internal.example.com -> 10.0.0.5`, which string inspection cannot.
18
18
  *
19
- * - **Here (sync, browser-safe):** scheme allow-list plus a literal-hostname
20
- * block-list. Runs in render logic, on every keystroke, in any runtime.
21
- * - **`connection-guard.ts` (async, Node):** the same check plus DNS
22
- * resolution, run where a connection is actually attempted.
23
- *
24
- * The predicates below are ported from that file's synchronous half verbatim
25
- * in behavior. **If one side's block-list changes, change the other.** A gap
26
- * between them is a request the UI accepted and the runtime then refused, or
27
- * worse, the reverse.
28
- *
29
- * That instruction is now enforced rather than merely written down:
30
- * `__tests__/utils/endpoint-policy.parity.test.ts` runs both implementations
31
- * over the cross-product of address forms and whitespace padding and fails on
32
- * any disagreement. It exists because the sentence above was not enough — the
33
- * two drifted on Unicode whitespace (this side trimmed, the other did not), and
34
- * a hand-written 46-URL audit corpus rated them identical because it happened
35
- * not to contain a padded case. A corpus only proves the axis it samples.
19
+ * `__tests__/utils/endpoint-policy.parity.test.ts` still checks the URL wrappers
20
+ * across address forms and whitespace padding. They previously drifted on
21
+ * Unicode whitespace (this side trimmed, the other did not), and a hand-written
22
+ * 46-URL audit corpus missed the padded case. A corpus only proves the axis it
23
+ * samples, so URL parsing and trimming remain independently characterized.
36
24
  *
37
25
  * ## What this is for
38
26
  *
@@ -46,85 +34,9 @@
46
34
  * Loopback is allowed on purpose — local model servers (Ollama and friends)
47
35
  * are a first-class configuration, not an attack.
48
36
  */
49
- /** Strips one RFC 1034 trailing dot (`localhost.` resolves as `localhost`) and IPv6 brackets, then lowercases. */
50
- function normalizeHost(hostname) {
51
- const stripped = hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;
52
- return stripped.toLowerCase().replace(/\.+$/, '');
53
- }
54
- function parseIpv4(hostname) {
55
- const parts = hostname.split('.');
56
- if (parts.length !== 4)
57
- return null;
58
- const parsed = parts.map((part) => {
59
- if (!/^\d{1,3}$/.test(part))
60
- return null;
61
- const value = Number(part);
62
- return value >= 0 && value <= 255 ? value : null;
63
- });
64
- if (parsed.some((part) => part === null))
65
- return null;
66
- return parsed;
67
- }
68
- function isLoopbackIpv4(hostname) {
69
- const parts = parseIpv4(hostname);
70
- return Boolean(parts && parts[0] === 127);
71
- }
72
- function isBlockedIpv4(hostname) {
73
- const parts = parseIpv4(hostname);
74
- if (!parts)
75
- return false;
76
- const [a, b] = parts;
77
- return (a === 0 ||
78
- (a === 100 && b >= 64 && b <= 127) ||
79
- (a === 169 && b === 254) ||
80
- a === 10 ||
81
- (a === 192 && b === 168) ||
82
- (a === 172 && b >= 16 && b <= 31) ||
83
- a >= 224);
84
- }
85
- /** `::ffff:10.0.0.1` and `::ffff:a00:1` both denote the same IPv4 address; returns it dotted so the v4 rules apply. */
86
- function ipv4MappedToDotted(hostname) {
87
- const host = normalizeHost(hostname);
88
- const mapped = /^::ffff:(.+)$/i.exec(host)?.[1];
89
- if (!mapped)
90
- return null;
91
- if (parseIpv4(mapped.toLowerCase()))
92
- return mapped.toLowerCase();
93
- const hexParts = mapped.split(':');
94
- if (hexParts.length !== 2 || !hexParts.every((part) => /^[0-9a-f]{1,4}$/i.test(part))) {
95
- return null;
96
- }
97
- // Non-null assertions, not a runtime guard: the length/regex checks above
98
- // already guarantee exactly two non-empty hex segments here.
99
- const hi = hexParts[0];
100
- const lo = hexParts[1];
101
- const value = (Number.parseInt(hi, 16) << 16) | Number.parseInt(lo, 16);
102
- return [(value >>> 24) & 255, (value >>> 16) & 255, (value >>> 8) & 255, value & 255].join('.');
103
- }
104
- /** True for `localhost`, `::1`, `127.0.0.0/8`, and their IPv4-mapped-IPv6 forms. Allowed — local model servers live here. */
105
- export function isLoopbackEndpointHost({ hostname }) {
106
- const host = normalizeHost(hostname);
107
- if (host === 'localhost' || host === '::1')
108
- return true;
109
- if (isLoopbackIpv4(host))
110
- return true;
111
- const mapped = ipv4MappedToDotted(host);
112
- return Boolean(mapped && isLoopbackIpv4(mapped));
113
- }
114
- /** True for RFC1918/link-local/CGNAT/multicast/unspecified/unique-local-IPv6 literals — private space a credentialed request must not be steered into. */
115
- export function isBlockedEndpointHost({ hostname }) {
116
- const host = normalizeHost(hostname);
117
- if (host === '::')
118
- return true;
119
- if (isBlockedIpv4(host))
120
- return true;
121
- if (/^f[cd][0-9a-f]{2}:/i.test(host))
122
- return true;
123
- if (/^fe[89ab][0-9a-f]:/i.test(host))
124
- return true;
125
- const mapped = ipv4MappedToDotted(host);
126
- return Boolean(mapped && isBlockedIpv4(mapped));
127
- }
37
+ import { isLoopbackApiHost as isLoopbackEndpointHost, isBlockedExternalApiHostname as isBlockedEndpointHost, } from '@jini-ai/platform/net/endpoint-policy';
38
+ // Preserve the UI's published names and object signatures as aliases of the owner.
39
+ export { isLoopbackEndpointHost, isBlockedEndpointHost };
128
40
  /**
129
41
  * Whether `raw` is an endpoint this UI will accept: an absolute `http(s)` URL
130
42
  * whose literal hostname is not private address space.
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Generic wrapper for surfacing filesystem-read failures (e.g. from a
3
3
  * drag-and-drop file/folder read) with a consistent, identifiable error
4
- * shape. Origin: `utils/fileSystemErrors.ts` — ported verbatim, no OD
5
- * coupling found (zero imports, zero product-identity strings).
4
+ * shape.
6
5
  */
7
6
  export declare const FILE_SYSTEM_READ_ERROR_MESSAGE = "Could not read one or more dropped files or folders. Make sure they still exist and try again.";
8
7
  /** Wraps an arbitrary caught error into a named `FileSystemReadError`. */
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Generic wrapper for surfacing filesystem-read failures (e.g. from a
3
3
  * drag-and-drop file/folder read) with a consistent, identifiable error
4
- * shape. Origin: `utils/fileSystemErrors.ts` — ported verbatim, no OD
5
- * coupling found (zero imports, zero product-identity strings).
4
+ * shape.
6
5
  */
7
6
  function errorSummary(error) {
8
7
  if (error instanceof Error) {
@@ -9,9 +9,6 @@ import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
9
9
  * stale `nativeEvent.isComposing` causes Enter to insert a newline instead
10
10
  * of submitting.
11
11
  *
12
- * Origin: `utils/imeComposing.ts` — ported verbatim (no OD coupling; only
13
- * depends on React's `KeyboardEvent` type).
14
- *
15
12
  * @overallScore 100
16
13
  */
17
14
  export declare function isImeComposing(event: ReactKeyboardEvent<HTMLInputElement | HTMLTextAreaElement>, composing: boolean): boolean;
@@ -8,9 +8,6 @@
8
8
  * stale `nativeEvent.isComposing` causes Enter to insert a newline instead
9
9
  * of submitting.
10
10
  *
11
- * Origin: `utils/imeComposing.ts` — ported verbatim (no OD coupling; only
12
- * depends on React's `KeyboardEvent` type).
13
- *
14
11
  * @overallScore 100
15
12
  */
16
13
  export function isImeComposing(event, composing) {
@@ -1,6 +1,3 @@
1
- /**
2
- * Origin: `utils/platform.ts` — ported verbatim, no OD coupling (zero
3
- * imports, pure `navigator.platform` sniff).
4
- */
1
+ /** SSR-safe detection of Mac/iOS navigator.platform values. */
5
2
  export declare function isMacPlatform(): boolean;
6
3
  //# sourceMappingURL=platform.d.ts.map
@@ -1,7 +1,4 @@
1
- /**
2
- * Origin: `utils/platform.ts` — ported verbatim, no OD coupling (zero
3
- * imports, pure `navigator.platform` sniff).
4
- */
1
+ /** SSR-safe detection of Mac/iOS navigator.platform values. */
5
2
  export function isMacPlatform() {
6
3
  return typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
7
4
  }
@@ -16,9 +16,7 @@
16
16
  * - re-invoking on the same container retargets instead of running two
17
17
  * competing tweens.
18
18
  *
19
- * Origin: `utils/smoothScrollToTop.ts` — ported verbatim (no OD coupling;
20
- * the bezier solver is local math rather than a motion library dependency,
21
- * unchanged from the origin's own reasoning for avoiding one in tests).
19
+ * The bezier solver is local math, avoiding a motion-library dependency in this utility and its tests.
22
20
  */
23
21
  /**
24
22
  * Smoothly (or instantly, per reduced-motion) scrolls `container` to the
@@ -16,9 +16,7 @@
16
16
  * - re-invoking on the same container retargets instead of running two
17
17
  * competing tweens.
18
18
  *
19
- * Origin: `utils/smoothScrollToTop.ts` — ported verbatim (no OD coupling;
20
- * the bezier solver is local math rather than a motion library dependency,
21
- * unchanged from the origin's own reasoning for avoiding one in tests).
19
+ * The bezier solver is local math, avoiding a motion-library dependency in this utility and its tests.
22
20
  */
23
21
  // cubic-bezier(x1, y1, x2, y2) solver (WebKit UnitBezier shape).
24
22
  function unitBezier(x1, y1, x2, y2) {
@@ -42,16 +40,8 @@ function unitBezier(x1, y1, x2, y2) {
42
40
  if (Math.abs(err) < 1e-6)
43
41
  break;
44
42
  const d = sampleDX(t);
45
- // Empirically + analytically unreachable for this file's one and only
46
- // curve (`EASE_OUT = unitBezier(0.23, 1, 0.32, 1)` — the sole call
47
- // site, with fixed constants, never re-parameterized by any caller):
48
- // sampled sampleDX(t) at 1,000,001 evenly-spaced points across the
49
- // full domain t∈[0,1] (the only range `smoothScrollToTop` ever feeds
50
- // in, since its own `t` is clamped via `Math.min(1, ...)`) and the
51
- // minimum observed was ~0.6095 — nowhere near this 1e-6 guard. See
52
- // packages/ui/archived provenance ledger's 2026-07-22 dated entry for the full
53
- // re-verification record (matches this task's own standard for
54
- // documenting provable unreachability instead of forcing a test).
43
+ // For the sole fixed curve, sampleDX stays at least about 0.6095 over the clamped [0, 1]
44
+ // domain. This guard cannot fire for current inputs; retain it for a future curve change.
55
45
  if (Math.abs(d) < 1e-6)
56
46
  break;
57
47
  t -= err / d;
@@ -3,11 +3,7 @@
3
3
  // "city" label from a timezone id for compact UI display. Framework-free —
4
4
  // no React, no DOM beyond the standard `Intl` global.
5
5
  //
6
- // Origin: `detectLocalTimezone`/`listSupportedTimezones`/`tzCityLabel` from
7
- // the vendored schedule-editor god-component (recon r6 §1.19) — pure
8
- // `Intl` wrappers with zero product coupling, so they ship as flat utils
9
- // rather than living inside `features/schedule-picker/` (which imports them
10
- // from here instead of re-declaring them).
6
+ // Pure Intl utilities live here so schedule-picker and other features share the same rules.
11
7
  /** A conservative fallback list used only when the runtime doesn't support
12
8
  * `Intl.supportedValuesOf('timeZone')` (e.g. older JS engines). */
13
9
  const FALLBACK_TIMEZONES = [
@@ -21,9 +21,6 @@
21
21
  * single local session, so cryptographic uniqueness isn't required —
22
22
  * just enough entropy to avoid collisions in normal use.
23
23
  *
24
- * Origin: `utils/uuid.ts` — ported verbatim; only the doc comment's
25
- * product-identity reference was reworded generically.
26
- *
27
24
  * @overallScore 100
28
25
  */
29
26
  export declare function randomUUID(): string;
@@ -21,9 +21,6 @@
21
21
  * single local session, so cryptographic uniqueness isn't required —
22
22
  * just enough entropy to avoid collisions in normal use.
23
23
  *
24
- * Origin: `utils/uuid.ts` — ported verbatim; only the doc comment's
25
- * product-identity reference was reworded generically.
26
- *
27
24
  * @overallScore 100
28
25
  */
29
26
  export function randomUUID() {
@@ -3,9 +3,7 @@
3
3
  * generic pattern for opting a session into deterministic/reduced-motion
4
4
  * rendering for visual-regression testing.
5
5
  *
6
- * Origin: `utils/visualStability.ts`, whose storage key was a
7
- * product-identity string namespaced to the origin product. Genericized to a
8
- * caller-configurable key (default `'jini:visual-stability'`).
6
+ * Hosts can configure the storage key; the default is 'jini:visual-stability'.
9
7
  *
10
8
  * @overallScore 100
11
9
  */
@@ -3,9 +3,7 @@
3
3
  * generic pattern for opting a session into deterministic/reduced-motion
4
4
  * rendering for visual-regression testing.
5
5
  *
6
- * Origin: `utils/visualStability.ts`, whose storage key was a
7
- * product-identity string namespaced to the origin product. Genericized to a
8
- * caller-configurable key (default `'jini:visual-stability'`).
6
+ * Hosts can configure the storage key; the default is 'jini:visual-stability'.
9
7
  *
10
8
  * @overallScore 100
11
9
  */