@kortix/ui 0.14.8 → 0.14.9
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 +2 -2
- package/dist/_vendor/kortix-shared.js +63 -3
- package/dist/_vendor/shared/app-config.d.ts +175 -0
- package/dist/_vendor/shared/channel-message.d.ts +34 -5
- package/dist/_vendor/shared/index.d.ts +1 -0
- package/dist/_vendor/shared/space-slug.d.ts +37 -0
- package/dist/access/access/access-detail-shell.js +1 -1
- package/dist/access/access/principal-picker.d.ts +11 -0
- package/dist/access/access/principal-picker.js +3 -2
- package/dist/apps/app-preview-pool.d.ts +7 -2
- package/dist/apps/app-preview-pool.js +11 -5
- package/dist/apps/app-shared.d.ts +22 -15
- package/dist/apps/app-shared.js +37 -19
- package/dist/apps/index.d.ts +2 -2
- package/dist/apps/index.js +2 -2
- package/dist/artifacts/artifact-frame.d.ts +16 -0
- package/dist/artifacts/artifact-frame.js +51 -0
- package/dist/artifacts/artifact-session.d.ts +18 -0
- package/dist/artifacts/artifact-session.js +101 -0
- package/dist/artifacts/frame-policy.d.ts +30 -0
- package/dist/artifacts/frame-policy.js +61 -0
- package/dist/artifacts/index.d.ts +3 -0
- package/dist/artifacts/index.js +5 -0
- package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
- package/dist/browser/sandbox-browser-chrome.js +2 -2
- package/dist/customize/agents/agent-grant-pages.d.ts +5 -1
- package/dist/customize/agents/agent-grant-pages.js +30 -4
- package/dist/customize/agents/agent-page.js +3 -1
- package/dist/customize/agents/agent-policy-sets.d.ts +43 -0
- package/dist/customize/agents/agent-policy-sets.js +72 -0
- package/dist/customize/agents/agent-triggers-section.js +2 -1
- package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.d.ts +6 -1
- package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.js +10 -4
- package/dist/customize/connectors/agent-access.d.ts +35 -0
- package/dist/customize/connectors/agent-access.js +77 -0
- package/dist/customize/connectors/catalog/browse-sections.d.ts +2 -29
- package/dist/customize/connectors/catalog/browse-sections.js +7 -74
- package/dist/customize/connectors/catalog/catalog-entry.d.ts +18 -37
- package/dist/customize/connectors/catalog/catalog-entry.js +28 -40
- package/dist/customize/connectors/catalog/connector-browse.d.ts +3 -32
- package/dist/customize/connectors/catalog/connector-browse.js +19 -50
- package/dist/customize/connectors/catalog/use-catalog.d.ts +20 -59
- package/dist/customize/connectors/catalog/use-catalog.js +48 -175
- package/dist/customize/connectors/connector-identity.d.ts +9 -11
- package/dist/customize/connectors/connector-identity.js +13 -42
- package/dist/customize/connectors/connector-moves-banner.d.ts +2 -1
- package/dist/customize/connectors/connector-moves-banner.js +4 -6
- package/dist/customize/connectors/connector-routes.d.ts +21 -9
- package/dist/customize/connectors/connector-routes.js +29 -20
- package/dist/customize/connectors/connectors-page.js +53 -82
- package/dist/customize/connectors/detail/app-page.js +226 -98
- package/dist/customize/connectors/detail/computer-app-page.d.ts +16 -0
- package/dist/customize/connectors/detail/computer-app-page.js +39 -0
- package/dist/customize/connectors/detail/connector-accounts.js +3 -1
- package/dist/customize/connectors/detail/connector-advanced-tools.d.ts +4 -1
- package/dist/customize/connectors/detail/connector-advanced-tools.js +2 -2
- package/dist/customize/connectors/detail/connector-agents.d.ts +14 -0
- package/dist/customize/connectors/detail/connector-agents.js +69 -0
- package/dist/customize/connectors/detail/connector-detail-tabs.js +2 -1
- package/dist/customize/connectors/detail/connector-move-row.d.ts +8 -5
- package/dist/customize/connectors/detail/connector-move-row.js +23 -14
- package/dist/customize/connectors/detail/connector-page.d.ts +4 -1
- package/dist/customize/connectors/detail/connector-page.js +14 -10
- package/dist/customize/connectors/detail/connector-policy-sets.d.ts +28 -0
- package/dist/customize/connectors/detail/connector-policy-sets.js +203 -0
- package/dist/customize/connectors/detail/connector-settings.d.ts +4 -1
- package/dist/customize/connectors/detail/connector-settings.js +7 -3
- package/dist/customize/connectors/detail/connector-status.d.ts +19 -6
- package/dist/customize/connectors/detail/connector-status.js +34 -10
- package/dist/customize/connectors/detail/connector-tabs.d.ts +4 -1
- package/dist/customize/connectors/detail/connector-tabs.js +8 -2
- package/dist/customize/connectors/detail/connector-tools.d.ts +3 -3
- package/dist/customize/connectors/detail/connector-tools.js +105 -48
- package/dist/customize/connectors/detail/email-inboxes.d.ts +43 -0
- package/dist/customize/connectors/detail/email-inboxes.js +184 -0
- package/dist/customize/connectors/detail/provider-info.d.ts +1 -1
- package/dist/customize/connectors/detail/provider-info.js +2 -2
- package/dist/customize/connectors/detail/status-badge.d.ts +1 -1
- package/dist/customize/connectors/detail/status-badge.js +4 -5
- package/dist/customize/connectors/detail/use-connector-detail.d.ts +6 -0
- package/dist/customize/connectors/detail/use-connector-detail.js +14 -4
- package/dist/customize/connectors/install/install-dialog.d.ts +23 -0
- package/dist/customize/connectors/install/install-dialog.js +40 -0
- package/dist/customize/connectors/install/install.d.ts +13 -2
- package/dist/customize/connectors/install/install.js +9 -4
- package/dist/customize/connectors/install/use-install.d.ts +19 -2
- package/dist/customize/connectors/install/use-install.js +65 -37
- package/dist/customize/connectors/install/way-kind.d.ts +5 -0
- package/dist/customize/connectors/install/way-kind.js +16 -0
- package/dist/customize/connectors/provider-label.d.ts +2 -1
- package/dist/customize/connectors/provider-label.js +13 -3
- package/dist/customize/connectors/tools/pattern-rule-draft.d.ts +4 -0
- package/dist/customize/connectors/tools/pattern-rule-draft.js +12 -3
- package/dist/customize/connectors/tools/policy-sets.d.ts +27 -0
- package/dist/customize/connectors/tools/policy-sets.js +41 -0
- package/dist/customize/connectors/tools/tool-groups.d.ts +32 -9
- package/dist/customize/connectors/tools/tool-groups.js +78 -16
- package/dist/customize/connectors/tools/tool-policy-control.d.ts +11 -10
- package/dist/customize/connectors/tools/tool-policy-control.js +15 -16
- package/dist/customize/connectors/tools/tool-policy-labels.d.ts +6 -5
- package/dist/customize/connectors/tools/tool-policy-labels.js +17 -16
- package/dist/customize/connectors/tools/tool-policy.d.ts +50 -4
- package/dist/customize/connectors/tools/tool-policy.js +74 -8
- package/dist/customize/connectors/use-agent-access.d.ts +85 -0
- package/dist/customize/connectors/use-agent-access.js +72 -0
- package/dist/customize/migrate-to-v2/index.d.ts +1 -2
- package/dist/customize/migrate-to-v2/index.js +1 -2
- package/dist/customize/migrate-to-v2/migration-prompt.d.ts +12 -25
- package/dist/customize/migrate-to-v2/migration-prompt.js +34 -236
- package/dist/customize/migrate-to-v2/upgrade-defs.d.ts +1 -1
- package/dist/customize/migrate-to-v2/upgrade-defs.js +7 -6
- package/dist/customize/migrate-to-v2/upgrade-view.js +7 -7
- package/dist/customize/migrate-to-v2/use-run-upgrade.d.ts +3 -3
- package/dist/customize/providers/provider-accordion.js +17 -2
- package/dist/customize/sections/component/channel-access-panel.d.ts +16 -0
- package/dist/customize/sections/component/channel-access-panel.js +146 -0
- package/dist/customize/sections/component/channel-connect-hero.d.ts +3 -2
- package/dist/customize/sections/component/channel-connect-hero.js +3 -2
- package/dist/customize/sections/component/channel-hero-frame.d.ts +31 -0
- package/dist/customize/sections/component/channel-hero-frame.js +40 -0
- package/dist/customize/sections/component/channel-mocks.d.ts +6 -0
- package/dist/customize/sections/component/channel-mocks.js +46 -0
- package/dist/customize/sections/component/channel-row.d.ts +13 -3
- package/dist/customize/sections/component/channel-row.js +10 -4
- package/dist/customize/sections/component/email-connect-form.d.ts +27 -0
- package/dist/customize/sections/component/email-connect-form.js +183 -0
- package/dist/customize/sections/component/slack-connect-card.d.ts +7 -0
- package/dist/customize/sections/component/slack-connect-card.js +2 -3
- package/dist/customize/sections/component/teams-connect-card.d.ts +7 -0
- package/dist/customize/sections/component/teams-connect-card.js +2 -3
- package/dist/customize/sections/component/telegram-channel-row.js +16 -25
- package/dist/customize/sections/connectors-view.d.ts +1 -15
- package/dist/customize/sections/connectors-view.js +81 -314
- package/dist/customize/sections/view/agent-editor-catalog.js +5 -0
- package/dist/customize/sections/view/channels-view.d.ts +5 -1
- package/dist/customize/sections/view/channels-view.js +61 -75
- package/dist/customize/sections/view/gateway/gateway-overview.js +10 -2
- package/dist/customize/settings/tabs/experimental-tab.js +35 -16
- package/dist/customize/shared/catalog/catalog-empty-state.d.ts +1 -12
- package/dist/customize/shared/catalog/catalog-empty-state.js +3 -3
- package/dist/customize/shared/channels-switch-dialog.d.ts +18 -0
- package/dist/customize/shared/channels-switch-dialog.js +30 -0
- package/dist/customize/shared/feature-gate-screen.js +27 -9
- package/dist/customize/shared/use-customize-prefetch.d.ts +2 -3
- package/dist/customize/shared/use-customize-prefetch.js +9 -27
- package/dist/customize/triggers/schedule/post-to-field.d.ts +18 -0
- package/dist/customize/triggers/schedule/post-to-field.js +49 -0
- package/dist/customize/triggers/schedule/schedule-copy.d.ts +10 -6
- package/dist/customize/triggers/schedule/schedule-copy.js +42 -11
- package/dist/customize/triggers/schedule/schedule-detail-sheet.js +2 -1
- package/dist/customize/triggers/schedule/trigger-composer-logic.d.ts +22 -0
- package/dist/customize/triggers/schedule/trigger-composer-logic.js +25 -0
- package/dist/customize/triggers/schedule/trigger-composer.d.ts +5 -0
- package/dist/customize/triggers/schedule/trigger-composer.js +23 -14
- package/dist/customize/triggers/schedule/trigger-edit-logic.js +3 -0
- package/dist/customize/triggers/schedule/trigger-filter.js +1 -1
- package/dist/customize/triggers/schedule/trigger-runs-logic.d.ts +2 -2
- package/dist/customize/triggers/schedule/trigger-runs-panel.js +1 -0
- package/dist/customize/triggers/schedule/trigger-toast.js +2 -1
- package/dist/customize/triggers/schedule-view.js +5 -2
- package/dist/files/index.d.ts +1 -0
- package/dist/files/index.js +1 -0
- package/dist/files/project/change-request-recovery.d.ts +1 -26
- package/dist/files/project/change-request-recovery.js +1 -59
- package/dist/files/project/components/change-request-detail-dialog.js +13 -3
- package/dist/files/project/components/drive-explorer.js +15 -15
- package/dist/files/project/components/drive-toolbar.d.ts +16 -2
- package/dist/files/project/components/drive-toolbar.js +14 -5
- package/dist/files/project/hooks/use-change-request-recovery.js +3 -19
- package/dist/files/project/hooks/use-change-requests.d.ts +1 -0
- package/dist/files/project/hooks/use-change-requests.js +11 -2
- package/dist/files/renderers/image-frame.d.ts +46 -0
- package/dist/files/renderers/image-frame.js +59 -0
- package/dist/files/renderers/image-renderer.d.ts +11 -1
- package/dist/files/renderers/image-renderer.js +13 -3
- package/dist/files/renderers/pdf/pdf-clipboard.d.ts +2 -0
- package/dist/files/renderers/pdf/pdf-clipboard.js +21 -0
- package/dist/files/renderers/pdf/pdf-viewer.js +4 -2
- package/dist/files/renderers/show-content-renderer.d.ts +9 -1
- package/dist/files/renderers/show-content-renderer.js +56 -14
- package/dist/files/runtime/file-source.js +2 -0
- package/dist/files/runtime/hooks/use-binary-blob.d.ts +10 -1
- package/dist/files/runtime/hooks/use-binary-blob.js +10 -4
- package/dist/files/runtime/hooks/use-shared-image.d.ts +41 -0
- package/dist/files/runtime/hooks/use-shared-image.js +177 -0
- package/dist/files/viewer/file-content-renderer.d.ts +22 -1
- package/dist/files/viewer/file-content-renderer.js +50 -7
- package/dist/files/viewer/file-header.d.ts +7 -3
- package/dist/files/viewer/file-header.js +20 -11
- package/dist/files/viewer/file-preview-modal.d.ts +1 -1
- package/dist/files/viewer/file-preview-modal.js +10 -22
- package/dist/files/viewer/file-source.d.ts +13 -0
- package/dist/files/viewer/use-file-content-state.d.ts +2 -0
- package/dist/files/viewer/use-file-content-state.js +28 -10
- package/dist/files/viewer/viewer-actions.d.ts +3 -1
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/hooks/use-media-query.d.ts +1 -0
- package/dist/hooks/use-mobile.d.ts +5 -0
- package/dist/hooks/use-mobile.js +8 -0
- package/dist/i18n/en.d.ts +873 -85
- package/dist/i18n/en.js +888 -100
- package/dist/i18n/ui-catalog/localize-ui-catalog.js +4 -0
- package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +15 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/internal/account-hub/account-settings-sidebar.js +1 -1
- package/dist/internal/account-hub/hub-sheet.d.ts +9 -1
- package/dist/internal/account-hub/hub-sheet.js +21 -5
- package/dist/internal/account-hub/sections.d.ts +1 -1
- package/dist/internal/account-hub/sections.js +2 -1
- package/dist/internal/auth/auth-card-shell.js +3 -3
- package/dist/internal/components/agent-mark.js +5 -2
- package/dist/internal/connector-logo/connector-logo-source.d.ts +63 -0
- package/dist/internal/connector-logo/connector-logo-source.js +48 -0
- package/dist/internal/connector-logo/connector-logo.d.ts +39 -0
- package/dist/internal/connector-logo/connector-logo.js +56 -0
- package/dist/internal/connector-logo/connector-mark-files.d.ts +11 -0
- package/dist/internal/connector-logo/connector-mark-files.js +35 -0
- package/dist/internal/files/file-read-retry.d.ts +13 -0
- package/dist/internal/files/file-read-retry.js +23 -3
- package/dist/internal/files/session-drive-files.d.ts +5 -1
- package/dist/internal/files/session-drive-files.js +7 -2
- package/dist/internal/hooks/channels/use-channels-installations.d.ts +32 -5
- package/dist/internal/hooks/channels/use-channels-installations.js +55 -7
- package/dist/internal/hooks/channels/use-email-inboxes.d.ts +19 -0
- package/dist/internal/hooks/channels/use-email-inboxes.js +48 -0
- package/dist/internal/hooks/connectors/use-add-account.js +1 -1
- package/dist/internal/hooks/index.d.ts +1 -1
- package/dist/internal/hooks/index.js +1 -1
- package/dist/internal/hooks/projects/use-new-project-session.d.ts +9 -0
- package/dist/internal/hooks/projects/use-new-project-session.js +28 -12
- package/dist/internal/hooks/use-auto-scroll.d.ts +8 -1
- package/dist/internal/hooks/use-auto-scroll.js +4 -4
- package/dist/internal/icons/telegram.d.ts +5 -0
- package/dist/internal/icons/telegram.js +6 -1
- package/dist/internal/layout/nav-hover-card.d.ts +35 -0
- package/dist/internal/layout/nav-hover-card.js +84 -0
- package/dist/internal/layout/project-nav-row.d.ts +3 -1
- package/dist/internal/layout/project-nav-row.js +3 -2
- package/dist/internal/layout/project-page-header.d.ts +4 -2
- package/dist/internal/layout/project-page-header.js +4 -3
- package/dist/internal/layout/project-settings-nav.js +59 -2
- package/dist/internal/lib/browser-error-noise.d.ts +1 -1
- package/dist/internal/lib/browser-error-noise.js +1 -1
- package/dist/internal/lib/browser-noise/rules/injected-scripts.d.ts +7 -0
- package/dist/internal/lib/browser-noise/rules/injected-scripts.js +15 -0
- package/dist/internal/lib/manifest-version.d.ts +1 -1
- package/dist/internal/lib/manifest-version.js +2 -2
- package/dist/internal/lib/memory-href.d.ts +16 -0
- package/dist/internal/lib/memory-href.js +46 -0
- package/dist/internal/lib/use-project-feature-flags.js +9 -9
- package/dist/internal/lib/utils/heic-convert.d.ts +1 -1
- package/dist/internal/lib/utils/heic-convert.js +3 -4
- package/dist/internal/lib/web-notifications.d.ts +20 -0
- package/dist/internal/lib/web-notifications.js +48 -4
- package/dist/internal/notifications/notification-bell.d.ts +4 -1
- package/dist/internal/notifications/notification-bell.js +2 -2
- package/dist/internal/selection/selection-bar.d.ts +2 -1
- package/dist/internal/selection/selection-bar.js +1 -1
- package/dist/internal/stores/file-preview-store.d.ts +29 -2
- package/dist/internal/stores/file-preview-store.js +19 -1
- package/dist/internal/stores/kortix-computer-store.js +3 -0
- package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
- package/dist/internal/stores/sidebar-nav-store.js +3 -1
- package/dist/lib/routes.d.ts +13 -1
- package/dist/lib/routes.js +13 -0
- package/dist/markdown/index.d.ts +2 -1
- package/dist/markdown/index.js +2 -1
- package/dist/markdown/setup-links/approval-decision.d.ts +1 -0
- package/dist/markdown/setup-links/approval-decision.js +6 -2
- package/dist/markdown/setup-links/connector-handshake.d.ts +3 -1
- package/dist/markdown/setup-links/connector-handshake.js +12 -10
- package/dist/markdown/setup-links/secret-intake-form.d.ts +0 -10
- package/dist/markdown/setup-links/secret-intake-form.js +1 -1
- package/dist/markdown/setup-links/show-connect-hero.d.ts +28 -0
- package/dist/markdown/setup-links/show-connect-hero.js +91 -0
- package/dist/markdown/unified-markdown.js +5 -5
- package/dist/primitives/badge.d.ts +1 -1
- package/dist/primitives/badge.js +2 -0
- package/dist/primitives/disclosure.js +14 -2
- package/dist/primitives/empty-art.d.ts +12 -0
- package/dist/primitives/empty-art.js +11 -0
- package/dist/primitives/mermaid-renderer.d.ts +2 -0
- package/dist/primitives/mermaid-renderer.js +21 -15
- package/dist/primitives/resizable.js +6 -1
- package/dist/primitives/resize-seam.d.ts +11 -0
- package/dist/primitives/resize-seam.js +12 -0
- package/dist/primitives/sidebar-panel.js +4 -7
- package/dist/primitives/tabs.d.ts +14 -1
- package/dist/primitives/tabs.js +1 -1
- package/dist/providers/ui-provider.d.ts +9 -0
- package/dist/session/composer/ComposerAboveCard.d.ts +1 -1
- package/dist/session/composer/ComposerAboveCard.js +2 -2
- package/dist/session/composer/ComposerCard.d.ts +1 -1
- package/dist/session/composer/ComposerCard.js +3 -3
- package/dist/session/composer/agent-selector.d.ts +13 -1
- package/dist/session/composer/agent-selector.js +25 -5
- package/dist/session/composer/composer-agent-access.d.ts +15 -0
- package/dist/session/composer/composer-agent-access.js +27 -0
- package/dist/session/composer/composer-chat-input.d.ts +8 -2
- package/dist/session/composer/composer-chat-input.js +8 -6
- package/dist/session/composer/composer-logic.d.ts +16 -0
- package/dist/session/composer/composer-logic.js +12 -0
- package/dist/session/composer/composer-underbar.d.ts +3 -1
- package/dist/session/composer/composer-underbar.js +4 -3
- package/dist/session/composer/composer.d.ts +15 -1
- package/dist/session/composer/composer.js +19 -1
- package/dist/session/composer/draft/composer-draft.d.ts +6 -2
- package/dist/session/composer/draft/composer-draft.js +5 -1
- package/dist/session/composer/reasoning-effort-selector.js +1 -1
- package/dist/session/models/chatgpt-connection-button.d.ts +11 -0
- package/dist/session/models/chatgpt-connection-button.js +54 -0
- package/dist/session/models/model-selector.js +5 -2
- package/dist/session/models/use-model-connection-gate.d.ts +5 -5
- package/dist/session/models/use-model-connection-gate.js +5 -5
- package/dist/session/outcomes/jump-to-outcome.d.ts +10 -0
- package/dist/session/outcomes/jump-to-outcome.js +11 -0
- package/dist/session/outcomes/session-outcomes-bridge.d.ts +14 -0
- package/dist/session/outcomes/session-outcomes-bridge.js +12 -0
- package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
- package/dist/session/outcomes/session-outcomes-provider.js +33 -3
- package/dist/session/panel/easy/app-preview.d.ts +10 -0
- package/dist/session/panel/easy/app-preview.js +31 -12
- package/dist/session/panel/easy/change-requests-card.d.ts +7 -0
- package/dist/session/panel/easy/change-requests-card.js +17 -0
- package/dist/session/panel/easy/easy-panel-logic.js +7 -1
- package/dist/session/panel/easy/easy-panel.js +5 -2
- package/dist/session/panel/easy/file-preview.js +1 -1
- package/dist/session/panel/easy/outputs-card.d.ts +2 -1
- package/dist/session/panel/easy/outputs-card.js +3 -2
- package/dist/session/panel/mobile-tool-drawer.js +7 -1
- package/dist/session/panel/session-audit-panel.js +9 -2
- package/dist/session/panel/session-detail-panel.js +9 -3
- package/dist/session/panel/session-files-explorer.d.ts +9 -0
- package/dist/session/panel/session-files-explorer.js +12 -3
- package/dist/session/panel/session-panel-provider.d.ts +15 -1
- package/dist/session/panel/session-panel-provider.js +32 -7
- package/dist/session/panel/shared/derive-panels.d.ts +8 -1
- package/dist/session/panel/shared/derive-panels.js +12 -2
- package/dist/session/panel/shared/output-priority.js +2 -0
- package/dist/session/participants/session-participants.d.ts +5 -1
- package/dist/session/participants/session-participants.js +9 -2
- package/dist/session/prompts/approval-request.d.ts +5 -0
- package/dist/session/prompts/approval-request.js +7 -1
- package/dist/session/prompts/session-approval-review.d.ts +9 -0
- package/dist/session/prompts/session-approval-review.js +18 -0
- package/dist/session/status/session-error-banner.js +2 -49
- package/dist/session/status/session-starting-loader.d.ts +14 -2
- package/dist/session/status/session-starting-loader.js +16 -4
- package/dist/session/thread/after-first-prompt.d.ts +7 -0
- package/dist/session/thread/after-first-prompt.js +8 -0
- package/dist/session/thread/detect-command.d.ts +12 -0
- package/dist/session/thread/detect-command.js +20 -0
- package/dist/session/thread/session-chat.d.ts +3 -1
- package/dist/session/thread/session-chat.js +377 -252
- package/dist/session/thread/session-thread.d.ts +3 -1
- package/dist/session/thread/session-thread.js +9 -2
- package/dist/session/thread/thread-boot-rows.d.ts +36 -0
- package/dist/session/thread/thread-boot-rows.js +89 -0
- package/dist/session/thread/transcript-rows.d.ts +31 -0
- package/dist/session/thread/transcript-rows.js +3 -0
- package/dist/session/thread/transcript.d.ts +0 -9
- package/dist/session/thread/transcript.js +104 -19
- package/dist/session/tool/shared/infrastructure-contexts.d.ts +28 -0
- package/dist/session/tool/shared/infrastructure-contexts.js +31 -3
- package/dist/session/tool/tool-part-renderer.d.ts +5 -4
- package/dist/session/tool/tool-part-renderer.js +8 -3
- package/dist/session/tool/tools/bash-tool.js +7 -6
- package/dist/session/tool/tools/channel-send-card.d.ts +1 -2
- package/dist/session/tool/tools/channel-send.d.ts +2 -1
- package/dist/session/tool/tools/codemode-tool.js +1 -1
- package/dist/session/tool/tools/connector-tools.js +1 -1
- package/dist/session/tool/tools/image-gen-tool.d.ts +1 -1
- package/dist/session/tool/tools/image-gen-tool.js +240 -53
- package/dist/session/tool/tools/show-tool.js +19 -8
- package/dist/session/turn/channel-brand.d.ts +8 -3
- package/dist/session/turn/channel-brand.js +20 -4
- package/dist/session/turn/message-author.d.ts +8 -0
- package/dist/session/turn/message-author.js +24 -0
- package/dist/session/turn/message-row.d.ts +52 -0
- package/dist/session/turn/message-row.js +44 -0
- package/dist/session/turn/plan-anchor.d.ts +1 -1
- package/dist/session/turn/plan-anchor.js +1 -1
- package/dist/session/turn/user-message.d.ts +7 -1
- package/dist/session/turn/user-message.js +21 -7
- package/dist/styles.css +1 -1
- package/dist/workspace/home/access-requests-bell.d.ts +9 -2
- package/dist/workspace/home/access-requests-bell.js +20 -6
- package/dist/workspace/home/first-chat.d.ts +8 -21
- package/dist/workspace/home/first-chat.js +22 -72
- package/dist/workspace/index.d.ts +6 -0
- package/dist/workspace/index.js +6 -0
- package/dist/workspace/layout/project-home.d.ts +14 -2
- package/dist/workspace/layout/project-home.js +60 -21
- package/dist/workspace/layout/use-project-home-send.d.ts +9 -1
- package/dist/workspace/layout/use-project-home-send.js +82 -3
- package/dist/workspace/memory/body-doc.d.ts +17 -0
- package/dist/workspace/memory/body-doc.js +208 -0
- package/dist/workspace/memory/body-extensions.d.ts +38 -0
- package/dist/workspace/memory/body-extensions.js +103 -0
- package/dist/workspace/memory/company-memory-page.d.ts +8 -0
- package/dist/workspace/memory/company-memory-page.js +16 -0
- package/dist/workspace/memory/memory-all.d.ts +20 -0
- package/dist/workspace/memory/memory-all.js +27 -0
- package/dist/workspace/memory/memory-body.d.ts +19 -0
- package/dist/workspace/memory/memory-body.js +139 -0
- package/dist/workspace/memory/memory-empty.d.ts +13 -0
- package/dist/workspace/memory/memory-empty.js +28 -0
- package/dist/workspace/memory/memory-history.d.ts +27 -0
- package/dist/workspace/memory/memory-history.js +108 -0
- package/dist/workspace/memory/memory-labels.d.ts +16 -0
- package/dist/workspace/memory/memory-labels.js +47 -0
- package/dist/workspace/memory/memory-page-list.d.ts +34 -0
- package/dist/workspace/memory/memory-page-list.js +156 -0
- package/dist/workspace/memory/memory-reader.d.ts +58 -0
- package/dist/workspace/memory/memory-reader.js +123 -0
- package/dist/workspace/memory/memory-view.d.ts +21 -0
- package/dist/workspace/memory/memory-view.js +124 -0
- package/dist/workspace/memory/remember-popover.d.ts +19 -0
- package/dist/workspace/memory/remember-popover.js +61 -0
- package/dist/workspace/memory/remember-this.d.ts +12 -0
- package/dist/workspace/memory/remember-this.js +89 -0
- package/dist/workspace/memory/source-mark.d.ts +18 -0
- package/dist/workspace/memory/source-mark.js +79 -0
- package/dist/workspace/memory/used-memory-chip.d.ts +14 -0
- package/dist/workspace/memory/used-memory-chip.js +61 -0
- package/dist/workspace/new/advanced-fields.d.ts +2 -1
- package/dist/workspace/new/advanced-fields.js +2 -2
- package/dist/workspace/new/source-param.d.ts +1 -1
- package/dist/workspace/new/source-param.js +3 -1
- package/dist/workspace/new/use-create-workspace.d.ts +8 -10
- package/dist/workspace/new/use-create-workspace.js +3 -3
- package/dist/workspace/projects/onboarding/app-logo.d.ts +7 -2
- package/dist/workspace/projects/onboarding/app-logo.js +6 -13
- package/dist/workspace/projects/policies-panel.js +5 -3
- package/dist/workspace/review/review-actions.d.ts +4 -2
- package/dist/workspace/review/review-actions.js +8 -2
- package/dist/workspace/review/review-center.js +11 -7
- package/dist/workspace/review/review-detail.d.ts +10 -0
- package/dist/workspace/review/review-detail.js +101 -13
- package/dist/workspace/saved-session-skeleton.d.ts +2 -2
- package/dist/workspace/saved-session-skeleton.js +3 -3
- package/dist/workspace/session-workspace.js +15 -13
- package/dist/workspace/sessions/project-sessions-view.js +3 -2
- package/dist/workspace/sessions/sessions-toolbar.d.ts +3 -1
- package/dist/workspace/sessions/sessions-toolbar.js +2 -2
- package/dist/workspace/sidebar/footer/project-apps-nav.d.ts +5 -0
- package/dist/workspace/sidebar/footer/project-apps-nav.js +41 -2
- package/dist/workspace/sidebar/footer/project-artifacts-nav.d.ts +6 -0
- package/dist/workspace/sidebar/footer/project-artifacts-nav.js +22 -0
- package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.js +13 -13
- package/dist/workspace/sidebar/footer/project-memory-nav.d.ts +7 -0
- package/dist/workspace/sidebar/footer/project-memory-nav.js +19 -0
- package/dist/workspace/sidebar/nav-row-meta.js +14 -2
- package/dist/workspace/sidebar/project-nav-items.js +3 -0
- package/dist/workspace/sidebar/project-session-list.js +68 -56
- package/dist/workspace/sidebar/project-sidebar.js +9 -2
- package/dist/workspace/sidebar/project-spaces-nav.d.ts +8 -0
- package/dist/workspace/sidebar/project-spaces-nav.js +69 -0
- package/dist/workspace/sidebar/session-group-order.d.ts +9 -0
- package/dist/workspace/sidebar/session-group-order.js +25 -0
- package/dist/workspace/sidebar/session-group-sections.d.ts +62 -0
- package/dist/workspace/sidebar/session-group-sections.js +267 -0
- package/dist/workspace/sidebar/session-list-chrome.d.ts +28 -0
- package/dist/workspace/sidebar/session-list-chrome.js +50 -0
- package/dist/workspace/spaces/channel-glyph.d.ts +5 -0
- package/dist/workspace/spaces/channel-glyph.js +6 -0
- package/dist/workspace/spaces/channel-setup.d.ts +16 -0
- package/dist/workspace/spaces/channel-setup.js +105 -0
- package/dist/workspace/spaces/channels-onboarding.d.ts +13 -0
- package/dist/workspace/spaces/channels-onboarding.js +37 -0
- package/dist/workspace/spaces/create-channel-dialog.d.ts +16 -0
- package/dist/workspace/spaces/create-channel-dialog.js +158 -0
- package/dist/workspace/spaces/private-channels-not-in.d.ts +9 -0
- package/dist/workspace/spaces/private-channels-not-in.js +69 -0
- package/dist/workspace/spaces/space-agents.d.ts +10 -0
- package/dist/workspace/spaces/space-agents.js +10 -0
- package/dist/workspace/spaces/space-details-dialog.d.ts +20 -0
- package/dist/workspace/spaces/space-details-dialog.js +176 -0
- package/dist/workspace/spaces/space-link-label.d.ts +49 -0
- package/dist/workspace/spaces/space-link-label.js +73 -0
- package/dist/workspace/spaces/space-links.d.ts +19 -0
- package/dist/workspace/spaces/space-links.js +230 -0
- package/dist/workspace/spaces/space-members.d.ts +46 -0
- package/dist/workspace/spaces/space-members.js +182 -0
- package/dist/workspace/spaces/space-roster.d.ts +15 -0
- package/dist/workspace/spaces/space-roster.js +44 -0
- package/dist/workspace/spaces/space-threads-tab.d.ts +56 -0
- package/dist/workspace/spaces/space-threads-tab.js +156 -0
- package/dist/workspace/spaces/space-timeline-view.d.ts +254 -0
- package/dist/workspace/spaces/space-timeline-view.js +235 -0
- package/dist/workspace/spaces/space-timeline.d.ts +106 -0
- package/dist/workspace/spaces/space-timeline.js +183 -0
- package/dist/workspace/spaces/space-triggers.d.ts +13 -0
- package/dist/workspace/spaces/space-triggers.js +78 -0
- package/dist/workspace/spaces/space-view.d.ts +37 -0
- package/dist/workspace/spaces/space-view.js +436 -0
- package/package.json +27 -36
- package/skills/kortix-ui/SKILL.md +3 -3
- package/skills/kortix-ui/references/custom-dashboard.md +3 -3
- package/skills/kortix-ui/references/modules.md +28 -4
- package/skills/kortix-ui/references/theming.md +1 -1
- package/dist/customize/connectors/catalog/merge-search.d.ts +0 -10
- package/dist/customize/connectors/catalog/merge-search.js +0 -26
- package/dist/customize/connectors/detail/find-easy-connect-app.d.ts +0 -29
- package/dist/customize/connectors/detail/find-easy-connect-app.js +0 -22
- package/dist/customize/migrate-to-v2/use-migrate-to-v2.d.ts +0 -9
- package/dist/customize/migrate-to-v2/use-migrate-to-v2.js +0 -24
- package/dist/customize/sections/connector-connection-header.d.ts +0 -36
- package/dist/customize/sections/connector-connection-header.js +0 -33
- package/dist/customize/sections/connector-connection-modal.d.ts +0 -34
- package/dist/customize/sections/connector-connection-modal.js +0 -72
- package/dist/customize/sections/discover-catalogue-card.d.ts +0 -19
- package/dist/customize/sections/discover-catalogue-card.js +0 -26
- package/dist/customize/sections/discover-catalogue-detail.d.ts +0 -15
- package/dist/customize/sections/discover-catalogue-detail.js +0 -24
- package/dist/customize/sections/discover-catalogue.d.ts +0 -17
- package/dist/customize/sections/discover-catalogue.js +0 -132
- package/dist/customize/sections/llm-provider/provider-access-menu.d.ts +0 -10
- package/dist/customize/sections/llm-provider/provider-access-menu.js +0 -23
- package/dist/files/project/components/drive-empty-art.d.ts +0 -2
- package/dist/files/project/components/drive-empty-art.js +0 -5
- package/dist/internal/hooks/use-media-query.d.ts +0 -2
- package/dist/internal/share/public-share-link-button.d.ts +0 -14
- package/dist/internal/share/public-share-link-button.js +0 -17
- package/dist/markdown/setup-links/app-credentials-form.d.ts +0 -12
- package/dist/markdown/setup-links/app-credentials-form.js +0 -115
- /package/dist/{internal/hooks → hooks}/use-media-query.js +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ApiError } from '@kortix/sdk';
|
|
4
|
+
import { safeHttpUrl } from '../../_vendor/kortix-shared.js';
|
|
5
|
+
import { useAddProjectSpaceLink, usePinProjectSpaceLink, useProjectSpaceLinks, useRemoveProjectSpaceLink, } from '@kortix/sdk/react';
|
|
6
|
+
import { ChatTeardropTextIcon, LinkSimpleIcon, MagnifyingGlassIcon, PlusIcon, PushPinIcon, PushPinSlashIcon, TrashIcon, XIcon, } from '@phosphor-icons/react';
|
|
7
|
+
import { useId, useMemo, useState } from 'react';
|
|
8
|
+
import { useLocale, useTranslations } from '../../i18n/index.js';
|
|
9
|
+
import { Copy } from '../../icons/copy.js';
|
|
10
|
+
import { useCopy } from '../../internal/hooks/use-copy.js';
|
|
11
|
+
import { SolidCheckIcon } from '../../internal/icons/solid-check-icon.js';
|
|
12
|
+
import { relativeTime } from '../../internal/lib/relative-time.js';
|
|
13
|
+
import { Button } from '../../primitives/button.js';
|
|
14
|
+
import { ButtonGroup } from '../../primitives/button-group.js';
|
|
15
|
+
import { ConfirmDialog } from '../../primitives/confirm-dialog.js';
|
|
16
|
+
import { DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator } from '../../primitives/dropdown-menu.js';
|
|
17
|
+
import { EmptyArt } from '../../primitives/empty-art.js';
|
|
18
|
+
import { EmptyState } from '../../primitives/empty-state.js';
|
|
19
|
+
import { ErrorState } from '../../primitives/error-state.js';
|
|
20
|
+
import { Field, FieldDescription, FieldGroup, FieldLabel } from '../../primitives/field.js';
|
|
21
|
+
import Hint from '../../primitives/hint.js';
|
|
22
|
+
import { Input } from '../../primitives/input.js';
|
|
23
|
+
import Loading from '../../primitives/loading.js';
|
|
24
|
+
import { Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, } from '../../primitives/modal.js';
|
|
25
|
+
import { FaviconAvatar } from '../../primitives/favicon-avatar.js';
|
|
26
|
+
import { errorToast, successToast } from '../../primitives/toast.js';
|
|
27
|
+
import { UserAvatar } from '../../primitives/user-avatar.js';
|
|
28
|
+
import { useUiViewerId } from '../../providers/ui-provider.js';
|
|
29
|
+
import { participantLabel } from '../../session/participants/session-participants.js';
|
|
30
|
+
import { filterLinks, LINK_ADDED, linkFiltersFromParams, linkHost, linkTitle, } from './space-link-label.js';
|
|
31
|
+
import { dayKind } from './space-timeline.js';
|
|
32
|
+
import { Facet, FilterMenu, FilterSearch, FilterTokens } from './space-threads-tab.js';
|
|
33
|
+
/** The API's limits: an http(s) URL of at most 2,048 characters, a title of at most 200. */
|
|
34
|
+
const URL_MAX = 2048;
|
|
35
|
+
const TITLE_MAX = 200;
|
|
36
|
+
/** The URL keys of the search and the filters. */
|
|
37
|
+
const FILTER_PARAMS = ['q', 'pinned', 'by', 'added'];
|
|
38
|
+
/** The radio value of a facet's "Any" row (Radix needs a non-empty value). */
|
|
39
|
+
const ANY = 'any';
|
|
40
|
+
/** "example.com/x" becomes "https://example.com/x"; a value with a scheme is kept as typed. */
|
|
41
|
+
function withScheme(value) {
|
|
42
|
+
return /^[a-z][a-z\d+.-]*:\/\//i.test(value) ? value : `https://${value}`;
|
|
43
|
+
}
|
|
44
|
+
/** An http(s) URL whose host has a dot ("localhost" and "foo" are not links). */
|
|
45
|
+
function isWebUrl(value) {
|
|
46
|
+
try {
|
|
47
|
+
const { protocol, hostname } = new URL(value);
|
|
48
|
+
return (protocol === 'http:' || protocol === 'https:') && hostname.includes('.');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const personName = (name) => (name ? participantLabel({ name, email: null }) : '');
|
|
55
|
+
/**
|
|
56
|
+
* A channel's Links tab, laid out like its Threads tab: one toolbar (search,
|
|
57
|
+
* the Filter menu, the active filters, "Add link"), then one list on one left
|
|
58
|
+
* edge. Pinned links come first under "Pinned"; the others follow by the day
|
|
59
|
+
* they were last shared (Today, Yesterday, Earlier). The channel's links all
|
|
60
|
+
* load at once, so search and filters read the loaded list. The URL holds
|
|
61
|
+
* them (`?q=&pinned=1&by=&added=`).
|
|
62
|
+
*/
|
|
63
|
+
export function SpaceLinks({ projectId, space, place, onOpenThread, params, setParams, }) {
|
|
64
|
+
const t = useTranslations('spaces.view');
|
|
65
|
+
const tf = useTranslations('spaces.view.threadFilter');
|
|
66
|
+
const viewerId = useUiViewerId();
|
|
67
|
+
const links = useProjectSpaceLinks(projectId, space.space_id);
|
|
68
|
+
const [adding, setAdding] = useState(false);
|
|
69
|
+
const [removing, setRemoving] = useState(null);
|
|
70
|
+
const remove = useRemoveProjectSpaceLink(projectId, space.space_id);
|
|
71
|
+
const pin = usePinProjectSpaceLink(projectId, space.space_id);
|
|
72
|
+
const filters = linkFiltersFromParams(params);
|
|
73
|
+
const setParam = (key, value) => setParams((next) => (value ? next.set(key, value) : next.delete(key)));
|
|
74
|
+
const clearAll = () => setParams((next) => FILTER_PARAMS.forEach((key) => next.delete(key)));
|
|
75
|
+
// Everyone who shared a link here, by name; the viewer is "Me".
|
|
76
|
+
const people = useMemo(() => {
|
|
77
|
+
const byId = new Map();
|
|
78
|
+
for (const link of links.data ?? []) {
|
|
79
|
+
if (link.created_by && link.created_by !== viewerId && !byId.has(link.created_by)) {
|
|
80
|
+
byId.set(link.created_by, personName(link.created_by_name) || t('authorFallback'));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return [...byId].map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name));
|
|
84
|
+
}, [links.data, viewerId, t]);
|
|
85
|
+
const groups = useMemo(() => {
|
|
86
|
+
const now = new Date();
|
|
87
|
+
const shown = filterLinks(links.data ?? [], filters, viewerId, now);
|
|
88
|
+
const pinned = shown
|
|
89
|
+
.filter((link) => link.pinned)
|
|
90
|
+
.sort((a, b) => Date.parse(b.pinned_at ?? b.created_at) - Date.parse(a.pinned_at ?? a.created_at));
|
|
91
|
+
const result = pinned.length ? [{ key: 'pinned', label: t('links.pinnedLabel'), links: pinned }] : [];
|
|
92
|
+
for (const link of shown) {
|
|
93
|
+
if (link.pinned)
|
|
94
|
+
continue;
|
|
95
|
+
const kind = dayKind(new Date(link.last_shared_at), now);
|
|
96
|
+
let group = result.find((g) => g.key === kind);
|
|
97
|
+
if (!group) {
|
|
98
|
+
group = { key: kind, label: kind === 'earlier' ? t('links.earlier') : t(kind), links: [] };
|
|
99
|
+
result.push(group);
|
|
100
|
+
}
|
|
101
|
+
group.links.push(link);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
// `filters` is rebuilt each render from the URL; its fields are the inputs.
|
|
105
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
+
}, [links.data, filters.q, filters.pinned, filters.by, filters.added, viewerId, t]);
|
|
107
|
+
const byLabel = (by) => by === 'me' || by === viewerId ? tf('me') : (people.find((p) => p.id === by)?.name ?? t('authorFallback'));
|
|
108
|
+
const addedLabel = (added) => tf(`activityValue.${added}`);
|
|
109
|
+
const tokens = [];
|
|
110
|
+
if (filters.pinned)
|
|
111
|
+
tokens.push({ key: 'pinned', facet: t('links.show'), label: t('links.pinnedLabel') });
|
|
112
|
+
if (filters.by)
|
|
113
|
+
tokens.push({ key: 'by', facet: t('links.sharedBy'), label: byLabel(filters.by) });
|
|
114
|
+
if (filters.added)
|
|
115
|
+
tokens.push({ key: 'added', facet: t('links.added'), label: addedLabel(filters.added) });
|
|
116
|
+
const pick = (key) => (value) => setParam(key, value === ANY ? null : value);
|
|
117
|
+
const setPinned = (link, next) => {
|
|
118
|
+
pin.mutate({ linkId: link.link_id, pinned: next }, {
|
|
119
|
+
onSuccess: (result) =>
|
|
120
|
+
// `null` only from an older server, where unpinning an added link removed it.
|
|
121
|
+
successToast(result === null ? t('links.removed') : next ? t('links.pinnedToast') : t('links.unpinnedToast')),
|
|
122
|
+
onError: (err) => errorToast(err instanceof ApiError && err.code === 'link_limit'
|
|
123
|
+
? t('links.limit')
|
|
124
|
+
: next
|
|
125
|
+
? t('links.pinFailed')
|
|
126
|
+
: t('links.unpinFailed')),
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
let body;
|
|
130
|
+
if (links.isError && !links.data) {
|
|
131
|
+
body = (_jsx("div", { className: "px-5 py-8", children: _jsx(ErrorState, { size: "sm", title: t('links.loadError'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => links.refetch(), children: t('retry') }) }) }));
|
|
132
|
+
}
|
|
133
|
+
else if (!links.data) {
|
|
134
|
+
body = null;
|
|
135
|
+
}
|
|
136
|
+
else if (links.data.length === 0) {
|
|
137
|
+
body = (_jsx(EmptyState, { className: "min-h-full", media: _jsx(EmptyArt, { icon: LinkSimpleIcon }), title: t('empty.links.title'), description: t('empty.links.description', { place }), action: _jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: () => setAdding(true), children: [_jsx(PlusIcon, { className: "size-3.5 shrink-0" }), t('links.add')] }) }));
|
|
138
|
+
}
|
|
139
|
+
else if (groups.length === 0) {
|
|
140
|
+
body = (_jsx(EmptyState, { className: "min-h-full", media: _jsx(EmptyArt, { icon: MagnifyingGlassIcon }), title: t('links.noMatch'), description: tf('noMatchHint'), action: _jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: clearAll, children: [_jsx(XIcon, { className: "size-3.5 shrink-0" }), tf('clearFilters')] }) }));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
body = (_jsx("div", { className: "py-2", children: groups.map((group, index) => (_jsxs("section", { "aria-labelledby": `links-${group.key}`, children: [_jsx("h3", { id: `links-${group.key}`, className: `text-muted-foreground px-5 pb-1 text-xs font-medium ${index === 0 ? 'pt-1' : 'pt-4'}`, children: group.label }), _jsx("ul", { children: group.links.map((link) => (_jsx(LinkRow, { link: link, busy: pin.isPending, onPin: () => setPinned(link, !link.pinned), onOpenThread: link.session_id && onOpenThread ? () => onOpenThread(link.session_id) : undefined, onRemove: link.can_delete ? () => setRemoving(link) : undefined }, link.link_id))) })] }, group.key))) }));
|
|
144
|
+
}
|
|
145
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [links.data?.length ? (_jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 px-5 pt-3 pb-1", children: [_jsx(FilterSearch, { value: filters.q, placeholder: t('links.search'), onSearch: (q) => setParam('q', q || null) }), _jsxs(FilterMenu, { children: [_jsx(Facet, { label: t('links.show'), value: filters.pinned ? t('links.pinnedLabel') : t('links.all'), children: _jsxs(DropdownMenuRadioGroup, { value: filters.pinned ? '1' : ANY, onValueChange: pick('pinned'), children: [_jsx(DropdownMenuRadioItem, { value: ANY, children: t('links.all') }), _jsx(DropdownMenuRadioItem, { value: "1", children: t('links.pinnedLabel') })] }) }), _jsx(Facet, { label: t('links.sharedBy'), value: filters.by ? byLabel(filters.by) : tf('anyone'), wide: true, children: _jsxs(DropdownMenuRadioGroup, { value: filters.by ?? ANY, onValueChange: pick('by'), children: [_jsx(DropdownMenuRadioItem, { value: ANY, children: tf('anyone') }), _jsx(DropdownMenuRadioItem, { value: "me", children: tf('me') }), people.length > 0 ? _jsx(DropdownMenuSeparator, {}) : null, people.map((person) => (_jsxs(DropdownMenuRadioItem, { value: person.id, children: [_jsx(UserAvatar, { size: "xs", name: person.name, email: "" }), _jsx("span", { className: "min-w-0 truncate", children: person.name })] }, person.id)))] }) }), _jsx(Facet, { label: t('links.added'), value: filters.added ? addedLabel(filters.added) : tf('anyTime'), children: _jsxs(DropdownMenuRadioGroup, { value: filters.added ?? ANY, onValueChange: pick('added'), children: [_jsx(DropdownMenuRadioItem, { value: ANY, children: tf('anyTime') }), LINK_ADDED.map((added) => (_jsx(DropdownMenuRadioItem, { value: added, children: addedLabel(added) }, added)))] }) })] }), _jsx(FilterTokens, { tokens: tokens, onRemove: (key) => setParam(key, null), onClear: clearAll }), _jsxs(Button, { variant: "outline", size: "sm", className: "ml-auto gap-1.5", onClick: () => setAdding(true), children: [_jsx(PlusIcon, { className: "size-3.5 shrink-0" }), t('links.add')] })] })) : null, _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: body }), _jsx(AddLinkModal, { projectId: projectId, space: space, place: place, open: adding, onOpenChange: setAdding }), _jsx(ConfirmDialog, { open: !!removing, onOpenChange: (open) => !open && setRemoving(null), title: t('links.removeTitle'), description: t('links.removeDescription', { link: removing ? linkTitle(removing) : '', place }), confirmLabel: t('links.remove'), confirmVariant: "destructive", isPending: remove.isPending, onConfirm: () => {
|
|
146
|
+
if (!removing)
|
|
147
|
+
return;
|
|
148
|
+
remove.mutate(removing.link_id, {
|
|
149
|
+
onSuccess: () => {
|
|
150
|
+
setRemoving(null);
|
|
151
|
+
successToast(t('links.removed'));
|
|
152
|
+
},
|
|
153
|
+
onError: () => errorToast(t('links.removeFailed')),
|
|
154
|
+
});
|
|
155
|
+
} })] }));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* One link on one line, at the Threads row's height and padding: the site's
|
|
159
|
+
* favicon (the app's favicon service, never the stored `icon_url`), the title, the site muted,
|
|
160
|
+
* and who shared it and when at the right. Hover or focus swaps that for the
|
|
161
|
+
* actions: Pin / Unpin, Copy link, Open thread, Remove. The row opens the link.
|
|
162
|
+
*/
|
|
163
|
+
function LinkRow({ link, busy, onPin, onOpenThread, onRemove, }) {
|
|
164
|
+
const t = useTranslations('spaces.view');
|
|
165
|
+
const locale = useLocale();
|
|
166
|
+
const { copy, copied } = useCopy({ successMessage: t('links.linkCopied') });
|
|
167
|
+
const host = linkHost(link.url);
|
|
168
|
+
const site = link.site_name && link.site_name.toLowerCase() !== host.toLowerCase() ? link.site_name : host;
|
|
169
|
+
const name = personName(link.created_by_name);
|
|
170
|
+
const when = relativeTime(link.last_shared_at, locale);
|
|
171
|
+
const pinLabel = link.pinned ? t('links.unpin') : t('links.pin');
|
|
172
|
+
const action = (label, hint, onClick, icon, disabled) => (_jsx(Hint, { label: hint, side: "top", children: _jsx(Button, { variant: "outline", size: "icon-xs", "aria-label": label, disabled: disabled, onClick: onClick, children: icon }) }));
|
|
173
|
+
return (_jsxs("li", { className: "group hover:bg-hover focus-within:bg-hover relative flex items-center gap-3 px-5 py-2", children: [_jsx(FaviconAvatar, { value: link.url, size: "xs", className: "shrink-0" }), _jsxs("div", { className: "flex min-w-0 flex-1 items-baseline gap-2", children: [_jsx("a", {
|
|
174
|
+
// A row another writer stored with a non-http(s) URL is shown, never linked.
|
|
175
|
+
href: safeHttpUrl(link.url) ? link.url : undefined, target: "_blank", rel: "noopener noreferrer", title: link.url, className: "focus-visible:after:ring-ring min-w-0 truncate text-sm font-medium outline-none after:absolute after:inset-0 focus-visible:after:ring-2 focus-visible:after:ring-inset", children: linkTitle(link) }), _jsx("span", { className: "text-muted-foreground max-w-48 shrink-0 truncate text-xs", children: site })] }), _jsx("span", { className: "text-muted-foreground shrink-0 text-xs tabular-nums group-focus-within:invisible group-hover:invisible pointer-coarse:hidden", children: name ? `${name} · ${when}` : when }), _jsxs(ButtonGroup, { className: "absolute inset-y-0 right-5 z-10 my-auto hidden h-fit group-focus-within:flex group-hover:flex pointer-coarse:static pointer-coarse:flex", children: [action(pinLabel, link.pinned ? t('links.unpinHint') : t('links.pinHint'), onPin, link.pinned ? _jsx(PushPinSlashIcon, { className: "size-3.5 shrink-0" }) : _jsx(PushPinIcon, { className: "size-3.5 shrink-0" }), busy), action(t('links.copyLink'), copied ? t('links.linkCopied') : t('links.copyLink'), () => void copy(link.url), copied ? _jsx(SolidCheckIcon, { className: "size-3.5 shrink-0" }) : _jsx(Copy, { className: "size-3.5 shrink-0" })), onOpenThread
|
|
176
|
+
? action(t('links.openThread'), t('links.openThread'), onOpenThread, _jsx(ChatTeardropTextIcon, { className: "size-3.5 shrink-0" }))
|
|
177
|
+
: null, onRemove
|
|
178
|
+
? action(t('links.remove'), t('links.remove'), onRemove, _jsx(TrashIcon, { className: "size-3.5 shrink-0" }))
|
|
179
|
+
: null] })] }));
|
|
180
|
+
}
|
|
181
|
+
function AddLinkModal({ projectId, space, place, open, onOpenChange, }) {
|
|
182
|
+
const t = useTranslations('spaces.view');
|
|
183
|
+
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
184
|
+
const id = useId();
|
|
185
|
+
const add = useAddProjectSpaceLink(projectId, space.space_id);
|
|
186
|
+
const [url, setUrl] = useState('');
|
|
187
|
+
const [title, setTitle] = useState('');
|
|
188
|
+
const [error, setError] = useState(null);
|
|
189
|
+
const close = () => {
|
|
190
|
+
if (add.isPending)
|
|
191
|
+
return;
|
|
192
|
+
setUrl('');
|
|
193
|
+
setTitle('');
|
|
194
|
+
setError(null);
|
|
195
|
+
onOpenChange(false);
|
|
196
|
+
};
|
|
197
|
+
const submit = () => {
|
|
198
|
+
if (add.isPending)
|
|
199
|
+
return;
|
|
200
|
+
const trimmed = withScheme(url.trim());
|
|
201
|
+
if (!isWebUrl(trimmed))
|
|
202
|
+
return setError('invalid');
|
|
203
|
+
add.mutate({ url: trimmed, ...(title.trim() ? { title: title.trim() } : {}) }, {
|
|
204
|
+
onSuccess: () => {
|
|
205
|
+
successToast(t('links.added'));
|
|
206
|
+
setUrl('');
|
|
207
|
+
setTitle('');
|
|
208
|
+
setError(null);
|
|
209
|
+
onOpenChange(false);
|
|
210
|
+
},
|
|
211
|
+
onError: (err) => setError(err instanceof ApiError && err.code === 'link_limit' ? 'limit' : 'failed'),
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
return (_jsx(Modal, { open: open, onOpenChange: (next) => !next && close(), children: _jsxs(ModalContent, { className: "lg:max-w-md", children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: t('links.add') }), _jsx(ModalDescription, { children: t('links.addDescription', { place }) })] }), _jsxs("form", {
|
|
215
|
+
// The URL check below says what is wrong, in the viewer's language.
|
|
216
|
+
noValidate: true, onSubmit: (event) => {
|
|
217
|
+
event.preventDefault();
|
|
218
|
+
submit();
|
|
219
|
+
}, children: [_jsx(ModalBody, { children: _jsxs(FieldGroup, { className: "gap-5", children: [_jsxs(Field, { className: "gap-1.5", children: [_jsx(FieldLabel, { htmlFor: `${id}-url`, children: t('links.urlLabel') }), _jsx(Input, { id: `${id}-url`, type: "url", inputMode: "url", autoComplete: "off", spellCheck: false, autoFocus: true, value: url, maxLength: URL_MAX, placeholder: "https://", "aria-invalid": error === 'invalid' ? true : undefined, "aria-describedby": `${id}-url-hint`, onChange: (event) => {
|
|
220
|
+
setUrl(event.target.value);
|
|
221
|
+
if (error)
|
|
222
|
+
setError(null);
|
|
223
|
+
} }), _jsx(FieldDescription, { id: `${id}-url-hint`, className: "text-destructive", children: _jsx("span", { "aria-live": "polite", children: error === 'invalid'
|
|
224
|
+
? t('links.invalidUrl')
|
|
225
|
+
: error === 'limit'
|
|
226
|
+
? t('links.addLimit')
|
|
227
|
+
: error === 'failed'
|
|
228
|
+
? t('links.addFailed')
|
|
229
|
+
: null }) })] }), _jsxs(Field, { className: "gap-1.5", children: [_jsx(FieldLabel, { htmlFor: `${id}-title`, children: t('links.titleLabel') }), _jsx(Input, { id: `${id}-title`, autoComplete: "off", value: title, maxLength: TITLE_MAX, placeholder: t('links.titlePlaceholder'), onChange: (event) => setTitle(event.target.value) })] })] }) }), _jsxs(ModalFooter, { className: "sm:justify-between", children: [_jsx(Button, { type: "button", variant: "outline-ghost", onClick: close, disabled: add.isPending, children: tI18nComplete.raw('text19766ed6ccb2') }), _jsxs(Button, { type: "submit", disabled: add.isPending, "aria-busy": add.isPending || undefined, children: [add.isPending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, t('links.add')] })] })] })] }) }));
|
|
230
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProjectSpace, ProjectSpaceMember } from '@kortix/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* The project's people, by display name: `#general`'s member list, which is
|
|
4
|
+
* every project member. `notIn`: the people not yet in that channel.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useProjectPeople(projectId: string, notIn?: ProjectSpace): {
|
|
7
|
+
available: ProjectSpaceMember[];
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Invite someone to the project by email: the project's own grant dialog
|
|
12
|
+
* (`AccessDialog`, the one Access → Projects opens), for a viewer who may
|
|
13
|
+
* manage the project's members. `dialog` is rendered by the caller.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useInviteToProject(projectId: string): {
|
|
16
|
+
canInvite: boolean;
|
|
17
|
+
invite: () => void;
|
|
18
|
+
dialog: import("react").JSX.Element | null;
|
|
19
|
+
};
|
|
20
|
+
/** A person's face and display name, never an email. */
|
|
21
|
+
export declare function MemberAvatar({ member, size }: {
|
|
22
|
+
member: ProjectSpaceMember;
|
|
23
|
+
size?: 'xs' | 'sm' | 'md';
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* "Add members": one checklist of the project's people and agents not yet in
|
|
27
|
+
* the channel, in the access picker's rows, People first. People show to
|
|
28
|
+
* whoever may add (`can_add`), agents to whoever may edit the channel
|
|
29
|
+
* (`can_edit`); an empty group is hidden. Each check adds a chip. Arrow keys
|
|
30
|
+
* move the highlight across both groups from the search field; Enter checks
|
|
31
|
+
* the highlighted row. The footer adds the people (one request) and appends
|
|
32
|
+
* the agents in pick order (one PATCH), in parallel: on an empty agent list
|
|
33
|
+
* the first agent picked becomes the default. "Invite by email" in the
|
|
34
|
+
* footer opens the project's invite dialog, for someone not in the project.
|
|
35
|
+
*/
|
|
36
|
+
export declare function AddChannelMembersDialog({ projectId, space, open, onOpenChange, }: {
|
|
37
|
+
projectId: string;
|
|
38
|
+
space: ProjectSpace;
|
|
39
|
+
open: boolean;
|
|
40
|
+
onOpenChange: (open: boolean) => void;
|
|
41
|
+
}): import("react").JSX.Element;
|
|
42
|
+
/** "Join channel", in the header of a public channel the viewer is not in. */
|
|
43
|
+
export declare function JoinChannelButton({ projectId, space }: {
|
|
44
|
+
projectId: string;
|
|
45
|
+
space: ProjectSpace;
|
|
46
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useAddProjectSpaceMembers, useJoinProjectSpace, useProjectSpaceMembers, useProjectAccountId, useProjectName, useProjectSpaces, useUpdateProjectSpace, useVisibleAgents, } from '@kortix/sdk/react';
|
|
4
|
+
import { EnvelopeSimpleIcon, MagnifyingGlassIcon } from '@phosphor-icons/react';
|
|
5
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { AccessDialog } from '../../access/access/access-dialog.js';
|
|
7
|
+
import { PickerRow } from '../../access/access/principal-picker.js';
|
|
8
|
+
import { useTranslations } from '../../i18n/index.js';
|
|
9
|
+
import { Close } from '../../icons/close.js';
|
|
10
|
+
import { AgentMark } from '../../internal/components/agent-mark.js';
|
|
11
|
+
import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
|
|
12
|
+
import { useProjectCan } from '../../internal/lib/use-project-can.js';
|
|
13
|
+
import { Badge } from '../../primitives/badge.js';
|
|
14
|
+
import { Button } from '../../primitives/button.js';
|
|
15
|
+
import { InputGroupSearch, InputGroupSearchClear, InputGroupSearchIcon, InputGroupSearchInput, } from '../../primitives/input-group.js';
|
|
16
|
+
import Loading from '../../primitives/loading.js';
|
|
17
|
+
import { Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, } from '../../primitives/modal.js';
|
|
18
|
+
import { errorToast, successToast } from '../../primitives/toast.js';
|
|
19
|
+
import { UserAvatar } from '../../primitives/user-avatar.js';
|
|
20
|
+
import { agentLabel } from './space-timeline.js';
|
|
21
|
+
/**
|
|
22
|
+
* The project's people, by display name: `#general`'s member list, which is
|
|
23
|
+
* every project member. `notIn`: the people not yet in that channel.
|
|
24
|
+
*/
|
|
25
|
+
export function useProjectPeople(projectId, notIn) {
|
|
26
|
+
const spaces = useProjectSpaces(projectId);
|
|
27
|
+
const generalId = spaces.data?.find((space) => space.is_default)?.space_id;
|
|
28
|
+
const everyone = useProjectSpaceMembers(projectId, generalId);
|
|
29
|
+
const inChannel = useProjectSpaceMembers(projectId, notIn?.is_default ? null : notIn?.space_id);
|
|
30
|
+
const available = useMemo(() => {
|
|
31
|
+
const taken = new Set((inChannel.data?.members ?? []).map((member) => member.user_id));
|
|
32
|
+
return (everyone.data?.members ?? []).filter((member) => !taken.has(member.user_id));
|
|
33
|
+
}, [everyone.data, inChannel.data]);
|
|
34
|
+
return { available, isLoading: everyone.isLoading || inChannel.isLoading };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Invite someone to the project by email: the project's own grant dialog
|
|
38
|
+
* (`AccessDialog`, the one Access → Projects opens), for a viewer who may
|
|
39
|
+
* manage the project's members. `dialog` is rendered by the caller.
|
|
40
|
+
*/
|
|
41
|
+
export function useInviteToProject(projectId) {
|
|
42
|
+
const canInvite = useProjectCan(projectId, PROJECT_ACTIONS.PROJECT_MEMBERS_MANAGE).allowed === true;
|
|
43
|
+
const accountId = useProjectAccountId(canInvite ? projectId : undefined);
|
|
44
|
+
const projectName = useProjectName(canInvite ? projectId : undefined) ?? '';
|
|
45
|
+
const [open, setOpen] = useState(false);
|
|
46
|
+
const dialog = accountId ? (_jsx(AccessDialog, { open: open, onOpenChange: setOpen, accountId: accountId, scope: { kind: 'project', projectId, projectName }, mode: { kind: 'grant' } })) : null;
|
|
47
|
+
return { canInvite: canInvite && !!accountId, invite: () => setOpen(true), dialog };
|
|
48
|
+
}
|
|
49
|
+
/** A person's face and display name, never an email. */
|
|
50
|
+
export function MemberAvatar({ member, size = 'md' }) {
|
|
51
|
+
return _jsx(UserAvatar, { email: "", name: member.name, avatarUrl: member.avatar_url, size: size });
|
|
52
|
+
}
|
|
53
|
+
const candidateKey = (candidate) => `${candidate.kind}:${candidate.id}`;
|
|
54
|
+
/**
|
|
55
|
+
* "Add members": one checklist of the project's people and agents not yet in
|
|
56
|
+
* the channel, in the access picker's rows, People first. People show to
|
|
57
|
+
* whoever may add (`can_add`), agents to whoever may edit the channel
|
|
58
|
+
* (`can_edit`); an empty group is hidden. Each check adds a chip. Arrow keys
|
|
59
|
+
* move the highlight across both groups from the search field; Enter checks
|
|
60
|
+
* the highlighted row. The footer adds the people (one request) and appends
|
|
61
|
+
* the agents in pick order (one PATCH), in parallel: on an empty agent list
|
|
62
|
+
* the first agent picked becomes the default. "Invite by email" in the
|
|
63
|
+
* footer opens the project's invite dialog, for someone not in the project.
|
|
64
|
+
*/
|
|
65
|
+
export function AddChannelMembersDialog({ projectId, space, open, onOpenChange, }) {
|
|
66
|
+
const invite = useInviteToProject(projectId);
|
|
67
|
+
return (_jsxs(_Fragment, { children: [_jsx(Modal, { open: open, onOpenChange: onOpenChange, children: _jsx(ModalContent, { className: "lg:max-w-md", children: open ? (_jsx(AddMembersForm, { projectId: projectId, space: space, onDone: () => onOpenChange(false), onInvite: invite.canInvite
|
|
68
|
+
? () => {
|
|
69
|
+
onOpenChange(false);
|
|
70
|
+
invite.invite();
|
|
71
|
+
}
|
|
72
|
+
: null })) : null }) }), invite.dialog] }));
|
|
73
|
+
}
|
|
74
|
+
function AddMembersForm({ projectId, space, onDone, onInvite, }) {
|
|
75
|
+
const t = useTranslations('spaces.addMembers');
|
|
76
|
+
const canAdd = useProjectSpaceMembers(projectId, space.space_id).data?.can_add === true;
|
|
77
|
+
// Its creator or a project manager; an older API sends no `can_edit`.
|
|
78
|
+
const editable = space.can_edit !== false;
|
|
79
|
+
const outside = space.visibility === 'private' && space.is_member === false;
|
|
80
|
+
const people = useProjectPeople(projectId, canAdd ? space : undefined);
|
|
81
|
+
const agents = useVisibleAgents({ projectId });
|
|
82
|
+
const add = useAddProjectSpaceMembers(projectId, space.space_id);
|
|
83
|
+
const update = useUpdateProjectSpace(projectId);
|
|
84
|
+
const [picked, setPicked] = useState([]);
|
|
85
|
+
const [query, setQuery] = useState('');
|
|
86
|
+
const [highlight, setHighlight] = useState(0);
|
|
87
|
+
const listRef = useRef(null);
|
|
88
|
+
const pending = add.isPending || update.isPending;
|
|
89
|
+
const name = space.slug ?? space.name;
|
|
90
|
+
const current = useMemo(() => space.agents ?? [], [space.agents]);
|
|
91
|
+
const personRows = useMemo(() => canAdd
|
|
92
|
+
? people.available
|
|
93
|
+
// A manager outside a private channel adds others, never themself (D1).
|
|
94
|
+
.filter((person) => !(outside && person.is_viewer))
|
|
95
|
+
.map((person) => ({ kind: 'person', id: person.user_id, label: person.name, person }))
|
|
96
|
+
: [], [canAdd, outside, people.available]);
|
|
97
|
+
const agentRows = useMemo(() => editable
|
|
98
|
+
? agents.filter((agent) => !current.includes(agent.name)).map((agent) => ({ kind: 'agent', id: agent.name, label: agentLabel(agent.name) }))
|
|
99
|
+
: [], [editable, agents, current]);
|
|
100
|
+
const needle = query.trim().toLowerCase();
|
|
101
|
+
// A person by display name; an agent by its label or its slug.
|
|
102
|
+
const matches = (row) => !needle || row.label.toLowerCase().includes(needle) || (row.kind === 'agent' && row.id.toLowerCase().includes(needle));
|
|
103
|
+
const shownPeople = personRows.filter(matches);
|
|
104
|
+
const shownAgents = agentRows.filter(matches);
|
|
105
|
+
// One list for the arrow keys: People, then Agents.
|
|
106
|
+
const shown = [...shownPeople, ...shownAgents];
|
|
107
|
+
const pickedKeys = new Set(picked.map(candidateKey));
|
|
108
|
+
const toggle = (row) => setPicked((list) => (list.some((item) => candidateKey(item) === candidateKey(row)) ? list.filter((item) => candidateKey(item) !== candidateKey(row)) : [...list, row]));
|
|
109
|
+
// The highlighted row stays in view as the arrow keys move it.
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
listRef.current?.querySelector(`[data-row="${highlight}"]`)?.scrollIntoView({ block: 'nearest' });
|
|
112
|
+
}, [highlight]);
|
|
113
|
+
const onKeyDown = (event) => {
|
|
114
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
const step = event.key === 'ArrowDown' ? 1 : -1;
|
|
117
|
+
setHighlight((index) => (shown.length === 0 ? 0 : (index + step + shown.length) % shown.length));
|
|
118
|
+
}
|
|
119
|
+
else if (event.key === 'Enter') {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
const row = shown[Math.min(highlight, shown.length - 1)];
|
|
122
|
+
if (row)
|
|
123
|
+
toggle(row);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
/** "1 person", "2 agents", "1 person and 2 agents". */
|
|
127
|
+
const summary = (personCount, agentCount) => {
|
|
128
|
+
const who = personCount > 0 ? t('peopleCount', { count: personCount }) : null;
|
|
129
|
+
const what = agentCount > 0 ? t('agentsCount', { count: agentCount }) : null;
|
|
130
|
+
return who && what ? t('both', { people: who, agents: what }) : (who ?? what ?? '');
|
|
131
|
+
};
|
|
132
|
+
const pickedPeople = picked.filter((row) => row.kind === 'person').map((row) => row.id);
|
|
133
|
+
const pickedAgents = picked.filter((row) => row.kind === 'agent').map((row) => row.id);
|
|
134
|
+
const submit = async () => {
|
|
135
|
+
if (picked.length === 0 || pending)
|
|
136
|
+
return;
|
|
137
|
+
const [peopleResult, agentsResult] = await Promise.allSettled([
|
|
138
|
+
pickedPeople.length > 0 ? add.mutateAsync(pickedPeople) : null,
|
|
139
|
+
pickedAgents.length > 0 ? update.mutateAsync({ spaceId: space.space_id, input: { agents: [...current, ...pickedAgents] } }) : null,
|
|
140
|
+
]);
|
|
141
|
+
const peopleFailed = peopleResult.status === 'rejected';
|
|
142
|
+
const agentsFailed = agentsResult.status === 'rejected';
|
|
143
|
+
if (!peopleFailed && !agentsFailed) {
|
|
144
|
+
successToast(t('added', { what: summary(pickedPeople.length, pickedAgents.length), name }));
|
|
145
|
+
onDone();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Keep the part that failed selected, so a second click retries only it.
|
|
149
|
+
setPicked((list) => list.filter((row) => (row.kind === 'person' ? peopleFailed : agentsFailed)));
|
|
150
|
+
if (peopleFailed && agentsFailed)
|
|
151
|
+
errorToast(t('failed'));
|
|
152
|
+
else if (peopleFailed)
|
|
153
|
+
errorToast(pickedAgents.length > 0 ? t('peopleFailedAgentsAdded', { what: summary(0, pickedAgents.length), name }) : t('peopleFailed'));
|
|
154
|
+
else
|
|
155
|
+
errorToast(pickedPeople.length > 0 ? t('agentsFailedPeopleAdded', { what: summary(pickedPeople.length, 0), name }) : t('agentsFailed'));
|
|
156
|
+
};
|
|
157
|
+
const loading = canAdd && people.isLoading;
|
|
158
|
+
const empty = loading
|
|
159
|
+
? null
|
|
160
|
+
: personRows.length + agentRows.length === 0
|
|
161
|
+
? t('allAdded')
|
|
162
|
+
: shown.length === 0
|
|
163
|
+
? t('noMatches', { query: query.trim() })
|
|
164
|
+
: null;
|
|
165
|
+
const group = (label, rows, offset) => rows.length === 0 ? null : (_jsxs("div", { role: "group", "aria-label": label, children: [_jsx("h3", { className: "text-muted-foreground px-2 pt-2 pb-1 text-xs font-medium", children: label }), rows.map((row, index) => (_jsx("div", { "data-row": offset + index, children: _jsx(PickerRow, { selection: "multi", selected: pickedKeys.has(candidateKey(row)), highlighted: offset + index === highlight, disabled: pending, onSelect: () => toggle(row),
|
|
166
|
+
// The 28px tile, as in the Members list: a person row and an agent row share one height.
|
|
167
|
+
leading: row.kind === 'person' ? _jsx(MemberAvatar, { member: row.person }) : _jsx(AgentMark, { slug: row.id, size: 21 }), label: row.label }) }, candidateKey(row))))] }));
|
|
168
|
+
return (_jsxs(_Fragment, { children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: t('title', { name }) }), _jsx(ModalDescription, { children: t('description') })] }), _jsxs(ModalBody, { className: "space-y-3", children: [picked.length > 0 ? (_jsx("ul", { className: "flex flex-wrap gap-1.5", "aria-label": t('selected'), children: picked.map((row) => (_jsx("li", { children: _jsxs(Badge, { variant: "outline", size: "sm", className: "gap-1.5 py-0.5 pr-0.5 pl-1 font-sans text-xs tracking-normal", children: [row.kind === 'person' ? _jsx(MemberAvatar, { member: row.person, size: "xs" }) : _jsx(AgentMark, { slug: row.id, size: 14, tile: 18 }), row.label, _jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", className: "text-muted-foreground size-5", "aria-label": t('unpick', { member: row.label }), disabled: pending, onClick: () => toggle(row), children: _jsx(Close, { className: "size-3" }) })] }) }, candidateKey(row)))) })) : null, _jsxs("div", { className: "border-border overflow-hidden rounded-md border", children: [_jsxs(InputGroupSearch, { className: "border-b", children: [_jsx(InputGroupSearchIcon, { children: _jsx(MagnifyingGlassIcon, {}) }), _jsx(InputGroupSearchInput, { value: query, onChange: (event) => {
|
|
169
|
+
setQuery(event.target.value);
|
|
170
|
+
setHighlight(0);
|
|
171
|
+
}, onKeyDown: onKeyDown, placeholder: t('search'), "aria-label": t('search'), autoFocus: true }), query ? _jsx(InputGroupSearchClear, { onClick: () => setQuery('') }) : null] }), _jsx("div", { ref: listRef, className: "max-h-72 overflow-y-auto p-1", children: loading ? (_jsx("div", { className: "flex justify-center py-6", children: _jsx(Loading, { className: "size-4 shrink-0" }) })) : empty ? (_jsxs("div", { className: "flex flex-col items-center gap-3 px-3 py-6", children: [_jsx("p", { className: "text-muted-foreground text-center text-xs", children: empty }), onInvite && !needle ? (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "gap-1.5", onClick: onInvite, children: [_jsx(EnvelopeSimpleIcon, { "aria-hidden": true, className: "size-3.5 shrink-0" }), t('inviteByEmail')] })) : null] })) : (_jsxs(_Fragment, { children: [group(t('people'), shownPeople, 0), group(t('agents'), shownAgents, shownPeople.length)] })) })] })] }), _jsxs(ModalFooter, { className: "sm:justify-between", children: [onInvite ? (_jsx(Button, { type: "button", variant: "transparent", size: "sm", className: "text-muted-foreground hover:text-foreground h-auto p-0 text-xs font-normal", onClick: onInvite, children: t('inviteByEmail') })) : (_jsx("span", {})), _jsxs("div", { className: "flex w-full flex-col-reverse gap-2 sm:w-auto sm:flex-row", children: [_jsx(Button, { type: "button", variant: "outline-ghost", onClick: onDone, disabled: pending, children: t('cancel') }), _jsxs(Button, { type: "button", onClick: submit, disabled: picked.length === 0 || pending, "aria-busy": pending || undefined, children: [pending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, picked.length === 0 ? t('confirmEmpty') : t('confirm', { what: summary(pickedPeople.length, pickedAgents.length) })] })] })] })] }));
|
|
172
|
+
}
|
|
173
|
+
/** "Join channel", in the header of a public channel the viewer is not in. */
|
|
174
|
+
export function JoinChannelButton({ projectId, space }) {
|
|
175
|
+
const t = useTranslations('spaces.view');
|
|
176
|
+
const join = useJoinProjectSpace(projectId, space.space_id);
|
|
177
|
+
const name = space.slug ?? space.name;
|
|
178
|
+
return (_jsxs(Button, { size: "sm", variant: "secondary", className: "shrink-0", disabled: join.isPending, "aria-busy": join.isPending || undefined, onClick: () => join.mutate(undefined, {
|
|
179
|
+
onSuccess: () => successToast(t('joined', { name })),
|
|
180
|
+
onError: () => errorToast(t('joinFailed')),
|
|
181
|
+
}), children: [join.isPending ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, t('join')] }));
|
|
182
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type spaceRoster } from './space-timeline.js';
|
|
2
|
+
type Roster = ReturnType<typeof spaceRoster>;
|
|
3
|
+
/**
|
|
4
|
+
* Who is in the channel, in its header: its members' faces, a hairline, its
|
|
5
|
+
* agents' tiles. The same for every viewer (the channel's member list and
|
|
6
|
+
* agent list, not the threads a viewer can load). People and agents never mix, so a glance tells them apart.
|
|
7
|
+
* Hover, or a click, opens the session header's people card
|
|
8
|
+
* (`SessionParticipantStack`): People, then Agents, each a label, a count and
|
|
9
|
+
* plain rows. Its one action, "View all members", opens the Members tab.
|
|
10
|
+
*/
|
|
11
|
+
export declare function SpaceRosterButton({ roster, onViewAll }: {
|
|
12
|
+
roster: Roster;
|
|
13
|
+
onViewAll: () => void;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { useTranslations } from '../../i18n/index.js';
|
|
5
|
+
import { AgentMark } from '../../internal/components/agent-mark.js';
|
|
6
|
+
import { AvatarGroup, AvatarGroupCount } from '../../primitives/avatar.js';
|
|
7
|
+
import { Button } from '../../primitives/button.js';
|
|
8
|
+
import { FadedScrollArea } from '../../primitives/faded-scroll-area.js';
|
|
9
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from '../../primitives/hover-card.js';
|
|
10
|
+
import { UserAvatar } from '../../primitives/user-avatar.js';
|
|
11
|
+
import { participantLabel } from '../../session/participants/session-participants.js';
|
|
12
|
+
import { agentLabel } from './space-timeline.js';
|
|
13
|
+
/** Faces before the rest collapse into "+N": two people, three agents. */
|
|
14
|
+
const PEOPLE_SHOWN = 2;
|
|
15
|
+
const AGENTS_SHOWN = 3;
|
|
16
|
+
/**
|
|
17
|
+
* Who is in the channel, in its header: its members' faces, a hairline, its
|
|
18
|
+
* agents' tiles. The same for every viewer (the channel's member list and
|
|
19
|
+
* agent list, not the threads a viewer can load). People and agents never mix, so a glance tells them apart.
|
|
20
|
+
* Hover, or a click, opens the session header's people card
|
|
21
|
+
* (`SessionParticipantStack`): People, then Agents, each a label, a count and
|
|
22
|
+
* plain rows. Its one action, "View all members", opens the Members tab.
|
|
23
|
+
*/
|
|
24
|
+
export function SpaceRosterButton({ roster, onViewAll }) {
|
|
25
|
+
const t = useTranslations('spaces.view');
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const { people, agents } = roster;
|
|
28
|
+
const morePeople = people.length - PEOPLE_SHOWN;
|
|
29
|
+
const moreAgents = agents.length - AGENTS_SHOWN;
|
|
30
|
+
const label = t('roster', {
|
|
31
|
+
people: t('rosterPeople', { count: people.length }),
|
|
32
|
+
agents: t('rosterAgents', { count: agents.length }),
|
|
33
|
+
});
|
|
34
|
+
return (_jsxs(HoverCard, { open: open, onOpenChange: setOpen, openDelay: 300, closeDelay: 100, children: [_jsx(HoverCardTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": label, "aria-expanded": open, onClick: () => setOpen((value) => !value), className: "hover:bg-hover focus-visible:ring-ring data-[state=open]:bg-hover flex h-8 shrink-0 cursor-pointer items-center gap-2 rounded-md border px-1.5 outline-none focus-visible:ring-2", children: [people.length > 0 ? (_jsxs(AvatarGroup, { className: "-space-x-1", children: [people.slice(0, PEOPLE_SHOWN).map((person) => (_jsx(UserAvatar, { email: person.email ?? '', name: participantLabel(person), avatarUrl: person.avatarUrl, size: "xs", ring: true }, person.id))), morePeople > 0 ? (_jsxs(AvatarGroupCount, { className: "h-5 w-auto min-w-5 rounded-full px-1 text-xs tabular-nums", children: ["+", morePeople] })) : null] })) : null, people.length > 0 && agents.length > 0 ? _jsx("span", { "aria-hidden": true, className: "bg-border h-4 w-px shrink-0" }) : null, agents.length > 0 ? (_jsxs("span", { className: "flex -space-x-1", children: [agents.slice(0, AGENTS_SHOWN).map((agent) => (
|
|
35
|
+
// 18px tile: the people's `xs` faces and the `+N` chip are `h-5`.
|
|
36
|
+
_jsx(AgentMark, { slug: agent, size: 14, tile: 18, className: "ring-background ring-2" }, agent))), moreAgents > 0 ? (_jsxs("span", { className: "bg-muted text-muted-foreground ring-background flex h-5 min-w-5 items-center justify-center rounded-sm px-1 text-xs font-medium tabular-nums ring-2", children: ["+", moreAgents] })) : null] })) : null] }) }), _jsxs(HoverCardContent, { side: "bottom", align: "end", sideOffset: 6, animated: false, className: "w-72 overflow-hidden p-0", onEscapeKeyDown: () => setOpen(false), children: [_jsxs(FadedScrollArea, { fadeColor: "from-popover", fadeSize: "6", className: "max-h-80 overscroll-contain", children: [people.length > 0 ? (_jsx(RosterGroup, { title: t('rosterTitlePeople'), count: people.length, children: people.map((person) => (_jsxs("li", { className: "text-foreground flex min-h-9 items-center gap-2.5 px-2 py-1 text-sm", children: [_jsx(UserAvatar, { email: person.email ?? '', name: participantLabel(person), avatarUrl: person.avatarUrl, size: "md" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: participantLabel(person) })] }, person.id))) })) : null, agents.length > 0 ? (_jsx(RosterGroup, { title: t('rosterTitleAgents'), count: agents.length, children: agents.map((agent) => (_jsxs("li", { className: "text-foreground flex min-h-9 items-center gap-2.5 px-2 py-1 text-sm", children: [_jsx(AgentMark, { slug: agent, size: 21 }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate leading-tight", children: agentLabel(agent) }), _jsx("span", { className: "text-muted-foreground truncate text-xs leading-tight tabular-nums", children: t('agentThreads', { count: roster.agentThreads[agent] ?? 0 }) })] })] }, agent))) })) : null] }), _jsx("div", { className: "border-t p-1", children: _jsx(Button, { variant: "ghost", size: "sm", className: "w-full justify-start font-normal", onClick: () => {
|
|
37
|
+
setOpen(false);
|
|
38
|
+
onViewAll();
|
|
39
|
+
}, children: t('viewAllMembers') }) })] })] }));
|
|
40
|
+
}
|
|
41
|
+
/** "People 6", then the rows: the session card's header line, once per group. */
|
|
42
|
+
function RosterGroup({ title, count, children }) {
|
|
43
|
+
return (_jsxs("section", { children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between px-3.5 pt-2.5 pb-1 text-xs", children: [_jsx("span", { children: title }), _jsx("span", { className: "tabular-nums", children: count })] }), _jsx("ul", { className: "p-1 pt-0", children: children })] }));
|
|
44
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ProjectSpace } from '@kortix/sdk';
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import { type SpaceThreads } from './space-timeline-view.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Threads tab of a channel or DM: a search over titles and first prompts,
|
|
6
|
+
* one Filter menu (Status, Started by, Agent, Activity), the active filters
|
|
7
|
+
* as removable tokens, and the rows. Search, person, agent and activity run
|
|
8
|
+
* on the server; Status reads the loaded rows, as on the Sessions page. The
|
|
9
|
+
* URL holds every value (`?q=&status=&by=&agent=&active=`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function SpaceThreadsTab({ projectId, space, threads, selectedId, onOpenThread, params, setParams, }: {
|
|
12
|
+
projectId: string;
|
|
13
|
+
space: ProjectSpace;
|
|
14
|
+
/** The space's unfiltered threads (the Chat tab's list): the people and agents the menu offers. */
|
|
15
|
+
threads: SpaceThreads;
|
|
16
|
+
selectedId: string | null;
|
|
17
|
+
onOpenThread: (sessionId: string) => void;
|
|
18
|
+
params: {
|
|
19
|
+
get(name: string): string | null;
|
|
20
|
+
};
|
|
21
|
+
setParams: (change: (params: URLSearchParams) => void) => void;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
/**
|
|
24
|
+
* The search field of a channel tab's toolbar (Threads, Links). The text is
|
|
25
|
+
* local while typing; `onSearch` gets the settled query ('' below `minChars`),
|
|
26
|
+
* and Esc clears it. A new `value` (Clear, the back button) replaces the text.
|
|
27
|
+
*/
|
|
28
|
+
export declare function FilterSearch({ value, placeholder, minChars, onSearch, }: {
|
|
29
|
+
value: string;
|
|
30
|
+
placeholder: string;
|
|
31
|
+
minChars?: number;
|
|
32
|
+
onSearch: (q: string) => void;
|
|
33
|
+
}): import("react").JSX.Element;
|
|
34
|
+
/** The quiet "Filter" button and its menu; the children are `Facet` rows. */
|
|
35
|
+
export declare function FilterMenu({ children }: {
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
}): import("react").JSX.Element;
|
|
38
|
+
export type FilterToken<K extends string> = {
|
|
39
|
+
key: K;
|
|
40
|
+
facet: string;
|
|
41
|
+
label: string;
|
|
42
|
+
icon?: ReactNode;
|
|
43
|
+
};
|
|
44
|
+
/** The active filters as removable tokens, then Clear. */
|
|
45
|
+
export declare function FilterTokens<K extends string>({ tokens, onRemove, onClear, }: {
|
|
46
|
+
tokens: readonly FilterToken<K>[];
|
|
47
|
+
onRemove: (key: K) => void;
|
|
48
|
+
onClear: () => void;
|
|
49
|
+
}): import("react").JSX.Element | null;
|
|
50
|
+
/** A facet row: its name, its current value muted on the right, its choices in a submenu. */
|
|
51
|
+
export declare function Facet({ label, value, wide, children }: {
|
|
52
|
+
label: string;
|
|
53
|
+
value: string;
|
|
54
|
+
wide?: boolean;
|
|
55
|
+
children: ReactNode;
|
|
56
|
+
}): import("react").JSX.Element;
|