@anchrd/intel 0.59.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/README.md +496 -0
- package/bin/intel.mjs +134 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
- package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
- package/dist/adapters/cloudflare/cloudflare.js +416 -0
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
- package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
- package/dist/adapters/content/content.d.ts +3 -0
- package/dist/adapters/content/content.js +128 -0
- package/dist/adapters/content/content.types.d.ts +38 -0
- package/dist/adapters/content/content.types.js +1 -0
- package/dist/adapters/db/db-audit.d.ts +62 -0
- package/dist/adapters/db/db-audit.js +150 -0
- package/dist/adapters/db/db-boards.d.ts +6 -0
- package/dist/adapters/db/db-boards.js +324 -0
- package/dist/adapters/db/db-feed.d.ts +30 -0
- package/dist/adapters/db/db-feed.js +276 -0
- package/dist/adapters/db/db-flows.d.ts +42 -0
- package/dist/adapters/db/db-flows.js +1311 -0
- package/dist/adapters/db/db-grants.d.ts +133 -0
- package/dist/adapters/db/db-grants.js +269 -0
- package/dist/adapters/db/db-indexing.d.ts +3 -0
- package/dist/adapters/db/db-indexing.js +200 -0
- package/dist/adapters/db/db-oauth.d.ts +3 -0
- package/dist/adapters/db/db-oauth.js +26 -0
- package/dist/adapters/db/db-prompts.d.ts +46 -0
- package/dist/adapters/db/db-prompts.js +144 -0
- package/dist/adapters/db/db.d.ts +6 -0
- package/dist/adapters/db/db.js +1687 -0
- package/dist/adapters/db/db.types.d.ts +16 -0
- package/dist/adapters/db/db.types.js +1 -0
- package/dist/adapters/document-converter/document-converter.d.ts +2 -0
- package/dist/adapters/document-converter/document-converter.js +26 -0
- package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
- package/dist/adapters/document-converter/document-converter.types.js +1 -0
- package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
- package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
- package/dist/adapters/index-queue/index-queue.d.ts +10 -0
- package/dist/adapters/index-queue/index-queue.js +12 -0
- package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
- package/dist/adapters/index-queue/index-queue.types.js +1 -0
- package/dist/adapters/json-schema/json-schema.d.ts +2 -0
- package/dist/adapters/json-schema/json-schema.js +77 -0
- package/dist/adapters/openid/openid.d.ts +5 -0
- package/dist/adapters/openid/openid.js +301 -0
- package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
- package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
- package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
- package/dist/adapters/remote-tools/remote-tools.js +129 -0
- package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
- package/dist/adapters/semantic-index/semantic-index.js +139 -0
- package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
- package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
- package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
- package/dist/adapters/session-cookie/session-cookie.js +87 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
- package/dist/audit/audit.d.ts +2 -0
- package/dist/audit/audit.js +87 -0
- package/dist/audit/audit.types.d.ts +23 -0
- package/dist/audit/audit.types.js +1 -0
- package/dist/auth/auth.d.ts +2 -0
- package/dist/auth/auth.js +284 -0
- package/dist/auth/auth.types.d.ts +108 -0
- package/dist/auth/auth.types.js +1 -0
- package/dist/boards/boards.d.ts +44 -0
- package/dist/boards/boards.js +447 -0
- package/dist/boards/boards.types.d.ts +117 -0
- package/dist/boards/boards.types.js +1 -0
- package/dist/build/build.d.ts +2 -0
- package/dist/build/build.js +192 -0
- package/dist/build/build.types.d.ts +17 -0
- package/dist/build/build.types.js +1 -0
- package/dist/bundle/bundle.d.ts +4 -0
- package/dist/bundle/bundle.js +1194 -0
- package/dist/bundle/bundle.types.d.ts +33 -0
- package/dist/bundle/bundle.types.js +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.js +262 -0
- package/dist/cli/cli.types.d.ts +14 -0
- package/dist/cli/cli.types.js +1 -0
- package/dist/contract/audit.d.ts +50 -0
- package/dist/contract/audit.js +72 -0
- package/dist/contract/board.d.ts +150 -0
- package/dist/contract/board.js +262 -0
- package/dist/contract/bundle.d.ts +147 -0
- package/dist/contract/bundle.js +139 -0
- package/dist/contract/contract.d.ts +112 -0
- package/dist/contract/contract.js +143 -0
- package/dist/contract/feed.d.ts +126 -0
- package/dist/contract/feed.js +149 -0
- package/dist/contract/flow-run.d.ts +346 -0
- package/dist/contract/flow-run.js +194 -0
- package/dist/contract/flow.d.ts +1057 -0
- package/dist/contract/flow.js +552 -0
- package/dist/contract/node.d.ts +551 -0
- package/dist/contract/node.js +456 -0
- package/dist/contract/share.d.ts +216 -0
- package/dist/contract/share.js +134 -0
- package/dist/contract/table.d.ts +171 -0
- package/dist/contract/table.js +117 -0
- package/dist/contract/tool.d.ts +122 -0
- package/dist/contract/tool.js +172 -0
- package/dist/feed/feed.d.ts +2 -0
- package/dist/feed/feed.js +77 -0
- package/dist/feed/feed.types.d.ts +23 -0
- package/dist/feed/feed.types.js +1 -0
- package/dist/flows/flows.d.ts +41 -0
- package/dist/flows/flows.js +2216 -0
- package/dist/flows/flows.types.d.ts +339 -0
- package/dist/flows/flows.types.js +1 -0
- package/dist/http/http.d.ts +9 -0
- package/dist/http/http.js +874 -0
- package/dist/http/http.types.d.ts +22 -0
- package/dist/http/http.types.js +1 -0
- package/dist/indexing/indexing.d.ts +6 -0
- package/dist/indexing/indexing.js +239 -0
- package/dist/indexing/indexing.types.d.ts +11 -0
- package/dist/indexing/indexing.types.js +1 -0
- package/dist/intel/intel.d.ts +3 -0
- package/dist/intel/intel.js +194 -0
- package/dist/intel/intel.types.d.ts +24 -0
- package/dist/intel/intel.types.js +1 -0
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +1292 -0
- package/dist/mcp/mcp.types.d.ts +25 -0
- package/dist/mcp/mcp.types.js +1 -0
- package/dist/nodes/document-links/document-links.d.ts +20 -0
- package/dist/nodes/document-links/document-links.js +57 -0
- package/dist/nodes/nodes.d.ts +2 -0
- package/dist/nodes/nodes.js +1667 -0
- package/dist/nodes/nodes.types.d.ts +511 -0
- package/dist/nodes/nodes.types.js +1 -0
- package/dist/prepare/prepare.d.ts +4 -0
- package/dist/prepare/prepare.js +16 -0
- package/dist/prepare/prepare.types.d.ts +9 -0
- package/dist/prepare/prepare.types.js +1 -0
- package/dist/prompts/prompts.d.ts +2 -0
- package/dist/prompts/prompts.js +65 -0
- package/dist/prompts/prompts.types.d.ts +71 -0
- package/dist/prompts/prompts.types.js +1 -0
- package/dist/shared/callers/callers.d.ts +28 -0
- package/dist/shared/callers/callers.js +30 -0
- package/dist/shared/csv/csv.d.ts +13 -0
- package/dist/shared/csv/csv.js +85 -0
- package/dist/shared/document-text/document-text.d.ts +21 -0
- package/dist/shared/document-text/document-text.js +31 -0
- package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
- package/dist/shared/gate-authorization/gate-authorization.js +26 -0
- package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
- package/dist/shared/grant-expiry/grant-expiry.js +26 -0
- package/dist/shared/intel-error/intel-error.d.ts +22 -0
- package/dist/shared/intel-error/intel-error.js +27 -0
- package/dist/shared/plain-title/plain-title.d.ts +16 -0
- package/dist/shared/plain-title/plain-title.js +45 -0
- package/dist/shared/problem-details/problem-details.d.ts +30 -0
- package/dist/shared/problem-details/problem-details.js +18 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
- package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
- package/dist/shared/safe-return-path/safe-return-path.js +11 -0
- package/dist/shared/sha256/sha256.d.ts +2 -0
- package/dist/shared/sha256/sha256.js +14 -0
- package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
- package/dist/tools/tool-servers/tool-servers.js +177 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +207 -0
- package/dist/tools/tools.types.d.ts +56 -0
- package/dist/tools/tools.types.js +1 -0
- package/examples/branding/favicon.svg +5 -0
- package/examples/branding/logo.svg +5 -0
- package/examples/branding/theme.css +16 -0
- package/examples/dev.vars.example +7 -0
- package/examples/intel.json +9 -0
- package/migrations/0000_intel_foundation.sql +203 -0
- package/migrations/0001_portal_native_tools.sql +21 -0
- package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
- package/migrations/0003_folder_permissions.sql +211 -0
- package/migrations/0004_subflow_runs.sql +14 -0
- package/migrations/0005_flow_node_cleanup.sql +28 -0
- package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
- package/migrations/0006_links_are_written_in_the_text.sql +20 -0
- package/migrations/0007_no_node_waits.sql +69 -0
- package/migrations/0008_three_layers.sql +130 -0
- package/migrations/0009_no_context_policy.sql +48 -0
- package/migrations/0010_no_run_waits_a_year.sql +27 -0
- package/migrations/0011_one_name_for_the_tree.sql +53 -0
- package/migrations/0012_table_snapshots.sql +29 -0
- package/migrations/0017_a_vector_per_card.sql +38 -0
- package/migrations/0018_no_context_policy_at_last.sql +97 -0
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/migrations/0020_cascade_purge_replay.sql +11 -0
- package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
- package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
- package/migrations/0023_a_board_and_its_tasks.sql +116 -0
- package/migrations/0024_the_archive_is_a_column.sql +33 -0
- package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
- package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
- package/migrations/0027_the_runs_of_every_flow.sql +27 -0
- package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
- package/package.json +118 -0
- package/src/contract/CLAUDE.md +152 -0
- package/src/contract/README.md +102 -0
- package/src/contract/audit.ts +85 -0
- package/src/contract/board.ts +306 -0
- package/src/contract/bundle.ts +157 -0
- package/src/contract/contract.ts +161 -0
- package/src/contract/feed.ts +165 -0
- package/src/contract/flow-run.ts +241 -0
- package/src/contract/flow.ts +675 -0
- package/src/contract/node.ts +583 -0
- package/src/contract/share.ts +196 -0
- package/src/contract/table.ts +178 -0
- package/src/contract/tool.ts +202 -0
- package/ui/README.md +110 -0
- package/ui/components.json +21 -0
- package/ui/index.html +13 -0
- package/ui/package.json +11 -0
- package/ui/scripts/lint-tokens.mjs +187 -0
- package/ui/src/access-summary/access-summary.tsx +385 -0
- package/ui/src/app/action-slot/action-slot.tsx +27 -0
- package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
- package/ui/src/app/app-tree/app-tree.tsx +1175 -0
- package/ui/src/app/app.tsx +87 -0
- package/ui/src/app/header-search/header-search.tsx +294 -0
- package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
- package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
- package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
- package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
- package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
- package/ui/src/app/tree-move/tree-move.tsx +427 -0
- package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
- package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
- package/ui/src/app/user-footer/user-footer.tsx +188 -0
- package/ui/src/app/view-menu/view-menu.tsx +115 -0
- package/ui/src/app-root/app-root.tsx +109 -0
- package/ui/src/app-root/app-root.types.ts +27 -0
- package/ui/src/archive/archive.tsx +417 -0
- package/ui/src/attachment-detail/attachment-detail.ts +52 -0
- package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
- package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
- package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
- package/ui/src/board/board-assignee/board-assignee.ts +48 -0
- package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
- package/ui/src/board/board-chip/board-chip.tsx +46 -0
- package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
- package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
- package/ui/src/board/board-data/board-data.ts +195 -0
- package/ui/src/board/board-data/board-data.types.ts +38 -0
- package/ui/src/board/board-dates/board-dates.ts +40 -0
- package/ui/src/board/board-dates/board-dates.tsx +105 -0
- package/ui/src/board/board-kanban/board-kanban.ts +162 -0
- package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
- package/ui/src/board/board-open-task/board-open-task.ts +32 -0
- package/ui/src/board/board-panel/board-panel.tsx +259 -0
- package/ui/src/board/board-settings/board-settings.tsx +226 -0
- package/ui/src/board/board-status/board-status.tsx +60 -0
- package/ui/src/board/board-stripes/board-stripes.ts +128 -0
- package/ui/src/board/board-table/board-table.ts +53 -0
- package/ui/src/board/board-table/board-table.tsx +610 -0
- package/ui/src/board/board-task/board-task.ts +108 -0
- package/ui/src/board/board-task/board-task.tsx +335 -0
- package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
- package/ui/src/branding/branding.tsx +17 -0
- package/ui/src/branding/custom-logo.ts +1 -0
- package/ui/src/branding/favicon.default.svg +5 -0
- package/ui/src/branding/favicon.svg +5 -0
- package/ui/src/components/ui/avatar.tsx +39 -0
- package/ui/src/components/ui/button.tsx +64 -0
- package/ui/src/components/ui/collapsible.tsx +20 -0
- package/ui/src/components/ui/command.tsx +160 -0
- package/ui/src/components/ui/dialog.tsx +143 -0
- package/ui/src/components/ui/dropdown-menu.tsx +187 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/popover.tsx +41 -0
- package/ui/src/components/ui/select.tsx +163 -0
- package/ui/src/components/ui/separator.tsx +26 -0
- package/ui/src/components/ui/sheet.tsx +136 -0
- package/ui/src/components/ui/sidebar.tsx +693 -0
- package/ui/src/components/ui/skeleton.tsx +13 -0
- package/ui/src/components/ui/switch.tsx +25 -0
- package/ui/src/components/ui/table.tsx +82 -0
- package/ui/src/components/ui/tabs.tsx +52 -0
- package/ui/src/components/ui/tooltip.tsx +51 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
- package/ui/src/data/query-client.ts +18 -0
- package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
- package/ui/src/data/request-refusal/request-refusal.ts +51 -0
- package/ui/src/data/sign-in/sign-in.ts +97 -0
- package/ui/src/data/sign-in/sign-in.types.ts +15 -0
- package/ui/src/document-link/document-link.tsx +114 -0
- package/ui/src/document-markdown/document-markdown.ts +195 -0
- package/ui/src/editor-schema/editor-schema.ts +23 -0
- package/ui/src/entry-picker/entry-picker.tsx +238 -0
- package/ui/src/feed/feed.tsx +344 -0
- package/ui/src/file-preview/file-preview-view.tsx +496 -0
- package/ui/src/file-preview/file-preview.tsx +28 -0
- package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
- package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
- package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
- package/ui/src/file-preview/word-file-preview.tsx +5 -0
- package/ui/src/flow-runs/flow-runs.tsx +238 -0
- package/ui/src/flows/flows.tsx +1646 -0
- package/ui/src/flows/node-icon/node-icon.ts +34 -0
- package/ui/src/flows/node-palette/node-palette.tsx +214 -0
- package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
- package/ui/src/folder-contents/folder-contents.tsx +321 -0
- package/ui/src/folder-index/folder-index.ts +55 -0
- package/ui/src/frontmatter/frontmatter.tsx +193 -0
- package/ui/src/graph-pane/graph-pane.tsx +53 -0
- package/ui/src/hooks/use-mobile.ts +19 -0
- package/ui/src/i18n/custom.json +4 -0
- package/ui/src/i18n/de.json +609 -0
- package/ui/src/i18n/en.json +609 -0
- package/ui/src/i18n/es.json +609 -0
- package/ui/src/i18n/i18n-context.tsx +65 -0
- package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
- package/ui/src/i18n/i18n.ts +32 -0
- package/ui/src/i18n/i18n.types.ts +49 -0
- package/ui/src/kind-icon.ts +97 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +68 -0
- package/ui/src/modal/modal.tsx +53 -0
- package/ui/src/node-details/node-details.tsx +545 -0
- package/ui/src/node-editor/node-editor.tsx +304 -0
- package/ui/src/node-graph/graph-notice.tsx +21 -0
- package/ui/src/node-graph/node-graph.ts +214 -0
- package/ui/src/node-graph/node-graph.tsx +295 -0
- package/ui/src/node-import/node-import.tsx +418 -0
- package/ui/src/node-save-error.ts +46 -0
- package/ui/src/node-source/node-source.tsx +128 -0
- package/ui/src/node-table/node-table.tsx +107 -0
- package/ui/src/nodes/nodes.tsx +240 -0
- package/ui/src/rename-resource/rename-resource.ts +80 -0
- package/ui/src/resolved-names/resolved-names.tsx +67 -0
- package/ui/src/resource-error.ts +33 -0
- package/ui/src/resource-menu/resource-menu.tsx +1156 -0
- package/ui/src/router/router-context.ts +6 -0
- package/ui/src/router/router.tsx +67 -0
- package/ui/src/router/router.types.ts +8 -0
- package/ui/src/router/selection-search.ts +88 -0
- package/ui/src/save-button/save-button.tsx +94 -0
- package/ui/src/section-hint/section-hint.tsx +40 -0
- package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
- package/ui/src/styles.css +189 -0
- package/ui/src/table-columns/table-columns.tsx +379 -0
- package/ui/src/theme/custom.css +1 -0
- package/ui/src/theme/theme-context.tsx +89 -0
- package/ui/src/theme/theme.ts +70 -0
- package/ui/src/time/relative-time.tsx +41 -0
- package/ui/src/time/time-context.tsx +78 -0
- package/ui/src/time/time.ts +165 -0
- package/ui/src/title-row/title-row.tsx +185 -0
- package/ui/src/tools/tools.tsx +546 -0
- package/ui/src/user-name/user-name.ts +61 -0
- package/ui/src/verbatim/verbatim.tsx +68 -0
- package/ui/tsconfig.json +17 -0
- package/ui/vite.config.ts +129 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createContext, type ReactNode, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { createI18n } from "./i18n.ts";
|
|
3
|
+
import type { I18n, LanguageChoice, Languages } from "./i18n.types.ts";
|
|
4
|
+
|
|
5
|
+
// The React seam of the i18n. Until #227 the instance travelled in the router context, which is
|
|
6
|
+
// static — there was no way to switch language without reloading the page. It is a context now, for
|
|
7
|
+
// the same reason the data provider is one: it is a dependency, not a module singleton. Unlike the
|
|
8
|
+
// data provider it has a sensible default — the English instance — so a component rendered without
|
|
9
|
+
// a provider (component tests) does not throw but simply speaks English.
|
|
10
|
+
const defaultI18n = createI18n();
|
|
11
|
+
|
|
12
|
+
const I18nContext = createContext<I18n>(defaultI18n);
|
|
13
|
+
|
|
14
|
+
// The choice between the available languages. `null` without a provider — not an empty list: the
|
|
15
|
+
// settings dialog asks precisely whether there is anything to switch at all.
|
|
16
|
+
const LanguageContext = createContext<LanguageChoice | null>(null);
|
|
17
|
+
|
|
18
|
+
interface I18nProviderProps {
|
|
19
|
+
languages: Languages;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The provider holds the active language — the only state the i18n has at runtime. It is client
|
|
24
|
+
// state (a preference), so it lives locally; remembering it is the job of createLanguages.
|
|
25
|
+
export function I18nProvider({ languages, children }: I18nProviderProps) {
|
|
26
|
+
const [current, setCurrent] = useState(languages.initial);
|
|
27
|
+
const i18n = useMemo(() => languages.i18n(current), [languages, current]);
|
|
28
|
+
|
|
29
|
+
// The root document reports the same language as the active catalog — at start AND after every
|
|
30
|
+
// switch. Otherwise a screen reader reads Spanish text with English pronunciation rules, and the
|
|
31
|
+
// browser offers a translation that is none. index.html only carries the static default.
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
document.documentElement.lang = i18n.locale;
|
|
34
|
+
}, [i18n.locale]);
|
|
35
|
+
|
|
36
|
+
const choice = useMemo<LanguageChoice>(
|
|
37
|
+
() => ({
|
|
38
|
+
codes: languages.codes,
|
|
39
|
+
current,
|
|
40
|
+
select: (code) => {
|
|
41
|
+
languages.remember(code);
|
|
42
|
+
setCurrent(code);
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
[languages, current],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<I18nContext value={i18n}>
|
|
50
|
+
<LanguageContext value={choice}>{children}</LanguageContext>
|
|
51
|
+
</I18nContext>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The whole instance, not just `t`: intel's screens hand `i18n` down to presentational children as a
|
|
56
|
+
// prop (ToolRows, GraphNotice, RelationGraphView …), and those need the object. A `useT()` would
|
|
57
|
+
// force either drilling `t` separately or turning every one of those into a hook consumer — a
|
|
58
|
+
// different refactor, and one that buys nothing.
|
|
59
|
+
export function useI18n(): I18n {
|
|
60
|
+
return useContext(I18nContext);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function useLanguage(): LanguageChoice | null {
|
|
64
|
+
return useContext(LanguageContext);
|
|
65
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import custom from "../custom.json";
|
|
3
|
+
import de from "../de.json";
|
|
4
|
+
import en from "../en.json";
|
|
5
|
+
import es from "../es.json";
|
|
6
|
+
import { CatalogSchema, createI18n } from "../i18n.ts";
|
|
7
|
+
import type { Catalog, Languages, LanguagesDeps } from "../i18n.types.ts";
|
|
8
|
+
|
|
9
|
+
// The language choice: which languages exist is the built-in set plus whatever `intel build` wrote
|
|
10
|
+
// into custom.json; which one runs is the reader's decision. Kept apart from createI18n, because the
|
|
11
|
+
// lookup needs to know none of this — it receives a catalog and reads from it.
|
|
12
|
+
|
|
13
|
+
// localStorage is right here and ONLY here: the language choice is a UI preference, not a mirror of
|
|
14
|
+
// server data (TanStack Query holds that) and not a secret. That is exactly why it survives a
|
|
15
|
+
// reload.
|
|
16
|
+
const STORE_KEY = "intel.language";
|
|
17
|
+
|
|
18
|
+
// The catalogs shipped with the UI. English is the default AND the key source; German and Spanish
|
|
19
|
+
// are translations of it.
|
|
20
|
+
//
|
|
21
|
+
// ⚠️ They cross no build step that could check them: `intel build` validates the CUSTOMER's
|
|
22
|
+
// language files against en.json, and these three are already in the package. What replaces that
|
|
23
|
+
// check is `i18n.unit.ts`, which compares the key sets of every file next to en.json. Without it a
|
|
24
|
+
// key added only to en.json would be English text in two other languages, and only a reader using
|
|
25
|
+
// those languages would ever notice.
|
|
26
|
+
const BUILT_IN: Record<string, Catalog> = {
|
|
27
|
+
en: CatalogSchema.parse(en) as Catalog,
|
|
28
|
+
de: CatalogSchema.parse(de) as Catalog,
|
|
29
|
+
es: CatalogSchema.parse(es) as Catalog,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// The language everything falls back to: it is compiled in and can therefore never be missing.
|
|
33
|
+
const FALLBACK = "en";
|
|
34
|
+
|
|
35
|
+
// custom.json is a build artifact — `intel build` writes it and validates each customer file
|
|
36
|
+
// strictly against en.json. It is still parsed here, because a custom.json from before the switcher
|
|
37
|
+
// is a FLAT catalog: accepting that silently would swap the customer's translation for English
|
|
38
|
+
// without a word.
|
|
39
|
+
const BundleSchema = z.strictObject({
|
|
40
|
+
defaultLanguage: z.string().min(1),
|
|
41
|
+
catalogs: z.record(z.string(), CatalogSchema),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export function createLanguages({ store }: LanguagesDeps = {}): Languages {
|
|
45
|
+
const bundle = BundleSchema.parse(custom);
|
|
46
|
+
// The built-in catalogs always stand available; the bundle comes last so a customer catalog under
|
|
47
|
+
// the same code REPLACES the shipped one rather than being ignored. Without that, changing one
|
|
48
|
+
// sentence of the German UI would mean maintaining a fourth language.
|
|
49
|
+
const catalogs: Record<string, Catalog> = { ...BUILT_IN, ...bundle.catalogs };
|
|
50
|
+
const codes = Object.keys(catalogs);
|
|
51
|
+
|
|
52
|
+
// A remembered language that no longer exists (the customer removed it from intel.json) is
|
|
53
|
+
// discarded rather than running half the UI on the English fallback.
|
|
54
|
+
const remembered = store?.getItem(STORE_KEY);
|
|
55
|
+
const preferred =
|
|
56
|
+
remembered !== null && remembered !== undefined && codes.includes(remembered)
|
|
57
|
+
? remembered
|
|
58
|
+
: bundle.defaultLanguage;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
codes,
|
|
62
|
+
initial: codes.includes(preferred) ? preferred : FALLBACK,
|
|
63
|
+
// An unknown code yields the English instance rather than an empty catalog: `t` would otherwise
|
|
64
|
+
// return every key verbatim, which looks like a broken screen instead of an untranslated one.
|
|
65
|
+
i18n: (code) => {
|
|
66
|
+
const catalog = catalogs[code];
|
|
67
|
+
return catalog ? createI18n({ catalog }) : createI18n();
|
|
68
|
+
},
|
|
69
|
+
remember: (code) => store?.setItem(STORE_KEY, code),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A language's name in its own language ("Deutsch", never "German") — that way everybody finds their
|
|
74
|
+
// entry even while the UI is in a language they cannot read. It therefore lives in no catalog: a
|
|
75
|
+
// language the customer adds tomorrow would otherwise need a new key in every language file. A code
|
|
76
|
+
// Intl does not know is shown unchanged.
|
|
77
|
+
export function languageName(code: string): string {
|
|
78
|
+
try {
|
|
79
|
+
return new Intl.DisplayNames([code], { type: "language" }).of(code) ?? code;
|
|
80
|
+
} catch {
|
|
81
|
+
return code;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import english from "./en.json";
|
|
3
|
+
import type { Catalog, I18n, I18nDeps } from "./i18n.types.ts";
|
|
4
|
+
|
|
5
|
+
// The whole i18n of intel-ui. No framework: i18next and friends bring bundle weight and an
|
|
6
|
+
// abstraction (namespaces, plural rules, loading states) that has no consumer here. What is needed
|
|
7
|
+
// is a lookup plus placeholders — the twenty lines below.
|
|
8
|
+
//
|
|
9
|
+
// English is the default AND the source of keys: en.json is the only file in which a new string is
|
|
10
|
+
// born. A translated catalog arrives as `catalog`; whatever is missing from it falls back to the
|
|
11
|
+
// English text — an incomplete translation shows English words, never an empty screen or a raw key.
|
|
12
|
+
export const CatalogSchema = z.record(z.string(), z.string());
|
|
13
|
+
|
|
14
|
+
const fallback = CatalogSchema.parse(english) as Catalog;
|
|
15
|
+
|
|
16
|
+
export function createI18n({ catalog = fallback }: I18nDeps = {}): I18n {
|
|
17
|
+
return {
|
|
18
|
+
locale: catalog.$locale ?? fallback.$locale ?? "en-US",
|
|
19
|
+
t(key, variables) {
|
|
20
|
+
// The key itself is the last fallback: a typo becomes visible on screen instead of vanishing
|
|
21
|
+
// as an empty string.
|
|
22
|
+
const template = catalog[key] ?? fallback[key] ?? key;
|
|
23
|
+
if (!variables) return template;
|
|
24
|
+
// Only known placeholders are replaced; an unfilled `{foo}` stays put — a visible gap beats a
|
|
25
|
+
// silent blank.
|
|
26
|
+
return template.replace(/\{(\w+)\}/g, (match, name: string) => {
|
|
27
|
+
const value = variables[name];
|
|
28
|
+
return value === undefined ? match : String(value);
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// A catalog is a flat file of dotted keys → text. Flat, because the lookup is then a map read and
|
|
2
|
+
// the completeness test compares two key sets instead of walking two trees. The reserved `$locale`
|
|
3
|
+
// key carries the file's BCP-47 locale (the sigil keeps it out of the UI key namespace), so every
|
|
4
|
+
// language file is self-describing and date/number formats need no second configuration.
|
|
5
|
+
export type Catalog = Record<string, string>;
|
|
6
|
+
|
|
7
|
+
// What `intel build` writes into src/i18n/custom.json: the starting language plus every language
|
|
8
|
+
// file listed in the customer's intel.json. The runtime loads nothing on demand — WHICH languages
|
|
9
|
+
// exist is decided at build time; only which one runs is the reader's choice.
|
|
10
|
+
export interface CatalogBundle {
|
|
11
|
+
defaultLanguage: string;
|
|
12
|
+
catalogs: Record<string, Catalog>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface I18nDeps {
|
|
16
|
+
// The active language file. Without one the UI runs on the English catalog (the default).
|
|
17
|
+
catalog?: Catalog;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface I18n {
|
|
21
|
+
locale: string;
|
|
22
|
+
t(key: string, variables?: Record<string, string | number>): string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The slice of the Web Storage API the preferences need — as a port, not a global, so a choice is
|
|
26
|
+
// testable without a DOM and localStorage is touched in exactly one place per preference.
|
|
27
|
+
export interface PreferenceStore {
|
|
28
|
+
getItem(key: string): string | null;
|
|
29
|
+
setItem(key: string, value: string): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface LanguagesDeps {
|
|
33
|
+
store?: PreferenceStore;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface Languages {
|
|
37
|
+
// Every selectable code: the built-in ones first, then whatever the customer added.
|
|
38
|
+
codes: string[];
|
|
39
|
+
// The language the app starts in: the remembered choice, otherwise the bundle's default.
|
|
40
|
+
initial: string;
|
|
41
|
+
i18n(code: string): I18n;
|
|
42
|
+
remember(code: string): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface LanguageChoice {
|
|
46
|
+
codes: string[];
|
|
47
|
+
current: string;
|
|
48
|
+
select(code: string): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CircleCheck,
|
|
3
|
+
FileAudio,
|
|
4
|
+
FileImage,
|
|
5
|
+
FileSpreadsheet,
|
|
6
|
+
FileTerminal,
|
|
7
|
+
FileText,
|
|
8
|
+
FileType,
|
|
9
|
+
FileVideo,
|
|
10
|
+
Folder,
|
|
11
|
+
KanbanSquare,
|
|
12
|
+
type LucideIcon,
|
|
13
|
+
Paperclip,
|
|
14
|
+
Presentation,
|
|
15
|
+
Table,
|
|
16
|
+
Workflow,
|
|
17
|
+
} from "lucide-react";
|
|
18
|
+
import {
|
|
19
|
+
type AttachmentPreviewKind,
|
|
20
|
+
attachmentPreviewKind,
|
|
21
|
+
} from "@/attachment-detail/attachment-detail.ts";
|
|
22
|
+
import type { TreeEntry } from "@/data/intel-data-provider/intel-data-provider.types.ts";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The one place a kind becomes a picture.
|
|
26
|
+
*
|
|
27
|
+
* ⚠️ Two screens draw the same row — the sidebar tree and the folder's own table — and they must
|
|
28
|
+
* draw it the same. A second map beside this one drifts the first time a kind is added: the tree
|
|
29
|
+
* would show the new icon and the folder screen the old one, for the same thing on the same screen.
|
|
30
|
+
* The picture is also only half the answer; both places still name the kind for a reader who cannot
|
|
31
|
+
* see it (`tree.kind.*`, `node.kind.*`), because an icon has no text.
|
|
32
|
+
*/
|
|
33
|
+
export const kindIcons: Record<TreeEntry["kind"], LucideIcon> = {
|
|
34
|
+
folder: Folder,
|
|
35
|
+
document: FileText,
|
|
36
|
+
attachment: Paperclip,
|
|
37
|
+
table: Table,
|
|
38
|
+
flow: Workflow,
|
|
39
|
+
// ⚠️ A board draws as a board, never as a folder, although it behaves like one in the tree. The
|
|
40
|
+
// icon is the ONLY thing that tells a reader the row will not open (D66, #376) — the chevron is
|
|
41
|
+
// absent, and an absent chevron looks exactly like an empty folder.
|
|
42
|
+
board: KanbanSquare,
|
|
43
|
+
// A task has an icon although the tree never draws one: the folder table, search results and the
|
|
44
|
+
// relation graph all reach a task by its own address, and each of them asks this map.
|
|
45
|
+
task: CircleCheck,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* One symbol per file ART, for the row of an `attachment` node (#815). `kindIcons.attachment`
|
|
50
|
+
* above stays the generic paperclip: it is what a caller asking the narrower "what kind of NODE is
|
|
51
|
+
* this" question gets, and archives and the relation graph still ask exactly that.
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ **`unsupported` is the only member sharing `kindIcons.attachment`'s Paperclip, and that is on
|
|
54
|
+
* purpose.** It is the one answer that means "this application cannot tell", so it draws as the
|
|
55
|
+
* same generic clip a caller gets for the kind alone.
|
|
56
|
+
*/
|
|
57
|
+
const attachmentIcons: Record<AttachmentPreviewKind, LucideIcon> = {
|
|
58
|
+
image: FileImage,
|
|
59
|
+
pdf: FileText,
|
|
60
|
+
word: FileType,
|
|
61
|
+
spreadsheet: FileSpreadsheet,
|
|
62
|
+
presentation: Presentation,
|
|
63
|
+
// ⚠️ Both draw even though neither has a preview anywhere in this application (#815, Jack's
|
|
64
|
+
// decision of 2026-08-29): the question this map answers is what a reader should be able to
|
|
65
|
+
// TELL APART, not what can be rendered on screen.
|
|
66
|
+
audio: FileAudio,
|
|
67
|
+
video: FileVideo,
|
|
68
|
+
unsupported: Paperclip,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The picture for one ROW, which is the kind plus the one thing that changes it: a document offered
|
|
73
|
+
* as a slash command draws differently (#775), and an attachment draws by its own file art (#815).
|
|
74
|
+
*
|
|
75
|
+
* ⚠️ **This is the sidebar's answer to a design question, not a shortcut.** The command itself is
|
|
76
|
+
* NOT written in the tree: the column is around 240px wide, and the text cut titles down to
|
|
77
|
+
* `Angebo…` and `Tiefenr…` — it displaced the name to say something that stands one level deeper
|
|
78
|
+
* anyway. The icon says the same thing in the space the row already has.
|
|
79
|
+
*
|
|
80
|
+
* ⚠️ It takes the ENTRY and not the kind, so every screen that draws a row gets the same answer
|
|
81
|
+
* from the same function — the reason `kindIcons` exists at all, one step further on. A caller that
|
|
82
|
+
* reaches for `kindIcons[kind]` directly is not wrong, it is answering the narrower question.
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ **A `TreeEntry` carries the node, not its version (#815)** — `mediaType` lives on the version
|
|
85
|
+
* (`packages/intel/src/contract/node.ts`), so the row has no field it could ask. The title's
|
|
86
|
+
* extension is the only signal it has, and `attachmentPreviewKind` already falls back to exactly
|
|
87
|
+
* that as its second path; passing it an empty media type here skips straight to that path. A title
|
|
88
|
+
* with no recognised extension draws the same Paperclip it always has — no regression, and no reach
|
|
89
|
+
* into the contract or the tree query for a question of presentation alone.
|
|
90
|
+
*/
|
|
91
|
+
export function iconFor(entry: TreeEntry): LucideIcon {
|
|
92
|
+
if (entry.type === "node" && entry.node.promptName !== null) return FileTerminal;
|
|
93
|
+
if (entry.type === "node" && entry.kind === "attachment") {
|
|
94
|
+
return attachmentIcons[attachmentPreviewKind(entry.node.title, "")];
|
|
95
|
+
}
|
|
96
|
+
return kindIcons[entry.kind];
|
|
97
|
+
}
|
package/ui/src/main.tsx
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { StrictMode } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { AppRoot, createRefusalStore } from "@/app-root/app-root.tsx";
|
|
5
|
+
import {
|
|
6
|
+
createIntelDataProvider,
|
|
7
|
+
loginPath,
|
|
8
|
+
} from "@/data/intel-data-provider/intel-data-provider.ts";
|
|
9
|
+
import { createIntelQueryClient } from "@/data/query-client.ts";
|
|
10
|
+
import { createBrowserSignIn, createSignInGuard } from "@/data/sign-in/sign-in.ts";
|
|
11
|
+
import { I18nProvider } from "@/i18n/i18n-context.tsx";
|
|
12
|
+
import { createLanguages } from "@/i18n/i18n-languages/i18n-languages.ts";
|
|
13
|
+
import { createIntelRouter } from "@/router/router.tsx";
|
|
14
|
+
import { applyTheme, readThemeChoice, SystemThemeQuery, themeFor } from "@/theme/theme.ts";
|
|
15
|
+
import { ThemeProvider } from "@/theme/theme-context.tsx";
|
|
16
|
+
import { RelativeClockProvider } from "@/time/relative-time.tsx";
|
|
17
|
+
import { TimeZoneProvider } from "@/time/time-context.tsx";
|
|
18
|
+
import "./styles.css";
|
|
19
|
+
import "./theme/custom.css";
|
|
20
|
+
|
|
21
|
+
// The first paint, BEFORE React — otherwise the light shell flashes until the provider mounts. Only
|
|
22
|
+
// applied here and deliberately NOT subscribed: the subscription belongs to the ThemeProvider, and
|
|
23
|
+
// two of them would overwrite one another.
|
|
24
|
+
applyTheme(
|
|
25
|
+
document.documentElement,
|
|
26
|
+
themeFor(readThemeChoice(window.localStorage), window.matchMedia(SystemThemeQuery).matches),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const root = document.getElementById("root");
|
|
30
|
+
if (!root) throw new Error("Missing #root element");
|
|
31
|
+
|
|
32
|
+
// Which languages exist is decided at build time and lies ready in the bundle; only which one runs
|
|
33
|
+
// is the reader's choice, and that choice is remembered in localStorage.
|
|
34
|
+
const languages = createLanguages({ store: window.localStorage });
|
|
35
|
+
const signIn = createBrowserSignIn(loginPath);
|
|
36
|
+
const mounted = createRoot(root);
|
|
37
|
+
const queryClient = createIntelQueryClient();
|
|
38
|
+
|
|
39
|
+
// ⚠️ The refusal arrives from inside a fetch rather than from React, and it used to be answered by
|
|
40
|
+
// a module-level `let` plus a hand-written repaint. Both are gone (#456): the flag is a store React
|
|
41
|
+
// can subscribe to, and everything that decides what is on screen lives in `AppRoot` — a component
|
|
42
|
+
// a test can render. `main.tsx` is wiring again, and only wiring.
|
|
43
|
+
const refusal = createRefusalStore();
|
|
44
|
+
const data = createIntelDataProvider({
|
|
45
|
+
onUnauthorized: createSignInGuard(signIn, () => refusal.refuse()),
|
|
46
|
+
});
|
|
47
|
+
const router = createIntelRouter({ data });
|
|
48
|
+
|
|
49
|
+
mounted.render(
|
|
50
|
+
<StrictMode>
|
|
51
|
+
{/* The refusal screen sits INSIDE the providers too: it is text like any other, and it is the
|
|
52
|
+
one screen a reader sees when nothing else works — English-only there would be the worst
|
|
53
|
+
place for it. */}
|
|
54
|
+
<I18nProvider languages={languages}>
|
|
55
|
+
<ThemeProvider store={window.localStorage}>
|
|
56
|
+
{/* ⚠️ INSIDE `I18nProvider`: the formatter needs both — the language says HOW a time is
|
|
57
|
+
written, the zone WHICH time it is (#467). */}
|
|
58
|
+
<TimeZoneProvider store={window.localStorage}>
|
|
59
|
+
<RelativeClockProvider>
|
|
60
|
+
<QueryClientProvider client={queryClient}>
|
|
61
|
+
<AppRoot data={data} router={router} refusal={refusal} />
|
|
62
|
+
</QueryClientProvider>
|
|
63
|
+
</RelativeClockProvider>
|
|
64
|
+
</TimeZoneProvider>
|
|
65
|
+
</ThemeProvider>
|
|
66
|
+
</I18nProvider>
|
|
67
|
+
</StrictMode>,
|
|
68
|
+
);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useId } from "react";
|
|
2
|
+
import { Modal as AriaModal, Dialog, ModalOverlay } from "react-aria-components";
|
|
3
|
+
import { useI18n } from "@/i18n/i18n-context.tsx";
|
|
4
|
+
import { cn } from "@/lib/utils.ts";
|
|
5
|
+
|
|
6
|
+
export function Modal({
|
|
7
|
+
title,
|
|
8
|
+
close,
|
|
9
|
+
children,
|
|
10
|
+
className,
|
|
11
|
+
}: {
|
|
12
|
+
title: string;
|
|
13
|
+
close(): void;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
}) {
|
|
17
|
+
const i18n = useI18n();
|
|
18
|
+
const titleId = useId();
|
|
19
|
+
return (
|
|
20
|
+
<ModalOverlay
|
|
21
|
+
isOpen
|
|
22
|
+
isDismissable
|
|
23
|
+
onOpenChange={(open) => {
|
|
24
|
+
if (!open) close();
|
|
25
|
+
}}
|
|
26
|
+
className="fixed inset-0 z-50 grid place-items-center bg-background/80 p-4 backdrop-blur-sm"
|
|
27
|
+
>
|
|
28
|
+
<AriaModal
|
|
29
|
+
className={cn(
|
|
30
|
+
"w-full max-w-md rounded-xl border bg-card p-6 text-card-foreground shadow-xl outline-none",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
<Dialog aria-labelledby={titleId} className="flex h-full flex-col outline-none">
|
|
35
|
+
<div className="mb-5 flex items-center justify-between gap-4">
|
|
36
|
+
<h2 id={titleId} className="text-lg font-semibold">
|
|
37
|
+
{title}
|
|
38
|
+
</h2>
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
onClick={close}
|
|
42
|
+
aria-label={i18n.t("common.close")}
|
|
43
|
+
className="rounded-md px-2 py-1 text-muted-foreground outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
|
|
44
|
+
>
|
|
45
|
+
×
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
{children}
|
|
49
|
+
</Dialog>
|
|
50
|
+
</AriaModal>
|
|
51
|
+
</ModalOverlay>
|
|
52
|
+
);
|
|
53
|
+
}
|