@kortix/ui 0.14.5 → 0.14.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_vendor/kortix-shared.js +489 -381
- package/dist/access/access/access-dialog.js +8 -5
- package/dist/access/access/copy-row.js +2 -2
- package/dist/access/sharing-picker.js +2 -1
- package/dist/browser/app-in-browser.js +5 -3
- package/dist/browser/browser-panel.js +6 -6
- package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
- package/dist/browser/sandbox-browser-chrome.js +4 -3
- package/dist/customize/agents/agent-access.d.ts +20 -0
- package/dist/customize/agents/agent-access.js +17 -0
- package/dist/customize/agents/agent-authority-card.js +1 -2
- package/dist/customize/agents/agent-detail-aside.js +15 -14
- package/dist/customize/agents/agent-drives-section.js +5 -6
- package/dist/customize/agents/agent-grant-pages.js +73 -76
- package/dist/customize/agents/agent-nav.prototype.d.ts +35 -0
- package/dist/customize/agents/agent-nav.prototype.js +95 -0
- package/dist/customize/agents/agent-page.js +59 -23
- package/dist/customize/agents/agent-people-section.js +18 -17
- package/dist/customize/agents/agent-triggers-section.js +6 -5
- package/dist/customize/agents/agents-page.js +18 -1
- package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
- package/dist/customize/connectors/catalog/catalog-entry.d.ts +14 -0
- package/dist/customize/connectors/catalog/catalog-entry.js +18 -1
- package/dist/customize/connectors/catalog/connector-browse.d.ts +28 -0
- package/dist/customize/connectors/catalog/connector-browse.js +18 -5
- package/dist/customize/connectors/connector-routes.d.ts +3 -1
- package/dist/customize/connectors/connector-routes.js +18 -0
- package/dist/customize/connectors/connectors-page.js +23 -4
- package/dist/customize/connectors/detail/app-page.js +19 -5
- package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
- package/dist/customize/connectors/detail/connector-modal.d.ts +10 -13
- package/dist/customize/connectors/detail/connector-modal.js +18 -101
- package/dist/customize/connectors/detail/connector-notice.js +6 -2
- package/dist/customize/connectors/detail/connector-page.d.ts +30 -0
- package/dist/customize/connectors/detail/connector-page.js +26 -13
- package/dist/customize/providers/provider-accordion.js +2 -1
- package/dist/customize/providers/provider-connect.js +2 -1
- package/dist/customize/sections/component/telegram-channel-row.js +1 -1
- package/dist/customize/sections/connector-connection-modal.js +11 -9
- package/dist/customize/sections/connectors-view.js +11 -13
- package/dist/customize/sections/llm-provider/custom-provider-form.js +24 -7
- package/dist/customize/sections/llm-provider/custom-provider-panel.js +44 -8
- package/dist/customize/sections/llm-provider/types.d.ts +3 -0
- package/dist/customize/sections/llm-provider/utils.js +2 -2
- package/dist/customize/sections/view/agent-editor-access-fields.d.ts +9 -16
- package/dist/customize/sections/view/agent-editor-access-fields.js +30 -30
- package/dist/customize/sections/view/agent-editor-basics-fields.js +3 -10
- package/dist/customize/sections/view/agent-editor-primitives.d.ts +28 -10
- package/dist/customize/sections/view/agent-editor-primitives.js +32 -19
- package/dist/customize/sections/view/agent-editor.js +3 -3
- package/dist/customize/sections/view/gateway/gateway-routing.js +18 -21
- package/dist/customize/sections/view/grant-mode-field.d.ts +2 -5
- package/dist/customize/sections/view/grant-mode-field.js +10 -18
- package/dist/customize/sections/view/permission-editor.js +11 -8
- package/dist/customize/sections/view/secrets-view.js +2 -1
- package/dist/customize/settings/rail-copy-context.js +5 -3
- package/dist/customize/settings/tabs/general-tab.d.ts +9 -2
- package/dist/customize/settings/tabs/general-tab.js +33 -11
- package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
- package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
- package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
- package/dist/customize/shared/capability-tabs.js +2 -1
- package/dist/customize/skills/skills-page.js +3 -5
- package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
- package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
- package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
- package/dist/customize/triggers/schedule/when-event.js +1 -1
- package/dist/diff/diff-view.js +9 -10
- package/dist/files/editor/code-editor.js +2 -3
- package/dist/files/project/api/runtime-files.js +3 -2
- package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
- package/dist/files/project/components/change-request-detail-dialog.js +3 -5
- package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
- package/dist/files/project/components/change-request-status-badge.js +27 -0
- package/dist/files/project/components/change-requests-panel.js +1 -1
- package/dist/files/project/components/drive-explorer.js +2 -1
- package/dist/files/project/components/drive-grid-view.js +11 -7
- package/dist/files/project/components/drive-list-view.js +4 -4
- package/dist/files/project/components/file-breadcrumbs.js +1 -1
- package/dist/files/project/components/file-explorer-page.js +1 -1
- package/dist/files/project/components/file-history-popover.js +1 -6
- package/dist/files/project/components/file-search.js +6 -1
- package/dist/files/project/components/open-change-request-dialog.js +2 -1
- package/dist/files/project/components/version-selector-logic.d.ts +11 -0
- package/dist/files/project/components/version-selector-logic.js +26 -0
- package/dist/files/project/components/version-selector.js +11 -20
- package/dist/files/project/file-source.js +18 -1
- package/dist/files/project/hooks/use-file-content.js +2 -0
- package/dist/files/project/hooks/use-server-health.js +17 -15
- package/dist/files/renderers/docx/docx-viewer.js +0 -5
- package/dist/files/renderers/image-renderer.js +6 -24
- package/dist/files/renderers/pdf/pdf-viewer.js +1 -1
- package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
- package/dist/files/renderers/show-content-renderer.js +1 -1
- package/dist/files/renderers/sqlite-renderer.js +8 -3
- package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
- package/dist/files/tree/components/file-tree-item.js +1 -1
- package/dist/files/viewer/file-header.js +1 -1
- package/dist/files/viewer/file-preview-modal.d.ts +1 -1
- package/dist/files/viewer/file-preview-modal.js +13 -3
- package/dist/files/viewer/file-source.d.ts +3 -1
- package/dist/files/viewer/use-file-content-state.js +27 -8
- package/dist/files/viewer/viewer-actions.js +2 -2
- package/dist/i18n/en.d.ts +355 -12
- package/dist/i18n/en.js +360 -17
- package/dist/internal/desktop/desktop-back-button.js +5 -3
- package/dist/internal/files/types.d.ts +2 -0
- package/dist/internal/goals/goal-meta.d.ts +30 -0
- package/dist/internal/goals/goal-meta.js +55 -0
- package/dist/internal/hooks/use-auto-scroll.js +5 -3
- package/dist/internal/lib/query-client-singleton.d.ts +9 -3
- package/dist/internal/lib/query-client-singleton.js +18 -12
- package/dist/internal/notifications/notification-rows.js +2 -1
- package/dist/internal/outcomes/outcome-types.d.ts +5 -0
- package/dist/internal/share/session-shares-modal.js +3 -7
- package/dist/internal/stores/kortix-computer-store.js +14 -2
- package/dist/internal/stores/session-hover-store.d.ts +32 -0
- package/dist/internal/stores/session-hover-store.js +38 -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 +2 -0
- package/dist/lib/routes.js +1 -0
- package/dist/markdown/code/inline-code.js +8 -0
- package/dist/markdown/code/output-paths-context.d.ts +18 -0
- package/dist/markdown/code/output-paths-context.js +35 -0
- package/dist/markdown/genui/components/charts.js +6 -5
- package/dist/primitives/map.js +2 -1
- package/dist/primitives/preview-image.js +4 -2
- package/dist/primitives/settings-section-header.d.ts +1 -1
- package/dist/primitives/textarea.js +1 -1
- package/dist/primitives/typed-title.js +12 -14
- package/dist/primitives/typing-animation.js +4 -2
- package/dist/session/composer/agent-selector.js +6 -1
- package/dist/session/composer/attachment-tile.d.ts +0 -2
- package/dist/session/composer/attachment-tile.js +0 -8
- package/dist/session/composer/clipboard-files.d.ts +2 -1
- package/dist/session/composer/composer-chat-input.js +5 -3
- package/dist/session/composer/composer-draft-recovery.d.ts +1 -2
- package/dist/session/composer/composer-draft-recovery.js +0 -13
- package/dist/session/composer/composer-logic.js +8 -7
- package/dist/session/composer/context-ring.d.ts +1 -1
- package/dist/session/composer/context-ring.js +2 -2
- package/dist/session/composer/context-usage.d.ts +3 -6
- package/dist/session/composer/context-usage.js +3 -6
- package/dist/session/composer/draft/composer-draft.js +4 -4
- package/dist/session/composer/inbox-row-claims.d.ts +2 -2
- package/dist/session/composer/inbox-row-claims.js +1 -1
- package/dist/session/composer/pasted-text.js +3 -2
- package/dist/session/composer/queue-projection.d.ts +3 -2
- package/dist/session/composer/queued-prompt-edit.d.ts +1 -1
- package/dist/session/composer/reasoning-effort-selector.d.ts +2 -1
- package/dist/session/composer/session-composer-readiness.d.ts +7 -22
- package/dist/session/composer/session-composer-readiness.js +12 -40
- package/dist/session/composer/stores/queued-draft-store.js +3 -2
- package/dist/session/composer/token-progress.d.ts +1 -1
- package/dist/session/composer/token-progress.js +2 -2
- package/dist/session/header/reminder-parts.d.ts +11 -2
- package/dist/session/header/reminder-parts.js +9 -4
- package/dist/session/header/session-goal-indicator.d.ts +1 -0
- package/dist/session/header/session-goal-indicator.js +41 -0
- package/dist/session/header/session-site-header.js +2 -1
- package/dist/session/media/sandbox-url-detector.js +0 -7
- package/dist/session/outcomes/change-request-outcomes.js +24 -3
- package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
- package/dist/session/panel/advanced/advanced-panel.js +3 -7
- package/dist/session/panel/easy/app-preview.js +12 -2
- package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
- package/dist/session/panel/easy/easy-panel-logic.js +1 -1
- package/dist/session/panel/easy/outputs-card.js +2 -1
- package/dist/session/panel/easy/panel-card.js +5 -3
- package/dist/session/panel/easy/preview-load.d.ts +14 -0
- package/dist/session/panel/easy/preview-load.js +15 -0
- package/dist/session/panel/easy/step-detail-body.js +2 -7
- package/dist/session/panel/session-action-panel-column.js +11 -1
- package/dist/session/panel/session-audit-shared.d.ts +0 -4
- package/dist/session/panel/session-audit-shared.js +1 -1
- package/dist/session/panel/session-changes-shared.d.ts +2 -1
- package/dist/session/panel/session-panel-provider.js +6 -2
- package/dist/session/panel/shared/use-panel-derivations.js +6 -4
- package/dist/session/panel/use-session-panel-layout.js +7 -5
- package/dist/session/print/session-print-header.js +9 -8
- package/dist/session/prompts/session-permission-prompt.js +6 -3
- package/dist/session/status/provisioning-failure.d.ts +2 -1
- package/dist/session/status/session-error-banner.js +8 -6
- package/dist/session/status/session-starting-loader.d.ts +24 -31
- package/dist/session/status/session-starting-loader.js +32 -81
- package/dist/session/thread/chat-minimap-items.d.ts +4 -4
- package/dist/session/thread/first-prompt-handover.d.ts +3 -2
- package/dist/session/thread/message-parsing.d.ts +3 -3
- package/dist/session/thread/session-chat.js +15 -19
- package/dist/session/thread/session-history-scroll.d.ts +2 -1
- package/dist/session/thread/session-history.js +3 -1
- package/dist/session/thread/session-thread.js +1 -1
- package/dist/session/thread/transcript.js +13 -7
- package/dist/session/tool/shared/file-list.js +19 -2
- package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
- package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
- package/dist/session/tool/shared/infrastructure-output.js +9 -5
- package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
- package/dist/session/tool/shared/infrastructure-rows.js +7 -5
- package/dist/session/tool/tools/bash-tool.js +10 -6
- package/dist/session/tool/tools/web-search-tool.js +25 -3
- package/dist/session/turn/recovered-failures.d.ts +8 -0
- package/dist/session/turn/recovered-failures.js +32 -0
- package/dist/session/turn/streaming-cadence.js +5 -8
- package/dist/styles.css +1 -1
- package/dist/terminal/pty-connection.d.ts +5 -4
- package/dist/terminal/session-terminal-panel.js +3 -5
- package/dist/theme/theme.js +2 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
- package/dist/workspace/review/review-center.js +13 -10
- package/dist/workspace/saved-session-skeleton-shape.d.ts +4 -3
- package/dist/workspace/saved-session-skeleton.d.ts +5 -1
- package/dist/workspace/saved-session-skeleton.js +5 -4
- package/dist/workspace/session-workspace.js +9 -14
- package/dist/workspace/sessions/deleted-session-row.js +5 -2
- package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
- package/dist/workspace/sidebar/footer/project-goals-nav.d.ts +6 -0
- package/dist/workspace/sidebar/footer/project-goals-nav.js +26 -0
- package/dist/workspace/sidebar/footer/project-mcp-nav.js +6 -2
- package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
- package/dist/workspace/sidebar/modal/session-embed-section.d.ts +25 -0
- package/dist/workspace/sidebar/modal/session-embed-section.js +71 -0
- package/dist/workspace/sidebar/modal/share-session-modal.js +2 -2
- 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 +12 -3
- package/dist/workspace/sidebar/session-brief-hover-card.js +5 -8
- package/package.json +4 -4
- package/skills/kortix-ui/references/modules.md +2 -0
- package/dist/session/status/use-readiness-settling.d.ts +0 -18
- package/dist/session/status/use-readiness-settling.js +0 -43
- package/dist/workspace/sidebar/modal/session-public-link-section.d.ts +0 -13
- package/dist/workspace/sidebar/modal/session-public-link-section.js +0 -66
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import { OWN_SCROLL_MS, chevronVisible, isAtEnd, keyScrollIntentFor, pickAnchorIndex, prefersReducedMotion, roomUnderNewestTurn, settleMotion, shouldReleaseFollow, } from './use-auto-scroll-physics.js';
|
|
4
4
|
export { AT_END_PX, BOTTOM_GAP_PX, CHEVRON_PX, GLIDE_MIN_PX, OWN_SCROLL_MS, TURN_TOP_OFFSET, chevronVisible, classifyScrollKey, isAtEnd, isEditableTarget, isSpaceActivatedTarget, keyScrollIntentFor, pickAnchorIndex, roomUnderNewestTurn, settleMotion, shouldReleaseFollow, } from './use-auto-scroll-physics.js';
|
|
5
5
|
/**
|
|
@@ -234,7 +234,9 @@ export function useAutoScroll({ hasContent = false, } = {}) {
|
|
|
234
234
|
markOwnScroll(OWN_SCROLL_MS);
|
|
235
235
|
el.scrollTop = end;
|
|
236
236
|
}, [sizeRoom, glideTo, markOwnScroll]);
|
|
237
|
-
|
|
237
|
+
useLayoutEffect(() => {
|
|
238
|
+
settleRef.current = settle;
|
|
239
|
+
}, [settle]);
|
|
238
240
|
const goToEnd = useCallback((behavior) => {
|
|
239
241
|
const el = scrollRef.current;
|
|
240
242
|
if (!el)
|
|
@@ -455,7 +457,7 @@ export function useAutoScroll({ hasContent = false, } = {}) {
|
|
|
455
457
|
document.removeEventListener('keydown', onKeyDown, { capture: true });
|
|
456
458
|
el.removeEventListener('scroll', onScroll);
|
|
457
459
|
};
|
|
458
|
-
}, [goToEnd, endGlide, hasContent, setFollow
|
|
460
|
+
}, [goToEnd, endGlide, hasContent, setFollow]);
|
|
459
461
|
return {
|
|
460
462
|
scrollRef,
|
|
461
463
|
contentRef,
|
|
@@ -3,7 +3,13 @@ import type { QueryClient } from '@tanstack/react-query';
|
|
|
3
3
|
export declare function registerQueryClient(client: QueryClient): void;
|
|
4
4
|
export declare function getSharedQueryClient(): QueryClient | null;
|
|
5
5
|
/**
|
|
6
|
-
* Run `run` with the shared client
|
|
7
|
-
*
|
|
6
|
+
* Run `run` with the shared client now (if one is registered), and again with
|
|
7
|
+
* every DIFFERENT client registered later. Returns the unsubscribe.
|
|
8
|
+
*
|
|
9
|
+
* AuthProvider sits ABOVE ReactQueryProvider, and #10344 mounts the app shell
|
|
10
|
+
* one auth round trip sooner, so a caller can ask before any client exists.
|
|
11
|
+
* `ReactQueryProvider` registers during render, and React can discard that
|
|
12
|
+
* render (a hydration retry, a suspension, Strict Mode) and commit a second
|
|
13
|
+
* client: a caller that ran once would hold a client the app no longer reads.
|
|
8
14
|
*/
|
|
9
|
-
export declare function
|
|
15
|
+
export declare function onEachQueryClient(run: (client: QueryClient) => void): () => void;
|
|
@@ -8,27 +8,33 @@
|
|
|
8
8
|
* from the central reset helper.
|
|
9
9
|
*/
|
|
10
10
|
let sharedQueryClient = null;
|
|
11
|
-
|
|
11
|
+
/** `onEachQueryClient` subscribers. replica-local: one tab's app. */
|
|
12
|
+
const followers = new Set();
|
|
12
13
|
export function registerQueryClient(client) {
|
|
14
|
+
if (sharedQueryClient === client)
|
|
15
|
+
return;
|
|
13
16
|
sharedQueryClient = client;
|
|
14
|
-
|
|
15
|
-
// shell one auth round trip sooner, so `adoptDeviceCaches` can run before
|
|
16
|
-
// this registers. Flush anyone who asked for the client before it existed.
|
|
17
|
-
const pending = waiters;
|
|
18
|
-
waiters = [];
|
|
19
|
-
for (const run of pending)
|
|
17
|
+
for (const run of [...followers])
|
|
20
18
|
run(client);
|
|
21
19
|
}
|
|
22
20
|
export function getSharedQueryClient() {
|
|
23
21
|
return sharedQueryClient;
|
|
24
22
|
}
|
|
25
23
|
/**
|
|
26
|
-
* Run `run` with the shared client
|
|
27
|
-
*
|
|
24
|
+
* Run `run` with the shared client now (if one is registered), and again with
|
|
25
|
+
* every DIFFERENT client registered later. Returns the unsubscribe.
|
|
26
|
+
*
|
|
27
|
+
* AuthProvider sits ABOVE ReactQueryProvider, and #10344 mounts the app shell
|
|
28
|
+
* one auth round trip sooner, so a caller can ask before any client exists.
|
|
29
|
+
* `ReactQueryProvider` registers during render, and React can discard that
|
|
30
|
+
* render (a hydration retry, a suspension, Strict Mode) and commit a second
|
|
31
|
+
* client: a caller that ran once would hold a client the app no longer reads.
|
|
28
32
|
*/
|
|
29
|
-
export function
|
|
33
|
+
export function onEachQueryClient(run) {
|
|
34
|
+
followers.add(run);
|
|
30
35
|
if (sharedQueryClient)
|
|
31
36
|
run(sharedQueryClient);
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
return () => {
|
|
38
|
+
followers.delete(run);
|
|
39
|
+
};
|
|
34
40
|
}
|
|
@@ -44,10 +44,11 @@ export function newArrivals(seen, rows) {
|
|
|
44
44
|
* is not.
|
|
45
45
|
*/
|
|
46
46
|
export function planArrivals(fresh, context) {
|
|
47
|
+
const unseen = new Set(context.unseen);
|
|
47
48
|
const announce = fresh
|
|
48
49
|
.filter((row) => !row.session_id ||
|
|
49
50
|
(!context.onScreen(row.session_id) &&
|
|
50
|
-
!(row.kind === 'turn_done' &&
|
|
51
|
+
!(row.kind === 'turn_done' && unseen.has(row.session_id))))
|
|
51
52
|
.slice(0, MAX_ARRIVAL_ALERTS);
|
|
52
53
|
return context.os ? { toast: [], os: announce } : { toast: announce, os: [] };
|
|
53
54
|
}
|
|
@@ -49,6 +49,11 @@ export interface OutcomeAction {
|
|
|
49
49
|
intent: 'open' | 'link';
|
|
50
50
|
/** Required when `intent` is `link`; ignored otherwise. */
|
|
51
51
|
href?: string;
|
|
52
|
+
/**
|
|
53
|
+
* For `open`: the source id the modal opens when it is not this outcome's
|
|
54
|
+
* own. A superseded change request opens its replacement (KRTX-3031).
|
|
55
|
+
*/
|
|
56
|
+
target?: string;
|
|
52
57
|
}
|
|
53
58
|
export interface Outcome {
|
|
54
59
|
/**
|
|
@@ -16,13 +16,13 @@ import { isShareLive, publicShareUrl, shareListState, useRevokePublicShare, useS
|
|
|
16
16
|
import { cn } from '../../lib/utils.js';
|
|
17
17
|
function ShareRow({ share, onRevoke, isRevoking, }) {
|
|
18
18
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
19
|
-
const tTranscript = useTranslations('hardcodedUi.publicTranscriptShare');
|
|
20
19
|
const [copied, setCopied] = useState(false);
|
|
21
20
|
const live = isShareLive(share);
|
|
22
21
|
// Read at render time: `publicShareUrl` is null outside a browser (SSR).
|
|
23
22
|
const url = publicShareUrl(share.public_path);
|
|
24
23
|
const isFile = share.resource_type === 'file';
|
|
25
|
-
// A transcript share
|
|
24
|
+
// A legacy transcript share (revoked; the feature is removed) still carries
|
|
25
|
+
// no port and no file: its label is the row's own meta.
|
|
26
26
|
const isTranscript = share.resource_type === 'transcript';
|
|
27
27
|
const Icon = isFile ? FileText : isTranscript ? ChatText : Globe;
|
|
28
28
|
const copy = async () => {
|
|
@@ -33,11 +33,7 @@ function ShareRow({ share, onRevoke, isRevoking, }) {
|
|
|
33
33
|
successToast(tI18nComplete.raw('textd0f24de8dbc6'));
|
|
34
34
|
setTimeout(() => setCopied(false), 2000);
|
|
35
35
|
};
|
|
36
|
-
return (_jsxs("li", { className: cn('group bg-popover flex items-center gap-3 rounded-md border px-4 py-2.5 transition-colors', !live && 'opacity-60'), children: [_jsx("span", { className: cn('flex size-9 shrink-0 items-center justify-center rounded-sm', live ? 'bg-kortix-green/15' : 'bg-muted'), children: _jsx(Icon, { className: cn('size-5', live ? 'text-kortix-green' : 'text-muted-foreground') }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium", children: share.label }), !live && (_jsx(Badge, { variant: "outline", size: "xs", children: share.revoked_at ? 'Revoked' : 'Expired' }))] }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: isFile
|
|
37
|
-
? share.file_path
|
|
38
|
-
: isTranscript
|
|
39
|
-
? tTranscript('rowMeta')
|
|
40
|
-
: `Port ${share.port}${share.path}` })] }), live && (_jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [_jsx(Hint, { label: copied ? 'Copied' : tI18nComplete.raw('textdbf362d4f210'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nComplete.raw('textdbf362d4f210'), disabled: !url, onClick: () => void copy(), className: "size-8 active:scale-[0.96]", children: copied ? (_jsx(Check, { className: "text-kortix-green size-4" })) : (_jsx(Link2, { className: "size-4" })) }) }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: onRevoke, disabled: isRevoking, children: [isRevoking ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, tI18nComplete.raw('text87e6d00bbf53')] })] }))] }));
|
|
36
|
+
return (_jsxs("li", { className: cn('group bg-popover flex items-center gap-3 rounded-md border px-4 py-2.5 transition-colors', !live && 'opacity-60'), children: [_jsx("span", { className: cn('flex size-9 shrink-0 items-center justify-center rounded-sm', live ? 'bg-kortix-green/15' : 'bg-muted'), children: _jsx(Icon, { className: cn('size-5', live ? 'text-kortix-green' : 'text-muted-foreground') }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium", children: share.label }), !live && (_jsx(Badge, { variant: "outline", size: "xs", children: share.revoked_at ? 'Revoked' : 'Expired' }))] }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: isFile ? share.file_path : isTranscript ? share.label : `Port ${share.port}${share.path}` })] }), live && (_jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [_jsx(Hint, { label: copied ? 'Copied' : tI18nComplete.raw('textdbf362d4f210'), side: "bottom", children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": tI18nComplete.raw('textdbf362d4f210'), disabled: !url, onClick: () => void copy(), className: "size-8 active:scale-[0.96]", children: copied ? (_jsx(Check, { className: "text-kortix-green size-4" })) : (_jsx(Link2, { className: "size-4" })) }) }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: onRevoke, disabled: isRevoking, children: [isRevoking ? _jsx(Loading, { className: "size-4 shrink-0" }) : null, tI18nComplete.raw('text87e6d00bbf53')] })] }))] }));
|
|
41
37
|
}
|
|
42
38
|
export function SessionSharesModal({ projectId, sessionId, open, onOpenChange, }) {
|
|
43
39
|
const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
|
|
@@ -173,6 +173,20 @@ export const useKortixComputerStore = create()(devtools((set, get) => ({
|
|
|
173
173
|
get().setIsActionPanelOpen(true);
|
|
174
174
|
},
|
|
175
175
|
setActiveSession: (sessionId, continuity) => {
|
|
176
|
+
const nextProjectSessionId = continuity?.projectSessionId ?? null;
|
|
177
|
+
const nextIsTransient = continuity?.transient ?? false;
|
|
178
|
+
// The boot shell of the session whose real chat already owns the right
|
|
179
|
+
// side. The two layers mount in either order, and the chat holds the
|
|
180
|
+
// panel's content: a shell that took over kept the right side after it
|
|
181
|
+
// faded out, so ⌘I reopened the cards instead of the detail it had
|
|
182
|
+
// just hidden (KRTX-2944). Not a session change, so nothing moves.
|
|
183
|
+
if (nextIsTransient &&
|
|
184
|
+
nextProjectSessionId !== null &&
|
|
185
|
+
get()._activeSessionId !== null &&
|
|
186
|
+
!get()._activeIsTransient &&
|
|
187
|
+
get()._activeProjectSessionId === nextProjectSessionId) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
176
190
|
// A quick-view is an intent about the session it was made in — it must
|
|
177
191
|
// not replay when some other session mounts later. Cleared even on the
|
|
178
192
|
// no-op re-activation path below, or a request planted for another
|
|
@@ -182,8 +196,6 @@ export const useKortixComputerStore = create()(devtools((set, get) => ({
|
|
|
182
196
|
set({ pendingQuickView: null });
|
|
183
197
|
}
|
|
184
198
|
const prev = get()._activeSessionId;
|
|
185
|
-
const nextProjectSessionId = continuity?.projectSessionId ?? null;
|
|
186
|
-
const nextIsTransient = continuity?.transient ?? false;
|
|
187
199
|
if (prev === sessionId) {
|
|
188
200
|
// Same layout re-activating (e.g. the shell flipping transient→ready
|
|
189
201
|
// state, or a tab refocus): keep the continuity bookkeeping fresh so
|
|
@@ -59,6 +59,38 @@ interface SessionHoverState {
|
|
|
59
59
|
closeSession: (sessionId: string) => void;
|
|
60
60
|
/** Immediate, for a click that opens something else and for unmount. */
|
|
61
61
|
dismiss: () => void;
|
|
62
|
+
/**
|
|
63
|
+
* The row whose session a click just opened. Its card would only describe
|
|
64
|
+
* the page on screen, so it stays shut until the pointer leaves the row.
|
|
65
|
+
* Unselected, like `warm`.
|
|
66
|
+
*/
|
|
67
|
+
suppressedSessionId: string | null;
|
|
68
|
+
suppress: (sessionId: string) => void;
|
|
69
|
+
release: (sessionId: string) => void;
|
|
62
70
|
}
|
|
63
71
|
export declare const useSessionHoverStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SessionHoverState>>;
|
|
72
|
+
type RowTarget = {
|
|
73
|
+
matches?: (selector: string) => boolean;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* A session row's hover wiring (KRTX-2847). The press focuses the row and the
|
|
77
|
+
* pointer rests on it, so without the click handler every switch showed the
|
|
78
|
+
* card of the session just opened, 200 ms later, until the page took focus.
|
|
79
|
+
* Focus leaving the row releases it only when the pointer is elsewhere: the
|
|
80
|
+
* new page takes focus while the pointer still rests on the row.
|
|
81
|
+
*/
|
|
82
|
+
export declare function sessionRowHoverHandlers(id: string): {
|
|
83
|
+
onPointerEnter: (event: {
|
|
84
|
+
pointerType: string;
|
|
85
|
+
currentTarget: RowTarget;
|
|
86
|
+
}) => void;
|
|
87
|
+
onPointerLeave: () => void;
|
|
88
|
+
onFocus: (event: {
|
|
89
|
+
currentTarget: RowTarget;
|
|
90
|
+
}) => void;
|
|
91
|
+
onBlur: (event: {
|
|
92
|
+
currentTarget: RowTarget;
|
|
93
|
+
}) => void;
|
|
94
|
+
onClick: () => void;
|
|
95
|
+
};
|
|
64
96
|
export {};
|
|
@@ -65,8 +65,11 @@ export const useSessionHoverStore = create((set, get) => ({
|
|
|
65
65
|
via: 'pointer',
|
|
66
66
|
slot: null,
|
|
67
67
|
warm: false,
|
|
68
|
+
suppressedSessionId: null,
|
|
68
69
|
setSlot: (slot) => set({ slot }),
|
|
69
70
|
openSession: (sessionId, anchor, via = 'pointer') => {
|
|
71
|
+
if (get().suppressedSessionId === sessionId)
|
|
72
|
+
return;
|
|
70
73
|
closeTimer = cancel(closeTimer);
|
|
71
74
|
warmTimer = cancel(warmTimer);
|
|
72
75
|
// A pending open belongs to whichever row the pointer has just left.
|
|
@@ -106,4 +109,39 @@ export const useSessionHoverStore = create((set, get) => ({
|
|
|
106
109
|
warmTimer = cancel(warmTimer);
|
|
107
110
|
set({ activeSessionId: null, warm: false });
|
|
108
111
|
},
|
|
112
|
+
suppress: (sessionId) => {
|
|
113
|
+
set({ suppressedSessionId: sessionId });
|
|
114
|
+
get().dismiss();
|
|
115
|
+
},
|
|
116
|
+
release: (sessionId) => {
|
|
117
|
+
if (get().suppressedSessionId === sessionId)
|
|
118
|
+
set({ suppressedSessionId: null });
|
|
119
|
+
},
|
|
109
120
|
}));
|
|
121
|
+
/**
|
|
122
|
+
* A session row's hover wiring (KRTX-2847). The press focuses the row and the
|
|
123
|
+
* pointer rests on it, so without the click handler every switch showed the
|
|
124
|
+
* card of the session just opened, 200 ms later, until the page took focus.
|
|
125
|
+
* Focus leaving the row releases it only when the pointer is elsewhere: the
|
|
126
|
+
* new page takes focus while the pointer still rests on the row.
|
|
127
|
+
*/
|
|
128
|
+
export function sessionRowHoverHandlers(id) {
|
|
129
|
+
const store = () => useSessionHoverStore.getState();
|
|
130
|
+
return {
|
|
131
|
+
onPointerEnter: (event) => {
|
|
132
|
+
if (event.pointerType !== 'touch')
|
|
133
|
+
store().openSession(id, event.currentTarget, 'pointer');
|
|
134
|
+
},
|
|
135
|
+
onPointerLeave: () => {
|
|
136
|
+
store().release(id);
|
|
137
|
+
store().closeSession(id);
|
|
138
|
+
},
|
|
139
|
+
onFocus: (event) => store().openSession(id, event.currentTarget, 'focus'),
|
|
140
|
+
onBlur: (event) => {
|
|
141
|
+
if (!event.currentTarget.matches?.(':hover'))
|
|
142
|
+
store().release(id);
|
|
143
|
+
store().closeSession(id);
|
|
144
|
+
},
|
|
145
|
+
onClick: () => store().suppress(id),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* (`customize-sidebar-modal.tsx` syncs it). A row a project does not offer (a flag is off, a permission is denied)
|
|
9
9
|
* keeps its place here and simply does not render.
|
|
10
10
|
*/
|
|
11
|
-
export declare const SIDEBAR_NAV_ITEM_IDS: readonly ["customize", "apps", "files", "rooms"];
|
|
11
|
+
export declare const SIDEBAR_NAV_ITEM_IDS: readonly ["customize", "apps", "files", "rooms", "goals"];
|
|
12
12
|
export type SidebarNavItemId = (typeof SIDEBAR_NAV_ITEM_IDS)[number];
|
|
13
13
|
/**
|
|
14
14
|
* `never` moves the row into the More menu (`project-nav-items.tsx`), so its
|
|
@@ -13,7 +13,7 @@ import { registerPersistedStore, resetPersistedStore } from './persisted-store-r
|
|
|
13
13
|
*/
|
|
14
14
|
// Review (2026-10-17) and Reminders (2026-10-19) left the sidebar for Customize
|
|
15
15
|
// tabs. A saved layout that still names one drops the id in `normalizeSidebarNav`.
|
|
16
|
-
export const SIDEBAR_NAV_ITEM_IDS = ['customize', 'apps', 'files', 'rooms'];
|
|
16
|
+
export const SIDEBAR_NAV_ITEM_IDS = ['customize', 'apps', 'files', 'rooms', 'goals'];
|
|
17
17
|
/** The layout before anyone customizes it. */
|
|
18
18
|
export const SIDEBAR_NAV_DEFAULTS = [
|
|
19
19
|
{ id: 'customize', visibility: 'always' },
|
|
@@ -22,6 +22,8 @@ export const SIDEBAR_NAV_DEFAULTS = [
|
|
|
22
22
|
// Rooms (2026-10-19, flag `agent_rooms`): chat rooms for sessions and people.
|
|
23
23
|
// In the More menu until the person pins it (2026-10-20).
|
|
24
24
|
{ id: 'rooms', visibility: 'never' },
|
|
25
|
+
// Goals (2026-10-21, flag `goals`): objectives agents own until verified done.
|
|
26
|
+
{ id: 'goals', visibility: 'always' },
|
|
25
27
|
];
|
|
26
28
|
function isItemId(value) {
|
|
27
29
|
return SIDEBAR_NAV_ITEM_IDS.includes(value);
|
package/dist/lib/routes.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface UiRoutes {
|
|
|
22
22
|
apps: (projectId: string) => string | null;
|
|
23
23
|
/** The project's chat rooms (flag `agent_rooms`). */
|
|
24
24
|
rooms: (projectId: string) => string | null;
|
|
25
|
+
/** The project's goals (flag `goals`); a goal is `<goals>/<scope>/<slug>`. */
|
|
26
|
+
goals: (projectId: string) => string | null;
|
|
25
27
|
members: (projectId: string) => string | null;
|
|
26
28
|
/** The project settings page, on its sandbox section. */
|
|
27
29
|
sandboxSettings: (projectId: string) => string | null;
|
package/dist/lib/routes.js
CHANGED
|
@@ -6,6 +6,7 @@ const DEFAULT_ROUTES = {
|
|
|
6
6
|
drive: (p) => `/projects/${p}/drive`,
|
|
7
7
|
apps: (p) => `/projects/${p}/apps`,
|
|
8
8
|
rooms: (p) => `/projects/${p}/rooms`,
|
|
9
|
+
goals: (p) => `/projects/${p}/goals`,
|
|
9
10
|
members: (p) => `/projects/${p}/members`,
|
|
10
11
|
sandboxSettings: (p) => `/projects/${p}/config?section=sandbox`,
|
|
11
12
|
customize: (p, path) => `/projects/${p}/customize${path ? `/${path}` : ''}`,
|
|
@@ -12,6 +12,7 @@ import { useSessionBrowserStore } from '../../internal/stores/session-browser-st
|
|
|
12
12
|
import { useUiLink } from '../../providers/ui-provider.js';
|
|
13
13
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
14
14
|
import { useInsideLink } from './inside-link-context.js';
|
|
15
|
+
import { outputPathForName, useOutputPaths } from './output-paths-context.js';
|
|
15
16
|
import { childrenToText } from './children-text.js';
|
|
16
17
|
// Server-safe by design — see `inline-chip.tsx`. Re-exported so the client
|
|
17
18
|
// consumers that already import them from here keep working.
|
|
@@ -114,6 +115,7 @@ export function ClickableInlineCode({ children }) {
|
|
|
114
115
|
const { proxyUrl } = useSandboxProxy();
|
|
115
116
|
const openPreview = useOpenPreviewLink();
|
|
116
117
|
const insideLink = useInsideLink();
|
|
118
|
+
const outputPaths = useOutputPaths();
|
|
117
119
|
const text = childrenToText(children).trim();
|
|
118
120
|
// Before the URL and path checks: a hex is neither, and the test is a single
|
|
119
121
|
// anchored regex against a string that is at most nine characters.
|
|
@@ -146,5 +148,11 @@ export function ClickableInlineCode({ children }) {
|
|
|
146
148
|
if (looksLikeFilePath(text)) {
|
|
147
149
|
return _jsx(FilePathCode, { text: text, children: children });
|
|
148
150
|
}
|
|
151
|
+
// A bare filename (`claims.md`) is no path, but it may name one of this
|
|
152
|
+
// session's outputs: open that file, still showing the name.
|
|
153
|
+
const outputPath = outputPathForName(text, outputPaths);
|
|
154
|
+
if (outputPath) {
|
|
155
|
+
return _jsx(FilePathCode, { text: outputPath, children: children });
|
|
156
|
+
}
|
|
149
157
|
return _jsx("code", { className: INLINE_CODE, children: children });
|
|
150
158
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The paths of the files this session produced: its Outputs.
|
|
3
|
+
*
|
|
4
|
+
* A reply names its deliverables by bare filename (`claims.md`), and a bare
|
|
5
|
+
* name is not a path, so it never linked. Opening it took Expand pane →
|
|
6
|
+
* Outputs → the file (KRTX-3023). Inline code that names exactly one of these
|
|
7
|
+
* outputs now opens it.
|
|
8
|
+
*
|
|
9
|
+
* Provider: `SessionPanelProvider`. Empty outside a session: nothing extra links.
|
|
10
|
+
*/
|
|
11
|
+
export declare const OutputPathsContext: import("react").Context<readonly string[]>;
|
|
12
|
+
export declare const useOutputPaths: () => readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* The output a bare filename names, or null: it is not a filename, no output
|
|
15
|
+
* has that name, or two different outputs share it and the reply does not say
|
|
16
|
+
* which.
|
|
17
|
+
*/
|
|
18
|
+
export declare function outputPathForName(text: string, paths: readonly string[]): string | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* The paths of the files this session produced: its Outputs.
|
|
5
|
+
*
|
|
6
|
+
* A reply names its deliverables by bare filename (`claims.md`), and a bare
|
|
7
|
+
* name is not a path, so it never linked. Opening it took Expand pane →
|
|
8
|
+
* Outputs → the file (KRTX-3023). Inline code that names exactly one of these
|
|
9
|
+
* outputs now opens it.
|
|
10
|
+
*
|
|
11
|
+
* Provider: `SessionPanelProvider`. Empty outside a session: nothing extra links.
|
|
12
|
+
*/
|
|
13
|
+
export const OutputPathsContext = createContext([]);
|
|
14
|
+
export const useOutputPaths = () => useContext(OutputPathsContext);
|
|
15
|
+
const BARE_FILENAME = /^[\w@][\w@.-]*\.[A-Za-z0-9]+$/;
|
|
16
|
+
/** `/workspace/a/b.md` and `a/b.md` are one file. */
|
|
17
|
+
const sameFileKey = (path) => path.replace(/^\/workspace\//, '');
|
|
18
|
+
/**
|
|
19
|
+
* The output a bare filename names, or null: it is not a filename, no output
|
|
20
|
+
* has that name, or two different outputs share it and the reply does not say
|
|
21
|
+
* which.
|
|
22
|
+
*/
|
|
23
|
+
export function outputPathForName(text, paths) {
|
|
24
|
+
if (!BARE_FILENAME.test(text))
|
|
25
|
+
return null;
|
|
26
|
+
let found = null;
|
|
27
|
+
for (const path of paths) {
|
|
28
|
+
if (path.slice(path.lastIndexOf('/') + 1) !== text)
|
|
29
|
+
continue;
|
|
30
|
+
if (found !== null && sameFileKey(found) !== sameFileKey(path))
|
|
31
|
+
return null;
|
|
32
|
+
found ??= path;
|
|
33
|
+
}
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
@@ -25,7 +25,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
25
25
|
*/
|
|
26
26
|
// eslint-disable-next-line no-restricted-imports -- screen-reader text for the figure; this module is reached only through lazy()
|
|
27
27
|
import { genuiA11yText } from '@kortix/sdk/genui';
|
|
28
|
-
import { useState } from 'react';
|
|
28
|
+
import { useMemo, useState } from 'react';
|
|
29
29
|
import { Bar, BarChart, CartesianGrid, Cell, Line, LineChart, Pie, PieChart, XAxis, YAxis } from 'recharts';
|
|
30
30
|
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '../../../primitives/chart.js';
|
|
31
31
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '../../../primitives/dialog.js';
|
|
@@ -95,7 +95,7 @@ function PieFigure({ variant, slices, total, unit, format, tooltip, focus, }) {
|
|
|
95
95
|
const restTotal = rest.reduce((sum, slice) => sum + slice.value, 0);
|
|
96
96
|
const block = (slice, i) => {
|
|
97
97
|
const small = i > 0 && slice.value / restTotal < 0.12;
|
|
98
|
-
return (_jsxs(HoverCard, { openDelay: 80, closeDelay: 40, children: [_jsx(HoverCardTrigger, { asChild: true, children: _jsx("
|
|
98
|
+
return (_jsxs(HoverCard, { openDelay: 80, closeDelay: 40, children: [_jsx(HoverCardTrigger, { asChild: true, children: _jsx("button", { type: "button", onMouseEnter: () => focus.hover(slice.key), onMouseLeave: () => focus.hover(null), onClick: () => focus.toggle(slice.key), "aria-label": `${slice.label}: ${format(slice.value)} (${slice.percent}%)`, className: cn('focus-visible:ring-ring duration-fast flex min-h-1.5 min-w-0 flex-col justify-between overflow-hidden rounded-md text-left transition-opacity outline-none focus-visible:ring-2', small ? 'p-0' : 'p-3', i < 2 ? 'text-background' : 'text-foreground'), style: { backgroundColor: PIE_RAMP[i], flexGrow: slice.value, flexBasis: 0, opacity: dimmed(focus, slice.key) }, children: small ? null : (_jsxs(_Fragment, { children: [_jsx("span", { className: "truncate text-xs", children: slice.label }), _jsxs("span", { className: cn('truncate tabular-nums', i === 0 ? 'text-2xl font-medium' : 'text-sm'), children: [slice.percent, "%"] })] })) }) }), _jsxs(HoverCardContent, { side: "top", className: "flex w-auto min-w-40 flex-col gap-1 p-3", children: [_jsxs("span", { className: "text-foreground flex items-center gap-2 text-sm font-medium", children: [dot(slice.fill), slice.label] }), _jsxs("span", { className: "text-muted-foreground font-mono text-xs tabular-nums", children: [unit ? `${format(slice.value)} ${unit}` : format(slice.value), " \u00B7 ", slice.percent, "%"] })] })] }, slice.key));
|
|
99
99
|
};
|
|
100
100
|
return (_jsxs("div", { className: "flex min-h-0 flex-1 gap-1", children: [block(first, 0), rest.length > 0 ? (_jsx("div", { className: "flex min-w-0 flex-col gap-1", style: { flexGrow: restTotal, flexBasis: 0 }, children: rest.map((slice, i) => block(slice, i + 1)) })) : null] }));
|
|
101
101
|
}
|
|
@@ -118,11 +118,11 @@ export function ChartView({ node, props }) {
|
|
|
118
118
|
const t = useTranslations('genui');
|
|
119
119
|
const focus = useFocus();
|
|
120
120
|
const locale = useLocale();
|
|
121
|
+
const number = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
122
|
+
const compact = useMemo(() => new Intl.NumberFormat(locale, { notation: 'compact' }), [locale]);
|
|
121
123
|
// A streamed pie arrives before its slices resolve (the parent line comes first). Hold its space until one lands.
|
|
122
124
|
if (node.type === 'PieChart' && kids(props.slices).length === 0)
|
|
123
125
|
return GenuiPending(node);
|
|
124
|
-
const number = new Intl.NumberFormat(locale);
|
|
125
|
-
const compact = new Intl.NumberFormat(locale, { notation: 'compact' });
|
|
126
126
|
const format = (value) => (typeof value === 'number' ? number.format(value) : value == null ? '—' : String(value));
|
|
127
127
|
// The tooltip row of `ChartTooltipContent`, with the table's number format.
|
|
128
128
|
const tooltip = (_jsx(ChartTooltipContent, { hideLabel: node.type === 'PieChart', formatter: (value, name, item) => (_jsxs(_Fragment, { children: [_jsx("span", { className: "size-2.5 shrink-0 rounded-xs", style: { backgroundColor: item.payload?.fill ?? item.color } }), _jsxs("span", { className: "flex flex-1 items-center justify-between gap-2 leading-none", children: [_jsx("span", { className: "text-muted-foreground", children: name }), _jsx("span", { className: "text-foreground font-mono font-medium tabular-nums", children: format(value) })] })] })) }));
|
|
@@ -160,7 +160,8 @@ export function ChartView({ node, props }) {
|
|
|
160
160
|
/** The chart's values as text: the Show data dialog's table. A missing value stays a dash, never a zero. */
|
|
161
161
|
export function ChartDataTable({ node }) {
|
|
162
162
|
const t = useTranslations('genui');
|
|
163
|
-
const
|
|
163
|
+
const locale = useLocale();
|
|
164
|
+
const number = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
164
165
|
const format = (value) => (typeof value === 'number' ? number.format(value) : value == null ? '—' : String(value));
|
|
165
166
|
const { props } = node;
|
|
166
167
|
let head;
|
package/dist/primitives/map.js
CHANGED
|
@@ -163,7 +163,8 @@ function MapMarker({ longitude, latitude, label, children, ...markerOptions }) {
|
|
|
163
163
|
marker.setLngLat([longitude, latitude]);
|
|
164
164
|
}
|
|
165
165
|
}, [marker, longitude, latitude]);
|
|
166
|
-
|
|
166
|
+
const markerContextValue = useMemo(() => ({ marker, map }), [marker, map]);
|
|
167
|
+
return _jsx(MarkerContext.Provider, { value: markerContextValue, children: children });
|
|
167
168
|
}
|
|
168
169
|
function MarkerContent({ children, className }) {
|
|
169
170
|
const { marker } = useMarkerContext();
|
|
@@ -22,10 +22,12 @@ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
|
22
22
|
const PreviewImageContent = React.forwardRef(({ className, fileContent, fileName, ...props }, ref) => {
|
|
23
23
|
const [zoomLevel, setZoomLevel] = React.useState(DEFAULT_ZOOM);
|
|
24
24
|
const [transformOrigin, setTransformOrigin] = React.useState(DEFAULT_ORIGIN);
|
|
25
|
-
React.
|
|
25
|
+
const [zoomedContent, setZoomedContent] = React.useState(fileContent);
|
|
26
|
+
if (fileContent !== zoomedContent) {
|
|
27
|
+
setZoomedContent(fileContent);
|
|
26
28
|
setZoomLevel(DEFAULT_ZOOM);
|
|
27
29
|
setTransformOrigin(DEFAULT_ORIGIN);
|
|
28
|
-
}
|
|
30
|
+
}
|
|
29
31
|
const getTransformOrigin = (event) => {
|
|
30
32
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
31
33
|
if (!rect.width || !rect.height) {
|
|
@@ -7,7 +7,7 @@ import type { ReactNode } from 'react';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function SettingsSectionHeader({ title, description, action, className, }: {
|
|
9
9
|
title: string;
|
|
10
|
-
description?:
|
|
10
|
+
description?: ReactNode;
|
|
11
11
|
action?: ReactNode;
|
|
12
12
|
className?: string;
|
|
13
13
|
}): import("react").JSX.Element;
|
|
@@ -59,7 +59,7 @@ export const useAutosizeTextArea = ({ textAreaRef, triggerAutoSize, maxHeight =
|
|
|
59
59
|
clearTimeout(resizeTimeoutRef.current);
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
}, [textAreaRef
|
|
62
|
+
}, [textAreaRef, triggerAutoSize, minHeight, maxHeight, currentHeight, init]);
|
|
63
63
|
};
|
|
64
64
|
export const Textarea = React.forwardRef(({ maxHeight = Number.MAX_SAFE_INTEGER, minHeight = 52, className, onChange, value, variant: _variant, ...props }, ref) => {
|
|
65
65
|
const textAreaRef = React.useRef(null);
|
|
@@ -19,25 +19,23 @@ import { shouldTypeOnChange, TYPE_SPEED_MS } from './typed-title-logic.js';
|
|
|
19
19
|
import { TypingAnimation } from './typing-animation.js';
|
|
20
20
|
import { cn } from '../lib/utils.js';
|
|
21
21
|
import { useReducedMotion } from 'motion/react';
|
|
22
|
-
import { useCallback,
|
|
22
|
+
import { useCallback, useState } from 'react';
|
|
23
23
|
export { TYPE_SPEED_MS };
|
|
24
24
|
export function useTypeOnChange(text) {
|
|
25
|
-
// The last value this row DISPLAYED
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
const previousRef = useRef(null);
|
|
25
|
+
// The last value this row DISPLAYED. It updates in the same render pass that
|
|
26
|
+
// decides to animate, or a second change arriving mid-type compares against
|
|
27
|
+
// a stale value and is missed.
|
|
28
|
+
const [previous, setPrevious] = useState(null);
|
|
30
29
|
const [isTyping, setIsTyping] = useState(false);
|
|
31
30
|
const [runId, setRunId] = useState(0);
|
|
32
31
|
const reduceMotion = !!useReducedMotion();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}, [text, reduceMotion]);
|
|
32
|
+
if (previous !== text) {
|
|
33
|
+
setPrevious(text);
|
|
34
|
+
if (shouldTypeOnChange({ previous, next: text, reduceMotion })) {
|
|
35
|
+
setIsTyping(true);
|
|
36
|
+
setRunId((id) => id + 1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
41
39
|
const stop = useCallback(() => setIsTyping(false), []);
|
|
42
40
|
return { isTyping, runId, stop };
|
|
43
41
|
}
|
|
@@ -34,12 +34,14 @@ export function TypingAnimation({ children, words, className, duration = 100, ty
|
|
|
34
34
|
const deletingSpeed = deleteSpeed ?? typingSpeed / 2;
|
|
35
35
|
const shouldStart = startOnView ? isInView : true;
|
|
36
36
|
const animationSourceKey = useMemo(() => (words ? words.join('\u0000') : (children ?? '')), [words, children]);
|
|
37
|
-
|
|
37
|
+
const [seenSourceKey, setSeenSourceKey] = useState(animationSourceKey);
|
|
38
|
+
if (animationSourceKey !== seenSourceKey) {
|
|
39
|
+
setSeenSourceKey(animationSourceKey);
|
|
38
40
|
setDisplayedText('');
|
|
39
41
|
setCurrentWordIndex(0);
|
|
40
42
|
setCurrentCharIndex(0);
|
|
41
43
|
setPhase('typing');
|
|
42
|
-
}
|
|
44
|
+
}
|
|
43
45
|
useEffect(() => {
|
|
44
46
|
let timeout = null;
|
|
45
47
|
if (shouldStart && wordsToAnimate.length > 0) {
|
|
@@ -45,7 +45,12 @@ export function AgentSelector({ agents, selectedAgent, onSelect, disabled = fals
|
|
|
45
45
|
const [search, setSearch] = useState('');
|
|
46
46
|
const { offeredAgents, agentLabels } = useUiHostConfig();
|
|
47
47
|
// A host that lists its agents (`offeredAgents`) offers only those; it names them with `agentLabels`.
|
|
48
|
-
const offered = useMemo(() =>
|
|
48
|
+
const offered = useMemo(() => {
|
|
49
|
+
if (!offeredAgents)
|
|
50
|
+
return agents;
|
|
51
|
+
const offeredNames = new Set(offeredAgents);
|
|
52
|
+
return agents.filter((a) => offeredNames.has(a.name));
|
|
53
|
+
}, [agents, offeredAgents]);
|
|
49
54
|
const primaryAgents = useMemo(() => composerSelectableAgents(offered), [offered]);
|
|
50
55
|
const label = (name) => agentLabels?.[name] ?? (isMetaAgentName(name) ? META_AGENT_DISPLAY_NAME : capitalizeWords(name));
|
|
51
56
|
// A `flash` state, a `prevAgentRef` feeding it, two effects and a 400ms
|
|
@@ -51,8 +51,6 @@ export declare function splitFilenameForTile(name: string): {
|
|
|
51
51
|
head: string;
|
|
52
52
|
tail: string;
|
|
53
53
|
} | null;
|
|
54
|
-
/** The one-string form, for places that cannot render two lines (tooltips, tests). */
|
|
55
|
-
export declare function middleTruncateFilename(name: string, max?: number): string;
|
|
56
54
|
/** A tile previews the head of the paste only: the clamp shows four lines, and the DOM need not hold 100 kB. */
|
|
57
55
|
export declare const PASTE_PREVIEW_CHARS = 400;
|
|
58
56
|
export interface AttachmentTileProps {
|
|
@@ -58,14 +58,6 @@ export function splitFilenameForTile(name) {
|
|
|
58
58
|
return null;
|
|
59
59
|
return { head: name.slice(0, -NAME_TAIL_CHARS), tail: name.slice(-NAME_TAIL_CHARS) };
|
|
60
60
|
}
|
|
61
|
-
/** The one-string form, for places that cannot render two lines (tooltips, tests). */
|
|
62
|
-
export function middleTruncateFilename(name, max = 22) {
|
|
63
|
-
if (name.length <= max)
|
|
64
|
-
return name;
|
|
65
|
-
const tail = Math.min(NAME_TAIL_CHARS, Math.floor((max - 1) / 2));
|
|
66
|
-
const head = max - 1 - tail;
|
|
67
|
-
return `${name.slice(0, head)}…${name.slice(-tail)}`;
|
|
68
|
-
}
|
|
69
61
|
/** A tile previews the head of the paste only: the clamp shows four lines, and the DOM need not hold 100 kB. */
|
|
70
62
|
export const PASTE_PREVIEW_CHARS = 400;
|
|
71
63
|
/**
|
|
@@ -2,7 +2,7 @@ export interface ClipboardItemLike {
|
|
|
2
2
|
readonly kind: string;
|
|
3
3
|
getAsFile(): File | null;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
interface ClipboardPayload {
|
|
6
6
|
readonly files: ArrayLike<File>;
|
|
7
7
|
readonly items: ArrayLike<ClipboardItemLike>;
|
|
8
8
|
}
|
|
@@ -14,3 +14,4 @@ export interface ClipboardPayload {
|
|
|
14
14
|
* handle the text itself.
|
|
15
15
|
*/
|
|
16
16
|
export declare function extractClipboardFiles(data: ClipboardPayload | null | undefined): File[];
|
|
17
|
+
export {};
|