feat(chat): desktop prompt navigator rail (#2054)
* feat(chat): add desktop prompt navigator rail Add a ChatGPT-style right-center prompt marker rail for web/desktop chat with hover/keyboard preview panel, load-more for partial history (panel only), Chat setting, and mod+alt+p shortcut. Disabled in VS Code across rail, shortcut, settings, help, and search surfaces. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(ui): read promptNavigatorEnabled from getState in shortcut handler Match the file convention used by other shortcut handlers so the toggle does not rely on a hook-level selector closure. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(ui): drop use-no-memo and default prompt navigator off Remove the project-unprecedented React Compiler opt-out, and ship the prompt navigator as opt-in to match other recent chat UI toggles. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Serhii Dziupin
Cursor Agent
parent
502c96630e
commit
1fb448d827
@@ -16,6 +16,7 @@ import { QuestionCard } from './QuestionCard';
|
|||||||
import { StatusRowContainer } from './StatusRowContainer';
|
import { StatusRowContainer } from './StatusRowContainer';
|
||||||
import { SessionRecapNote } from '@/components/chat/SessionRecapSpacer';
|
import { SessionRecapNote } from '@/components/chat/SessionRecapSpacer';
|
||||||
import ScrollToBottomButton from './components/ScrollToBottomButton';
|
import ScrollToBottomButton from './components/ScrollToBottomButton';
|
||||||
|
import { PromptNavigatorRail } from './components/PromptNavigatorRail';
|
||||||
import { ScrollShadow } from '@/components/ui/ScrollShadow';
|
import { ScrollShadow } from '@/components/ui/ScrollShadow';
|
||||||
import { useChatAutoFollow, type AnimationHandlers, type ContentChangeReason } from '@/hooks/useChatAutoFollow';
|
import { useChatAutoFollow, type AnimationHandlers, type ContentChangeReason } from '@/hooks/useChatAutoFollow';
|
||||||
import { useChatTimelineController } from './hooks/useChatTimelineController';
|
import { useChatTimelineController } from './hooks/useChatTimelineController';
|
||||||
@@ -162,6 +163,13 @@ type ChatViewportProps = {
|
|||||||
isProgrammaticFollowActive: boolean;
|
isProgrammaticFollowActive: boolean;
|
||||||
showLoadOlderButton: boolean;
|
showLoadOlderButton: boolean;
|
||||||
onLoadOlder: () => void;
|
onLoadOlder: () => void;
|
||||||
|
turnIds: string[];
|
||||||
|
activeTurnId: string | null;
|
||||||
|
onSelectTurn: (turnId: string) => void;
|
||||||
|
showPromptNavigator: boolean;
|
||||||
|
canLoadEarlierPrompts: boolean;
|
||||||
|
isLoadingOlderPrompts: boolean;
|
||||||
|
onLoadEarlierPrompts: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ChatViewport = React.memo(({
|
const ChatViewport = React.memo(({
|
||||||
@@ -188,8 +196,40 @@ const ChatViewport = React.memo(({
|
|||||||
isProgrammaticFollowActive,
|
isProgrammaticFollowActive,
|
||||||
showLoadOlderButton,
|
showLoadOlderButton,
|
||||||
onLoadOlder,
|
onLoadOlder,
|
||||||
|
turnIds,
|
||||||
|
activeTurnId,
|
||||||
|
onSelectTurn,
|
||||||
|
showPromptNavigator,
|
||||||
|
canLoadEarlierPrompts,
|
||||||
|
isLoadingOlderPrompts,
|
||||||
|
onLoadEarlierPrompts,
|
||||||
}: ChatViewportProps) => {
|
}: ChatViewportProps) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const promptPreviewsByTurnIdRef = React.useRef<Map<string, Part[]>>(new Map());
|
||||||
|
const promptPreviewsByTurnId = React.useMemo(() => {
|
||||||
|
const next = new Map<string, Part[]>();
|
||||||
|
for (const message of renderedMessages) {
|
||||||
|
if (message.info.role !== 'user') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next.set(message.info.id, message.parts);
|
||||||
|
}
|
||||||
|
const prev = promptPreviewsByTurnIdRef.current;
|
||||||
|
if (prev.size === next.size) {
|
||||||
|
let unchanged = true;
|
||||||
|
for (const [id, parts] of next) {
|
||||||
|
if (prev.get(id) !== parts) {
|
||||||
|
unchanged = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unchanged) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
promptPreviewsByTurnIdRef.current = next;
|
||||||
|
return next;
|
||||||
|
}, [renderedMessages]);
|
||||||
const focusScrollContainer = React.useCallback((event: React.MouseEvent<HTMLElement>) => {
|
const focusScrollContainer = React.useCallback((event: React.MouseEvent<HTMLElement>) => {
|
||||||
if (event.defaultPrevented || shouldIgnoreChatNavigationTarget(event.target)) {
|
if (event.defaultPrevented || shouldIgnoreChatNavigationTarget(event.target)) {
|
||||||
return;
|
return;
|
||||||
@@ -278,6 +318,17 @@ const ChatViewport = React.memo(({
|
|||||||
</div>
|
</div>
|
||||||
</ScrollShadow>
|
</ScrollShadow>
|
||||||
<OverlayScrollbar containerRef={scrollRef} suppressVisibility={isProgrammaticFollowActive} userIntentOnly observeMutations={false} />
|
<OverlayScrollbar containerRef={scrollRef} suppressVisibility={isProgrammaticFollowActive} userIntentOnly observeMutations={false} />
|
||||||
|
{showPromptNavigator ? (
|
||||||
|
<PromptNavigatorRail
|
||||||
|
turnIds={turnIds}
|
||||||
|
previewsByTurnId={promptPreviewsByTurnId}
|
||||||
|
activeTurnId={activeTurnId}
|
||||||
|
onSelectTurn={onSelectTurn}
|
||||||
|
canLoadEarlier={canLoadEarlierPrompts}
|
||||||
|
isLoadingOlder={isLoadingOlderPrompts}
|
||||||
|
onLoadEarlier={onLoadEarlierPrompts}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -304,7 +355,14 @@ const ChatViewport = React.memo(({
|
|||||||
&& prev.sessionPermissions === next.sessionPermissions
|
&& prev.sessionPermissions === next.sessionPermissions
|
||||||
&& prev.isProgrammaticFollowActive === next.isProgrammaticFollowActive
|
&& prev.isProgrammaticFollowActive === next.isProgrammaticFollowActive
|
||||||
&& prev.showLoadOlderButton === next.showLoadOlderButton
|
&& prev.showLoadOlderButton === next.showLoadOlderButton
|
||||||
&& prev.onLoadOlder === next.onLoadOlder;
|
&& prev.onLoadOlder === next.onLoadOlder
|
||||||
|
&& prev.turnIds === next.turnIds
|
||||||
|
&& prev.activeTurnId === next.activeTurnId
|
||||||
|
&& prev.onSelectTurn === next.onSelectTurn
|
||||||
|
&& prev.showPromptNavigator === next.showPromptNavigator
|
||||||
|
&& prev.canLoadEarlierPrompts === next.canLoadEarlierPrompts
|
||||||
|
&& prev.isLoadingOlderPrompts === next.isLoadingOlderPrompts
|
||||||
|
&& prev.onLoadEarlierPrompts === next.onLoadEarlierPrompts;
|
||||||
});
|
});
|
||||||
|
|
||||||
ChatViewport.displayName = 'ChatViewport';
|
ChatViewport.displayName = 'ChatViewport';
|
||||||
@@ -405,6 +463,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
|||||||
// UI store
|
// UI store
|
||||||
const isExpandedInput = useUIStore((state) => state.isExpandedInput);
|
const isExpandedInput = useUIStore((state) => state.isExpandedInput);
|
||||||
const stickyUserHeader = useUIStore((state) => state.stickyUserHeader);
|
const stickyUserHeader = useUIStore((state) => state.stickyUserHeader);
|
||||||
|
const promptNavigatorEnabled = useUIStore((state) => state.promptNavigatorEnabled);
|
||||||
const allowPromptingSubagentSessions = useUIStore((state) => state.allowPromptingSubagentSessions);
|
const allowPromptingSubagentSessions = useUIStore((state) => state.allowPromptingSubagentSessions);
|
||||||
const isTimelineDialogOpen = useUIStore((s) => s.isTimelineDialogOpen);
|
const isTimelineDialogOpen = useUIStore((s) => s.isTimelineDialogOpen);
|
||||||
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
||||||
@@ -706,6 +765,21 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
|||||||
scrollToMessage: timelineController.scrollToMessage,
|
scrollToMessage: timelineController.scrollToMessage,
|
||||||
resumeToBottom: timelineController.resumeToBottomInstant,
|
resumeToBottom: timelineController.resumeToBottomInstant,
|
||||||
});
|
});
|
||||||
|
const handlePromptNavigatorSelect = React.useCallback((turnId: string) => {
|
||||||
|
void navigation.scrollToTurnId(turnId, { behavior: 'smooth' });
|
||||||
|
}, [navigation]);
|
||||||
|
const canLoadEarlierPrompts = timelineController.historySignals.canLoadEarlier;
|
||||||
|
const showPromptNavigator = !isMobile
|
||||||
|
&& !isVSCode
|
||||||
|
&& !isDesktopExpandedInput
|
||||||
|
&& promptNavigatorEnabled
|
||||||
|
&& timelineController.turnIds.length >= 2;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!showPromptNavigator) {
|
||||||
|
useUIStore.getState().setPromptNavigatorPanelOpen(false);
|
||||||
|
}
|
||||||
|
}, [showPromptNavigator]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (typeof window === 'undefined' || !currentSessionId) return;
|
if (typeof window === 'undefined' || !currentSessionId) return;
|
||||||
@@ -1012,6 +1086,13 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
|||||||
isProgrammaticFollowActive={isFollowingProgrammatically}
|
isProgrammaticFollowActive={isFollowingProgrammatically}
|
||||||
showLoadOlderButton={showLoadOlderButton}
|
showLoadOlderButton={showLoadOlderButton}
|
||||||
onLoadOlder={handleLoadOlderClick}
|
onLoadOlder={handleLoadOlderClick}
|
||||||
|
turnIds={timelineController.turnIds}
|
||||||
|
activeTurnId={timelineController.activeTurnId}
|
||||||
|
onSelectTurn={handlePromptNavigatorSelect}
|
||||||
|
showPromptNavigator={showPromptNavigator}
|
||||||
|
canLoadEarlierPrompts={canLoadEarlierPrompts}
|
||||||
|
isLoadingOlderPrompts={timelineController.isLoadingOlder}
|
||||||
|
onLoadEarlierPrompts={handleLoadOlderClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { useSessionUIStore } from '@/sync/session-ui-store';
|
|||||||
import { useSessionMessageRecords } from '@/sync/sync-context';
|
import { useSessionMessageRecords } from '@/sync/sync-context';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { Icon } from "@/components/icon/Icon";
|
import { Icon } from "@/components/icon/Icon";
|
||||||
import type { Part } from '@opencode-ai/sdk/v2';
|
|
||||||
import { getCurrentIntlLocale, useI18n } from '@/lib/i18n';
|
import { getCurrentIntlLocale, useI18n } from '@/lib/i18n';
|
||||||
|
import { getFullText, getMessagePreview } from './lib/messagePreview';
|
||||||
import { useDeviceInfo } from '@/lib/device';
|
import { useDeviceInfo } from '@/lib/device';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
@@ -417,19 +417,6 @@ export const TimelineDialog: React.FC<TimelineDialogProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function getFullText(parts: Part[]): string {
|
|
||||||
return parts
|
|
||||||
.filter((p): p is Part & { type: 'text'; text: string } => p.type === 'text' && typeof p.text === 'string')
|
|
||||||
.map((p) => p.text)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMessagePreview(parts: Part[]): string {
|
|
||||||
const full = getFullText(parts);
|
|
||||||
const singleLine = full.replace(/\n/g, ' ');
|
|
||||||
return singleLine.length > 80 ? singleLine.slice(0, 80) : singleLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSearchSnippet(text: string, query: string, contextChars: number = 30): string | null {
|
function getSearchSnippet(text: string, query: string, contextChars: number = 30): string | null {
|
||||||
const lowerText = text.toLowerCase();
|
const lowerText = text.toLowerCase();
|
||||||
const lowerQuery = query.toLowerCase();
|
const lowerQuery = query.toLowerCase();
|
||||||
|
|||||||
@@ -0,0 +1,389 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { Part } from '@opencode-ai/sdk/v2';
|
||||||
|
|
||||||
|
import { Icon } from '@/components/icon/Icon';
|
||||||
|
import { useDeviceInfo } from '@/lib/device';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { getMessagePreview } from '../lib/messagePreview';
|
||||||
|
|
||||||
|
type PromptEntry = {
|
||||||
|
turnId: string;
|
||||||
|
preview: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PromptNavigatorRailProps = {
|
||||||
|
turnIds: string[];
|
||||||
|
previewsByTurnId: Map<string, Part[]>;
|
||||||
|
activeTurnId: string | null;
|
||||||
|
onSelectTurn: (turnId: string) => void;
|
||||||
|
canLoadEarlier: boolean;
|
||||||
|
isLoadingOlder: boolean;
|
||||||
|
onLoadEarlier: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LINE_HIT_HEIGHT_PX = 8;
|
||||||
|
const HOVER_CLOSE_DELAY_MS = 120;
|
||||||
|
const COMPACT_BACKDROP_MAX_WIDTH_PX = 1280;
|
||||||
|
|
||||||
|
const buildPromptEntries = (
|
||||||
|
turnIds: string[],
|
||||||
|
previewsByTurnId: Map<string, Part[]>,
|
||||||
|
): PromptEntry[] => {
|
||||||
|
return turnIds.map((turnId) => {
|
||||||
|
const parts = previewsByTurnId.get(turnId) ?? [];
|
||||||
|
return {
|
||||||
|
turnId,
|
||||||
|
preview: getMessagePreview(parts, 120),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveLineGapClass = (count: number): string => {
|
||||||
|
if (count > 24) {
|
||||||
|
return 'gap-px';
|
||||||
|
}
|
||||||
|
if (count > 12) {
|
||||||
|
return 'gap-0.5';
|
||||||
|
}
|
||||||
|
return 'gap-1';
|
||||||
|
};
|
||||||
|
|
||||||
|
type LineRailProps = {
|
||||||
|
prompts: PromptEntry[];
|
||||||
|
activeTurnId: string | null;
|
||||||
|
lineGapClass: string;
|
||||||
|
needsBackdrop: boolean;
|
||||||
|
emptyPreviewLabel: string;
|
||||||
|
onSelectTurn: (turnId: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Compact marker stack only — never renders load-more. Markers stay out of tab order. */
|
||||||
|
function LineRail({
|
||||||
|
prompts,
|
||||||
|
activeTurnId,
|
||||||
|
lineGapClass,
|
||||||
|
needsBackdrop,
|
||||||
|
emptyPreviewLabel,
|
||||||
|
onSelectTurn,
|
||||||
|
}: LineRailProps) {
|
||||||
|
const activeButtonRef = React.useRef<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
activeButtonRef.current?.scrollIntoView({ block: 'nearest' });
|
||||||
|
}, [activeTurnId, prompts.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex flex-col items-center rounded-full px-1 py-1.5',
|
||||||
|
needsBackdrop
|
||||||
|
? 'border border-[var(--interactive-border)]/40 bg-[var(--surface-background)]/90 shadow-sm backdrop-blur-sm'
|
||||||
|
: 'bg-transparent',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex max-h-[40vh] min-h-0 flex-col items-center overflow-y-auto overflow-x-hidden',
|
||||||
|
lineGapClass,
|
||||||
|
'[scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{prompts.map((prompt) => {
|
||||||
|
const isActive = prompt.turnId === activeTurnId;
|
||||||
|
const preview = prompt.preview.trim() || emptyPreviewLabel;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={prompt.turnId}
|
||||||
|
ref={isActive ? activeButtonRef : undefined}
|
||||||
|
type="button"
|
||||||
|
tabIndex={-1}
|
||||||
|
className={cn(
|
||||||
|
'flex shrink-0 items-center justify-center rounded-full',
|
||||||
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--interactive-focusRing)]',
|
||||||
|
)}
|
||||||
|
style={{
|
||||||
|
width: '16px',
|
||||||
|
height: `${LINE_HIT_HEIGHT_PX}px`,
|
||||||
|
}}
|
||||||
|
aria-label={preview}
|
||||||
|
aria-current={isActive ? 'true' : undefined}
|
||||||
|
onClick={() => {
|
||||||
|
onSelectTurn(prompt.turnId);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn(
|
||||||
|
'block h-0.5 rounded-full transition-colors',
|
||||||
|
isActive
|
||||||
|
? 'w-3.5 bg-[var(--surface-foreground)]'
|
||||||
|
: 'w-3 bg-[var(--surface-foreground)]/40',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type PromptMenuPanelProps = {
|
||||||
|
prompts: PromptEntry[];
|
||||||
|
activeTurnId: string | null;
|
||||||
|
canLoadEarlier: boolean;
|
||||||
|
isLoadingOlder: boolean;
|
||||||
|
emptyPreviewLabel: string;
|
||||||
|
currentPromptLabel: string;
|
||||||
|
loadMoreLabel: string;
|
||||||
|
onSelectTurn: (turnId: string) => void;
|
||||||
|
onLoadEarlier: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
|
onMouseEnter: () => void;
|
||||||
|
onMouseLeave: () => void;
|
||||||
|
focusOnMount: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Hover/keyboard menu — the only place load-more is allowed. */
|
||||||
|
function PromptMenuPanel({
|
||||||
|
prompts,
|
||||||
|
activeTurnId,
|
||||||
|
canLoadEarlier,
|
||||||
|
isLoadingOlder,
|
||||||
|
emptyPreviewLabel,
|
||||||
|
currentPromptLabel,
|
||||||
|
loadMoreLabel,
|
||||||
|
onSelectTurn,
|
||||||
|
onLoadEarlier,
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseLeave,
|
||||||
|
focusOnMount,
|
||||||
|
}: PromptMenuPanelProps) {
|
||||||
|
const activeItemRef = React.useRef<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!focusOnMount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activeItemRef.current?.focus();
|
||||||
|
}, [focusOnMount]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'absolute right-full top-1/2 z-30 mr-3 w-[min(18rem,calc(100vw-5rem))] -translate-y-1/2',
|
||||||
|
'rounded-xl border border-[var(--interactive-border)]/60 bg-[var(--surface-elevated)] p-1 shadow-md',
|
||||||
|
)}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
>
|
||||||
|
<ul className="max-h-[min(24rem,70vh)] overflow-y-auto">
|
||||||
|
{canLoadEarlier ? (
|
||||||
|
<li className="border-b border-[var(--interactive-border)]/40 px-1 pb-1">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
'flex w-full items-center justify-center gap-1.5 rounded-lg px-2.5 py-2',
|
||||||
|
'typography-meta text-[var(--surface-mutedForeground)] transition-colors',
|
||||||
|
'hover:bg-[var(--interactive-hover)]/60 hover:text-[var(--surface-foreground)]',
|
||||||
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--interactive-focusRing)]',
|
||||||
|
isLoadingOlder ? 'cursor-wait opacity-70' : undefined,
|
||||||
|
)}
|
||||||
|
disabled={isLoadingOlder}
|
||||||
|
onClick={onLoadEarlier}
|
||||||
|
>
|
||||||
|
{isLoadingOlder ? (
|
||||||
|
<Icon name="loader-4" className="size-3.5 shrink-0 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Icon name="arrow-up-s" className="size-3.5 shrink-0" />
|
||||||
|
)}
|
||||||
|
<span>{loadMoreLabel}</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
{prompts.map((prompt) => {
|
||||||
|
const isActive = prompt.turnId === activeTurnId;
|
||||||
|
const preview = prompt.preview.trim() || emptyPreviewLabel;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={prompt.turnId}>
|
||||||
|
<button
|
||||||
|
ref={isActive ? activeItemRef : undefined}
|
||||||
|
type="button"
|
||||||
|
className={cn(
|
||||||
|
'flex w-full items-start rounded-lg px-2.5 py-2 text-left transition-colors',
|
||||||
|
'hover:bg-[var(--interactive-hover)]/60',
|
||||||
|
isActive
|
||||||
|
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)]'
|
||||||
|
: 'text-[var(--surface-foreground)]',
|
||||||
|
)}
|
||||||
|
aria-current={isActive ? 'true' : undefined}
|
||||||
|
onClick={() => {
|
||||||
|
onSelectTurn(prompt.turnId);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="min-w-0 flex-1">
|
||||||
|
<span className="typography-meta line-clamp-2">{preview}</span>
|
||||||
|
{isActive ? (
|
||||||
|
<span className="mt-0.5 block typography-micro text-[var(--interactive-selection-foreground)]/80">
|
||||||
|
{currentPromptLabel}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PromptNavigatorRail({
|
||||||
|
turnIds,
|
||||||
|
previewsByTurnId,
|
||||||
|
activeTurnId,
|
||||||
|
onSelectTurn,
|
||||||
|
canLoadEarlier,
|
||||||
|
isLoadingOlder,
|
||||||
|
onLoadEarlier,
|
||||||
|
}: PromptNavigatorRailProps) {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { screenWidth } = useDeviceInfo();
|
||||||
|
const isPanelOpen = useUIStore((state) => state.isPromptNavigatorPanelOpen);
|
||||||
|
const setPromptNavigatorPanelOpen = useUIStore((state) => state.setPromptNavigatorPanelOpen);
|
||||||
|
const closeTimeoutRef = React.useRef<number | null>(null);
|
||||||
|
const rootRef = React.useRef<HTMLElement | null>(null);
|
||||||
|
const openedByPointerRef = React.useRef(false);
|
||||||
|
const [focusActiveOnOpen, setFocusActiveOnOpen] = React.useState(false);
|
||||||
|
|
||||||
|
const prompts = React.useMemo(
|
||||||
|
() => buildPromptEntries(turnIds, previewsByTurnId),
|
||||||
|
[previewsByTurnId, turnIds],
|
||||||
|
);
|
||||||
|
|
||||||
|
const needsBackdrop = screenWidth < COMPACT_BACKDROP_MAX_WIDTH_PX;
|
||||||
|
const lineGapClass = resolveLineGapClass(prompts.length);
|
||||||
|
const emptyPreviewLabel = t('chat.timeline.noTextContent');
|
||||||
|
const currentPromptLabel = t('chat.promptNavigator.currentPrompt');
|
||||||
|
const loadMoreLabel = t('chat.promptNavigator.loadMore');
|
||||||
|
|
||||||
|
const clearCloseTimeout = React.useCallback(() => {
|
||||||
|
if (closeTimeoutRef.current !== null) {
|
||||||
|
window.clearTimeout(closeTimeoutRef.current);
|
||||||
|
closeTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const openPanel = React.useCallback(() => {
|
||||||
|
clearCloseTimeout();
|
||||||
|
openedByPointerRef.current = true;
|
||||||
|
setFocusActiveOnOpen(false);
|
||||||
|
setPromptNavigatorPanelOpen(true);
|
||||||
|
}, [clearCloseTimeout, setPromptNavigatorPanelOpen]);
|
||||||
|
|
||||||
|
const scheduleClosePanel = React.useCallback(() => {
|
||||||
|
clearCloseTimeout();
|
||||||
|
closeTimeoutRef.current = window.setTimeout(() => {
|
||||||
|
// Keep keyboard-opened panel alive while focus is still inside the rail.
|
||||||
|
if (rootRef.current?.contains(document.activeElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openedByPointerRef.current = false;
|
||||||
|
setFocusActiveOnOpen(false);
|
||||||
|
setPromptNavigatorPanelOpen(false);
|
||||||
|
}, HOVER_CLOSE_DELAY_MS);
|
||||||
|
}, [clearCloseTimeout, setPromptNavigatorPanelOpen]);
|
||||||
|
|
||||||
|
React.useEffect(() => () => {
|
||||||
|
clearCloseTimeout();
|
||||||
|
setPromptNavigatorPanelOpen(false);
|
||||||
|
}, [clearCloseTimeout, setPromptNavigatorPanelOpen]);
|
||||||
|
|
||||||
|
// Keyboard shortcut flips the store open with focus outside the rail.
|
||||||
|
// Pointer open sets openedByPointerRef so we don't steal focus on hover.
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isPanelOpen) {
|
||||||
|
setFocusActiveOnOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (openedByPointerRef.current) {
|
||||||
|
openedByPointerRef.current = false;
|
||||||
|
setFocusActiveOnOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFocusActiveOnOpen(true);
|
||||||
|
}, [isPanelOpen]);
|
||||||
|
|
||||||
|
const handleSelectPrompt = React.useCallback((turnId: string) => {
|
||||||
|
onSelectTurn(turnId);
|
||||||
|
openedByPointerRef.current = false;
|
||||||
|
setFocusActiveOnOpen(false);
|
||||||
|
setPromptNavigatorPanelOpen(false);
|
||||||
|
}, [onSelectTurn, setPromptNavigatorPanelOpen]);
|
||||||
|
|
||||||
|
const handleLoadEarlier = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (isLoadingOlder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onLoadEarlier();
|
||||||
|
}, [isLoadingOlder, onLoadEarlier]);
|
||||||
|
|
||||||
|
const handleWrapperBlur = React.useCallback((event: React.FocusEvent<HTMLDivElement>) => {
|
||||||
|
const next = event.relatedTarget;
|
||||||
|
if (next instanceof Node && event.currentTarget.contains(next)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleClosePanel();
|
||||||
|
}, [scheduleClosePanel]);
|
||||||
|
|
||||||
|
if (prompts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav
|
||||||
|
ref={rootRef}
|
||||||
|
aria-label={t('chat.promptNavigator.aria')}
|
||||||
|
className="pointer-events-none absolute right-3 top-1/2 z-20 -translate-y-1/2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="pointer-events-auto relative"
|
||||||
|
onMouseEnter={openPanel}
|
||||||
|
onMouseLeave={scheduleClosePanel}
|
||||||
|
onFocus={openPanel}
|
||||||
|
onBlur={handleWrapperBlur}
|
||||||
|
>
|
||||||
|
<LineRail
|
||||||
|
prompts={prompts}
|
||||||
|
activeTurnId={activeTurnId}
|
||||||
|
lineGapClass={lineGapClass}
|
||||||
|
needsBackdrop={needsBackdrop}
|
||||||
|
emptyPreviewLabel={emptyPreviewLabel}
|
||||||
|
onSelectTurn={handleSelectPrompt}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isPanelOpen ? (
|
||||||
|
<PromptMenuPanel
|
||||||
|
prompts={prompts}
|
||||||
|
activeTurnId={activeTurnId}
|
||||||
|
canLoadEarlier={canLoadEarlier}
|
||||||
|
isLoadingOlder={isLoadingOlder}
|
||||||
|
emptyPreviewLabel={emptyPreviewLabel}
|
||||||
|
currentPromptLabel={currentPromptLabel}
|
||||||
|
loadMoreLabel={loadMoreLabel}
|
||||||
|
onSelectTurn={handleSelectPrompt}
|
||||||
|
onLoadEarlier={handleLoadEarlier}
|
||||||
|
onMouseEnter={openPanel}
|
||||||
|
onMouseLeave={scheduleClosePanel}
|
||||||
|
focusOnMount={focusActiveOnOpen}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test'
|
||||||
|
import type { Part } from '@opencode-ai/sdk/v2'
|
||||||
|
import { getFullText, getMessagePreview } from './messagePreview'
|
||||||
|
|
||||||
|
const textPart = (text: string): Part => ({ type: 'text', text } as Part)
|
||||||
|
|
||||||
|
describe('messagePreview', () => {
|
||||||
|
test('joins text parts for full text', () => {
|
||||||
|
expect(getFullText([textPart('hello'), textPart('world')])).toBe('hello\nworld')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('collapses newlines and truncates previews', () => {
|
||||||
|
expect(getMessagePreview([textPart('line one\nline two')], 80)).toBe('line one line two')
|
||||||
|
expect(getMessagePreview([textPart('abcdefghijklmnopqrstuvwxyz')], 10)).toBe('abcdefghij…')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns empty string when there is no text', () => {
|
||||||
|
expect(getMessagePreview([])).toBe('')
|
||||||
|
expect(getFullText([{ type: 'file' } as Part])).toBe('')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import type { Part } from '@opencode-ai/sdk/v2';
|
||||||
|
|
||||||
|
export function getFullText(parts: Part[]): string {
|
||||||
|
return parts
|
||||||
|
.filter((p): p is Part & { type: 'text'; text: string } => p.type === 'text' && typeof p.text === 'string')
|
||||||
|
.map((p) => p.text)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMessagePreview(parts: Part[], maxLength = 80): string {
|
||||||
|
const full = getFullText(parts);
|
||||||
|
const singleLine = full.replace(/\n/g, ' ');
|
||||||
|
return singleLine.length > maxLength ? `${singleLine.slice(0, maxLength)}…` : singleLine;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import { Icon } from "@/components/icon/Icon";
|
|||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { updateDesktopSettings } from '@/lib/persistence';
|
import { updateDesktopSettings } from '@/lib/persistence';
|
||||||
|
import { isVSCodeRuntime } from '@/lib/desktop';
|
||||||
import {
|
import {
|
||||||
formatShortcutForDisplay,
|
formatShortcutForDisplay,
|
||||||
getCustomizableShortcutActions,
|
getCustomizableShortcutActions,
|
||||||
@@ -54,7 +55,13 @@ export const KeyboardShortcutsSettings: React.FC = () => {
|
|||||||
const clearShortcutOverride = useUIStore((state) => state.clearShortcutOverride);
|
const clearShortcutOverride = useUIStore((state) => state.clearShortcutOverride);
|
||||||
const resetAllShortcutOverrides = useUIStore((state) => state.resetAllShortcutOverrides);
|
const resetAllShortcutOverrides = useUIStore((state) => state.resetAllShortcutOverrides);
|
||||||
|
|
||||||
const actions = React.useMemo(() => getCustomizableShortcutActions(), []);
|
const actions = React.useMemo(() => {
|
||||||
|
const all = getCustomizableShortcutActions();
|
||||||
|
if (!isVSCodeRuntime()) {
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
return all.filter((action) => action.id !== 'toggle_prompt_navigator');
|
||||||
|
}, []);
|
||||||
const actionLabel = React.useCallback((id: string, fallbackLabel: string): string => {
|
const actionLabel = React.useCallback((id: string, fallbackLabel: string): string => {
|
||||||
const key = `settings.openchamber.keyboardShortcuts.action.${id}.label`;
|
const key = `settings.openchamber.keyboardShortcuts.action.${id}.label`;
|
||||||
const translated = tUnsafe(key);
|
const translated = tUnsafe(key);
|
||||||
|
|||||||
@@ -145,7 +145,37 @@ const VisualSectionContent: React.FC = () => {
|
|||||||
|
|
||||||
// Chat section: User message rendering, Diff layout, Mobile status bar, Show reasoning traces, Follow-up behavior, Persist draft
|
// Chat section: User message rendering, Diff layout, Mobile status bar, Show reasoning traces, Follow-up behavior, Persist draft
|
||||||
const ChatSectionContent: React.FC = () => {
|
const ChatSectionContent: React.FC = () => {
|
||||||
return <OpenChamberVisualSettings visibleSettings={['sessionGoal', 'sessionAssist', 'chatRenderMode', 'messageTransport', 'activityRenderMode', 'userMessageRendering', 'mermaidRendering', 'reasoning', 'showToolFileIcons', 'showTurnChangedFiles', 'expandedTools', 'collapsibleUserMessages', 'stickyUserHeader', 'wideChatLayout', 'codeBlockLineWrap', 'splitAssistantMessageActions', 'subagentReadOnlyBanner', 'diffLayout', 'dotfiles', 'fileViewerPreview', 'followUpBehavior', 'persistDraft', 'inputSpellcheck']} />;
|
const isVSCode = isVSCodeRuntime();
|
||||||
|
return (
|
||||||
|
<OpenChamberVisualSettings
|
||||||
|
visibleSettings={[
|
||||||
|
'sessionGoal',
|
||||||
|
'sessionAssist',
|
||||||
|
'chatRenderMode',
|
||||||
|
'messageTransport',
|
||||||
|
'activityRenderMode',
|
||||||
|
'userMessageRendering',
|
||||||
|
'mermaidRendering',
|
||||||
|
'reasoning',
|
||||||
|
'showToolFileIcons',
|
||||||
|
'showTurnChangedFiles',
|
||||||
|
'expandedTools',
|
||||||
|
'collapsibleUserMessages',
|
||||||
|
'stickyUserHeader',
|
||||||
|
...(!isVSCode ? ['promptNavigatorEnabled' as const] : []),
|
||||||
|
'wideChatLayout',
|
||||||
|
'codeBlockLineWrap',
|
||||||
|
'splitAssistantMessageActions',
|
||||||
|
'subagentReadOnlyBanner',
|
||||||
|
'diffLayout',
|
||||||
|
'dotfiles',
|
||||||
|
'fileViewerPreview',
|
||||||
|
'followUpBehavior',
|
||||||
|
'persistDraft',
|
||||||
|
'inputSpellcheck',
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sessions section: Default model & agent, Session retention
|
// Sessions section: Default model & agent, Session retention
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ const normalizeUserMessageRenderingMode = (mode: unknown): 'markdown' | 'plain'
|
|||||||
return mode === 'markdown' ? 'markdown' : 'plain';
|
return mode === 'markdown' ? 'markdown' : 'plain';
|
||||||
};
|
};
|
||||||
|
|
||||||
type VisibleSetting = 'sessionAssist' | 'sessionGoal' | 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'editorFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'collapsibleUserMessages' | 'stickyUserHeader' | 'wideChatLayout' | 'codeBlockLineWrap' | 'splitAssistantMessageActions' | 'subagentReadOnlyBanner' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'fileViewerPreview' | 'reasoning' | 'showToolFileIcons' | 'showTurnChangedFiles' | 'expandedTools' | 'followUpBehavior' | 'terminalQuickKeys' | 'fileEditorKeymap' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage' | 'expandedEditorToolbar';
|
type VisibleSetting = 'sessionAssist' | 'sessionGoal' | 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'editorFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'collapsibleUserMessages' | 'stickyUserHeader' | 'promptNavigatorEnabled' | 'wideChatLayout' | 'codeBlockLineWrap' | 'splitAssistantMessageActions' | 'subagentReadOnlyBanner' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'fileViewerPreview' | 'reasoning' | 'showToolFileIcons' | 'showTurnChangedFiles' | 'expandedTools' | 'followUpBehavior' | 'terminalQuickKeys' | 'fileEditorKeymap' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage' | 'expandedEditorToolbar';
|
||||||
|
|
||||||
interface OpenChamberVisualSettingsProps {
|
interface OpenChamberVisualSettingsProps {
|
||||||
/** Which settings to show. If undefined, shows all. */
|
/** Which settings to show. If undefined, shows all. */
|
||||||
@@ -280,7 +280,9 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
const collapsibleUserMessages = useUIStore(state => state.collapsibleUserMessages);
|
const collapsibleUserMessages = useUIStore(state => state.collapsibleUserMessages);
|
||||||
const setCollapsibleUserMessages = useUIStore(state => state.setCollapsibleUserMessages);
|
const setCollapsibleUserMessages = useUIStore(state => state.setCollapsibleUserMessages);
|
||||||
const stickyUserHeader = useUIStore(state => state.stickyUserHeader);
|
const stickyUserHeader = useUIStore(state => state.stickyUserHeader);
|
||||||
|
const promptNavigatorEnabled = useUIStore(state => state.promptNavigatorEnabled);
|
||||||
const setStickyUserHeader = useUIStore(state => state.setStickyUserHeader);
|
const setStickyUserHeader = useUIStore(state => state.setStickyUserHeader);
|
||||||
|
const setPromptNavigatorEnabled = useUIStore(state => state.setPromptNavigatorEnabled);
|
||||||
const expandedEditorToolbar = useUIStore(state => state.expandedEditorToolbar);
|
const expandedEditorToolbar = useUIStore(state => state.expandedEditorToolbar);
|
||||||
const setExpandedEditorToolbar = useUIStore(state => state.setExpandedEditorToolbar);
|
const setExpandedEditorToolbar = useUIStore(state => state.setExpandedEditorToolbar);
|
||||||
const wideChatLayoutEnabled = useUIStore(state => state.wideChatLayoutEnabled);
|
const wideChatLayoutEnabled = useUIStore(state => state.wideChatLayoutEnabled);
|
||||||
@@ -441,6 +443,11 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
void updateDesktopSettings({ stickyUserHeader: enabled });
|
void updateDesktopSettings({ stickyUserHeader: enabled });
|
||||||
}, [setStickyUserHeader]);
|
}, [setStickyUserHeader]);
|
||||||
|
|
||||||
|
const handlePromptNavigatorEnabledChange = React.useCallback((enabled: boolean) => {
|
||||||
|
setPromptNavigatorEnabled(enabled);
|
||||||
|
void updateDesktopSettings({ promptNavigatorEnabled: enabled });
|
||||||
|
}, [setPromptNavigatorEnabled]);
|
||||||
|
|
||||||
const handleExpandedEditorToolbarChange = React.useCallback((enabled: boolean) => {
|
const handleExpandedEditorToolbarChange = React.useCallback((enabled: boolean) => {
|
||||||
setExpandedEditorToolbar(enabled);
|
setExpandedEditorToolbar(enabled);
|
||||||
void updateDesktopSettings({ expandedEditorToolbar: enabled });
|
void updateDesktopSettings({ expandedEditorToolbar: enabled });
|
||||||
@@ -572,9 +579,11 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
|| (shouldShow('activityRenderMode') && chatRenderMode === 'sorted')
|
|| (shouldShow('activityRenderMode') && chatRenderMode === 'sorted')
|
||||||
|| shouldShow('collapsibleUserMessages')
|
|| shouldShow('collapsibleUserMessages')
|
||||||
|| shouldShow('stickyUserHeader')
|
|| shouldShow('stickyUserHeader')
|
||||||
|
|| (shouldShow('promptNavigatorEnabled') && !isVSCode)
|
||||||
|| shouldShow('wideChatLayout')
|
|| shouldShow('wideChatLayout')
|
||||||
|| shouldShow('codeBlockLineWrap')
|
|| shouldShow('codeBlockLineWrap')
|
||||||
|| shouldShow('splitAssistantMessageActions')
|
|| shouldShow('splitAssistantMessageActions')
|
||||||
|
|| shouldShow('subagentReadOnlyBanner')
|
||||||
|| shouldShow('diffLayout')
|
|| shouldShow('diffLayout')
|
||||||
|| shouldShow('dotfiles')
|
|| shouldShow('dotfiles')
|
||||||
|| shouldShow('fileViewerPreview')
|
|| shouldShow('fileViewerPreview')
|
||||||
@@ -1901,7 +1910,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(shouldShow('sessionAssist') || shouldShow('collapsibleUserMessages') || shouldShow('stickyUserHeader') || shouldShow('wideChatLayout') || shouldShow('codeBlockLineWrap') || shouldShow('splitAssistantMessageActions') || shouldShow('subagentReadOnlyBanner') || shouldShow('dotfiles') || shouldShow('fileViewerPreview') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || shouldShow('showTurnChangedFiles') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && (
|
{(shouldShow('sessionAssist') || shouldShow('collapsibleUserMessages') || shouldShow('stickyUserHeader') || (shouldShow('promptNavigatorEnabled') && !isVSCode) || shouldShow('wideChatLayout') || shouldShow('codeBlockLineWrap') || shouldShow('splitAssistantMessageActions') || shouldShow('subagentReadOnlyBanner') || shouldShow('dotfiles') || shouldShow('fileViewerPreview') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || shouldShow('showTurnChangedFiles') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{(shouldShow('sessionAssist') || shouldShow('subagentReadOnlyBanner')) && (
|
{(shouldShow('sessionAssist') || shouldShow('subagentReadOnlyBanner')) && (
|
||||||
<section className="p-2 space-y-0.5">
|
<section className="p-2 space-y-0.5">
|
||||||
@@ -2029,7 +2038,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(shouldShow('collapsibleUserMessages') || shouldShow('stickyUserHeader') || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || shouldShow('codeBlockLineWrap')) && (
|
{(shouldShow('collapsibleUserMessages') || shouldShow('stickyUserHeader') || (shouldShow('promptNavigatorEnabled') && !isVSCode) || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || shouldShow('codeBlockLineWrap')) && (
|
||||||
<section className="p-2 space-y-0.5">
|
<section className="p-2 space-y-0.5">
|
||||||
<h3 data-settings-item="chat.message-appearance" className="typography-ui-header font-medium text-foreground py-1.5">{t('settings.openchamber.visual.section.messageAppearance')}</h3>
|
<h3 data-settings-item="chat.message-appearance" className="typography-ui-header font-medium text-foreground py-1.5">{t('settings.openchamber.visual.section.messageAppearance')}</h3>
|
||||||
{shouldShow('collapsibleUserMessages') && (
|
{shouldShow('collapsibleUserMessages') && (
|
||||||
@@ -2080,6 +2089,30 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{shouldShow('promptNavigatorEnabled') && !isVSCode && (
|
||||||
|
<div
|
||||||
|
data-settings-item="chat.prompt-navigator"
|
||||||
|
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-pressed={promptNavigatorEnabled}
|
||||||
|
onClick={() => handlePromptNavigatorEnabledChange(!promptNavigatorEnabled)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === ' ' || event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
handlePromptNavigatorEnabledChange(!promptNavigatorEnabled);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
checked={promptNavigatorEnabled}
|
||||||
|
onChange={handlePromptNavigatorEnabledChange}
|
||||||
|
ariaLabel={t('settings.openchamber.visual.field.promptNavigatorEnabledAria')}
|
||||||
|
/>
|
||||||
|
<span className="typography-ui-label text-foreground">{t('settings.openchamber.visual.field.promptNavigatorEnabled')}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{shouldShow('wideChatLayout') && (
|
{shouldShow('wideChatLayout') && (
|
||||||
<div
|
<div
|
||||||
data-settings-item="chat.wide-layout"
|
data-settings-item="chat.wide-layout"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
formatShortcutForDisplay,
|
formatShortcutForDisplay,
|
||||||
} from "@/lib/shortcuts";
|
} from "@/lib/shortcuts";
|
||||||
import { useI18n, type I18nKey } from "@/lib/i18n";
|
import { useI18n, type I18nKey } from "@/lib/i18n";
|
||||||
|
import { isVSCodeRuntime } from "@/lib/desktop";
|
||||||
import type { IconName } from "@/components/icon/icons";
|
import type { IconName } from "@/components/icon/icons";
|
||||||
|
|
||||||
type ShortcutItem = {
|
type ShortcutItem = {
|
||||||
@@ -40,6 +41,7 @@ export const HelpDialog: React.FC = () => {
|
|||||||
const setHelpDialogOpen = useUIStore((state) => state.setHelpDialogOpen);
|
const setHelpDialogOpen = useUIStore((state) => state.setHelpDialogOpen);
|
||||||
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
||||||
const mod = getModifierLabel();
|
const mod = getModifierLabel();
|
||||||
|
const isVSCode = isVSCodeRuntime();
|
||||||
|
|
||||||
const shortcuts: ShortcutSection[] = [
|
const shortcuts: ShortcutSection[] = [
|
||||||
{
|
{
|
||||||
@@ -114,6 +116,12 @@ export const HelpDialog: React.FC = () => {
|
|||||||
keys: '',
|
keys: '',
|
||||||
},
|
},
|
||||||
{ id: 'focus_input', descriptionKey: "helpDialog.item.focusChatInput", icon: "text", keys: '' },
|
{ id: 'focus_input', descriptionKey: "helpDialog.item.focusChatInput", icon: "text", keys: '' },
|
||||||
|
{
|
||||||
|
id: 'toggle_prompt_navigator',
|
||||||
|
descriptionKey: "helpDialog.item.togglePromptNavigator",
|
||||||
|
icon: "list-unordered",
|
||||||
|
keys: '',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'abort_run',
|
id: 'abort_run',
|
||||||
descriptionKey: "helpDialog.item.abortActiveRun",
|
descriptionKey: "helpDialog.item.abortActiveRun",
|
||||||
@@ -226,7 +234,9 @@ export const HelpDialog: React.FC = () => {
|
|||||||
{t(section.categoryKey)}
|
{t(section.categoryKey)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{section.items.map((shortcut) => {
|
{section.items
|
||||||
|
.filter((shortcut) => !(isVSCode && shortcut.id === 'toggle_prompt_navigator'))
|
||||||
|
.map((shortcut) => {
|
||||||
const displayKeys = shortcut.id
|
const displayKeys = shortcut.id
|
||||||
? renderShortcut(shortcut.id, Array.isArray(shortcut.keys) ? shortcut.keys[0] : shortcut.keys, shortcutOverrides)
|
? renderShortcut(shortcut.id, Array.isArray(shortcut.keys) ? shortcut.keys[0] : shortcut.keys, shortcutOverrides)
|
||||||
: (Array.isArray(shortcut.keys) ? shortcut.keys : shortcut.keys.split(" / "));
|
: (Array.isArray(shortcut.keys) ? shortcut.keys : shortcut.keys.split(" / "));
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export const useKeyboardShortcuts = () => {
|
|||||||
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
const setSettingsDialogOpen = useUIStore((s) => s.setSettingsDialogOpen);
|
||||||
const setModelSelectorOpen = useUIStore((s) => s.setModelSelectorOpen);
|
const setModelSelectorOpen = useUIStore((s) => s.setModelSelectorOpen);
|
||||||
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
const setTimelineDialogOpen = useUIStore((s) => s.setTimelineDialogOpen);
|
||||||
|
const togglePromptNavigatorPanel = useUIStore((s) => s.togglePromptNavigatorPanel);
|
||||||
|
const setPromptNavigatorPanelOpen = useUIStore((s) => s.setPromptNavigatorPanelOpen);
|
||||||
const toggleExpandedInput = useUIStore((s) => s.toggleExpandedInput);
|
const toggleExpandedInput = useUIStore((s) => s.toggleExpandedInput);
|
||||||
const shortcutOverrides = useUIStore((s) => s.shortcutOverrides);
|
const shortcutOverrides = useUIStore((s) => s.shortcutOverrides);
|
||||||
const currentDirectory = useDirectoryStore((s) => s.currentDirectory);
|
const currentDirectory = useDirectoryStore((s) => s.currentDirectory);
|
||||||
@@ -142,6 +144,42 @@ export const useKeyboardShortcuts = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventMatchesShortcut(e, combo('toggle_prompt_navigator'))) {
|
||||||
|
const {
|
||||||
|
activeMainTab,
|
||||||
|
promptNavigatorEnabled,
|
||||||
|
isSettingsDialogOpen,
|
||||||
|
isCommandPaletteOpen,
|
||||||
|
isHelpDialogOpen,
|
||||||
|
isSessionSwitcherOpen,
|
||||||
|
isAboutDialogOpen,
|
||||||
|
isTimelineDialogOpen,
|
||||||
|
isMultiRunLauncherOpen,
|
||||||
|
isImagePreviewOpen,
|
||||||
|
} = useUIStore.getState();
|
||||||
|
|
||||||
|
if (!promptNavigatorEnabled || isMobile || isVSCodeRuntime() || activeMainTab !== 'chat') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasOverlay = isSettingsDialogOpen
|
||||||
|
|| isCommandPaletteOpen
|
||||||
|
|| isHelpDialogOpen
|
||||||
|
|| isSessionSwitcherOpen
|
||||||
|
|| isAboutDialogOpen
|
||||||
|
|| isTimelineDialogOpen
|
||||||
|
|| isMultiRunLauncherOpen
|
||||||
|
|| isImagePreviewOpen;
|
||||||
|
|
||||||
|
if (hasOverlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
togglePromptNavigatorPanel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (eventMatchesShortcut(e, combo('open_status'))) {
|
if (eventMatchesShortcut(e, combo('open_status'))) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
void showOpenCodeStatus();
|
void showOpenCodeStatus();
|
||||||
@@ -505,6 +543,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
isMultiRunLauncherOpen,
|
isMultiRunLauncherOpen,
|
||||||
isImagePreviewOpen,
|
isImagePreviewOpen,
|
||||||
activeMainTab,
|
activeMainTab,
|
||||||
|
isPromptNavigatorPanelOpen,
|
||||||
} = useUIStore.getState();
|
} = useUIStore.getState();
|
||||||
|
|
||||||
if (isInsideDialog || isInsideTerminal || hasDropdownInteraction) {
|
if (isInsideDialog || isInsideTerminal || hasDropdownInteraction) {
|
||||||
@@ -512,6 +551,13 @@ export const useKeyboardShortcuts = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPromptNavigatorPanelOpen) {
|
||||||
|
e.preventDefault();
|
||||||
|
setPromptNavigatorPanelOpen(false);
|
||||||
|
resetAbortPriming();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If settings is open, close it
|
// If settings is open, close it
|
||||||
if (isSettingsDialogOpen) {
|
if (isSettingsDialogOpen) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -594,6 +640,8 @@ export const useKeyboardShortcuts = () => {
|
|||||||
setSettingsDialogOpen,
|
setSettingsDialogOpen,
|
||||||
setModelSelectorOpen,
|
setModelSelectorOpen,
|
||||||
setTimelineDialogOpen,
|
setTimelineDialogOpen,
|
||||||
|
togglePromptNavigatorPanel,
|
||||||
|
setPromptNavigatorPanelOpen,
|
||||||
toggleExpandedInput,
|
toggleExpandedInput,
|
||||||
setThemeMode,
|
setThemeMode,
|
||||||
working,
|
working,
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ export type DesktopSettings = {
|
|||||||
userMessageRenderingMode?: 'markdown' | 'plain';
|
userMessageRenderingMode?: 'markdown' | 'plain';
|
||||||
collapsibleUserMessages?: boolean;
|
collapsibleUserMessages?: boolean;
|
||||||
stickyUserHeader?: boolean;
|
stickyUserHeader?: boolean;
|
||||||
|
promptNavigatorEnabled?: boolean;
|
||||||
expandedEditorToolbar?: boolean;
|
expandedEditorToolbar?: boolean;
|
||||||
wideChatLayoutEnabled?: boolean;
|
wideChatLayoutEnabled?: boolean;
|
||||||
showSplitAssistantMessageActions?: boolean;
|
showSplitAssistantMessageActions?: boolean;
|
||||||
|
|||||||
@@ -1059,6 +1059,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Open model selector',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Open model selector',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Expand input',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Expand input',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Open conversation timeline',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Open conversation timeline',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': 'Toggle prompt navigator',
|
||||||
'settings.projects.sidebar.total': 'Total {count}',
|
'settings.projects.sidebar.total': 'Total {count}',
|
||||||
'settings.projects.sidebar.actions.addProject': 'Add project',
|
'settings.projects.sidebar.actions.addProject': 'Add project',
|
||||||
'settings.projects.page.empty.noProjects': 'No projects available.',
|
'settings.projects.page.empty.noProjects': 'No projects available.',
|
||||||
@@ -1803,6 +1804,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': 'Collapse Long User Messages',
|
'settings.openchamber.visual.field.collapsibleUserMessages': 'Collapse Long User Messages',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': 'Sticky user header',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': 'Sticky user header',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': 'Sticky User Header',
|
'settings.openchamber.visual.field.stickyUserHeader': 'Sticky User Header',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': 'Prompt navigator',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': 'Prompt Navigator',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': 'Wide chat layout',
|
'settings.openchamber.visual.field.wideChatLayoutAria': 'Wide chat layout',
|
||||||
|
|||||||
@@ -1563,6 +1563,7 @@ export const dict = {
|
|||||||
'helpDialog.item.createNewSession': 'Create New Session',
|
'helpDialog.item.createNewSession': 'Create New Session',
|
||||||
'helpDialog.item.createNewWorktreeDraft': 'Create New Worktree Draft',
|
'helpDialog.item.createNewWorktreeDraft': 'Create New Worktree Draft',
|
||||||
'helpDialog.item.focusChatInput': 'Focus Chat Input',
|
'helpDialog.item.focusChatInput': 'Focus Chat Input',
|
||||||
|
'helpDialog.item.togglePromptNavigator': 'Toggle Prompt Navigator',
|
||||||
'helpDialog.item.abortActiveRun': 'Abort active run (double press)',
|
'helpDialog.item.abortActiveRun': 'Abort active run (double press)',
|
||||||
'helpDialog.item.toggleRightSidebar': 'Toggle Right Sidebar',
|
'helpDialog.item.toggleRightSidebar': 'Toggle Right Sidebar',
|
||||||
'helpDialog.item.openRightSidebarGitTab': 'Open Right Sidebar Git Tab',
|
'helpDialog.item.openRightSidebarGitTab': 'Open Right Sidebar Git Tab',
|
||||||
@@ -1757,6 +1758,9 @@ export const dict = {
|
|||||||
'chat.draftStarters.sectionSkills': 'Skills',
|
'chat.draftStarters.sectionSkills': 'Skills',
|
||||||
'chat.draftStarters.remove': 'Remove',
|
'chat.draftStarters.remove': 'Remove',
|
||||||
'chat.scrollToBottom.aria': 'Scroll to bottom',
|
'chat.scrollToBottom.aria': 'Scroll to bottom',
|
||||||
|
'chat.promptNavigator.aria': 'Prompt navigation',
|
||||||
|
'chat.promptNavigator.currentPrompt': 'Current prompt',
|
||||||
|
'chat.promptNavigator.loadMore': 'Load more prompts',
|
||||||
'chat.timeline.relative.justNow': 'just now',
|
'chat.timeline.relative.justNow': 'just now',
|
||||||
'chat.timeline.relative.minutesAgo': '{count}m ago',
|
'chat.timeline.relative.minutesAgo': '{count}m ago',
|
||||||
'chat.timeline.relative.hoursAgo': '{count}h ago',
|
'chat.timeline.relative.hoursAgo': '{count}h ago',
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir selector de modelo",
|
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir selector de modelo",
|
||||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
||||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir línea de tiempo de conversación",
|
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir línea de tiempo de conversación",
|
||||||
|
"settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label": "Mostrar u ocultar navegador de prompts",
|
||||||
"settings.projects.sidebar.total": "Total {count}",
|
"settings.projects.sidebar.total": "Total {count}",
|
||||||
"settings.projects.sidebar.actions.addProject": "Añadir proyecto",
|
"settings.projects.sidebar.actions.addProject": "Añadir proyecto",
|
||||||
"settings.projects.page.empty.noProjects": "No hay proyectos disponibles.",
|
"settings.projects.page.empty.noProjects": "No hay proyectos disponibles.",
|
||||||
@@ -1770,6 +1771,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.collapsibleUserMessages": "Contraer mensajes largos del usuario",
|
"settings.openchamber.visual.field.collapsibleUserMessages": "Contraer mensajes largos del usuario",
|
||||||
"settings.openchamber.visual.field.stickyUserHeaderAria": "Encabezado del usuario fijo",
|
"settings.openchamber.visual.field.stickyUserHeaderAria": "Encabezado del usuario fijo",
|
||||||
"settings.openchamber.visual.field.stickyUserHeader": "Encabezado de usuario fijo",
|
"settings.openchamber.visual.field.stickyUserHeader": "Encabezado de usuario fijo",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabledAria": "Navegador de prompts",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabled": "Navegador de prompts",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Mostrar siempre la barra de herramientas del editor",
|
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Mostrar siempre la barra de herramientas del editor",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbar": "Mostrar siempre la barra de herramientas del editor (anclada bajo las pestañas)",
|
"settings.openchamber.visual.field.expandedEditorToolbar": "Mostrar siempre la barra de herramientas del editor (anclada bajo las pestañas)",
|
||||||
"settings.openchamber.visual.field.wideChatLayoutAria": "Diseño de chat ancho",
|
"settings.openchamber.visual.field.wideChatLayoutAria": "Diseño de chat ancho",
|
||||||
|
|||||||
@@ -1541,6 +1541,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"helpDialog.item.createNewSession": "Crear nueva sesión",
|
"helpDialog.item.createNewSession": "Crear nueva sesión",
|
||||||
"helpDialog.item.createNewWorktreeDraft": "Crear nuevo borrador de worktree",
|
"helpDialog.item.createNewWorktreeDraft": "Crear nuevo borrador de worktree",
|
||||||
"helpDialog.item.focusChatInput": "Enfocar entrada de chat",
|
"helpDialog.item.focusChatInput": "Enfocar entrada de chat",
|
||||||
|
"helpDialog.item.togglePromptNavigator": "Mostrar u ocultar navegador de prompts",
|
||||||
"helpDialog.item.abortActiveRun": "Detener ejecución activa (doble presionar)",
|
"helpDialog.item.abortActiveRun": "Detener ejecución activa (doble presionar)",
|
||||||
"helpDialog.item.toggleRightSidebar": "Mostrar u ocultar barra lateral derecha",
|
"helpDialog.item.toggleRightSidebar": "Mostrar u ocultar barra lateral derecha",
|
||||||
"helpDialog.item.openRightSidebarGitTab": "Abrir pestaña de Git en barra lateral derecha",
|
"helpDialog.item.openRightSidebarGitTab": "Abrir pestaña de Git en barra lateral derecha",
|
||||||
@@ -1735,6 +1736,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.draftStarters.sectionSkills": "Skills",
|
"chat.draftStarters.sectionSkills": "Skills",
|
||||||
"chat.draftStarters.remove": "Remove",
|
"chat.draftStarters.remove": "Remove",
|
||||||
"chat.scrollToBottom.aria": "Ir al final",
|
"chat.scrollToBottom.aria": "Ir al final",
|
||||||
|
"chat.promptNavigator.aria": "Navegación de prompts",
|
||||||
|
"chat.promptNavigator.currentPrompt": "Prompt actual",
|
||||||
|
"chat.promptNavigator.loadMore": "Cargar más prompts",
|
||||||
"chat.timeline.relative.justNow": "ahora mismo",
|
"chat.timeline.relative.justNow": "ahora mismo",
|
||||||
"chat.timeline.relative.minutesAgo": "hace {count}m",
|
"chat.timeline.relative.minutesAgo": "hace {count}m",
|
||||||
"chat.timeline.relative.hoursAgo": "hace {count}h",
|
"chat.timeline.relative.hoursAgo": "hace {count}h",
|
||||||
|
|||||||
@@ -951,6 +951,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Ouvrir le sélecteur de modèle',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'Ouvrir le sélecteur de modèle',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Développer l\'entrée',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Développer l\'entrée',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Chronologie de la conversation ouverte',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': 'Chronologie de la conversation ouverte',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': 'Afficher ou masquer le navigateur de prompts',
|
||||||
'settings.projects.sidebar.total': 'Total {count}',
|
'settings.projects.sidebar.total': 'Total {count}',
|
||||||
'settings.projects.sidebar.actions.addProject': 'Ajouter un projet',
|
'settings.projects.sidebar.actions.addProject': 'Ajouter un projet',
|
||||||
'settings.projects.page.empty.noProjects': 'Aucun projet disponible.',
|
'settings.projects.page.empty.noProjects': 'Aucun projet disponible.',
|
||||||
@@ -1679,6 +1680,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': 'Réduire les longs messages utilisateur',
|
'settings.openchamber.visual.field.collapsibleUserMessages': 'Réduire les longs messages utilisateur',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': 'En-tête utilisateur collant',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': 'En-tête utilisateur collant',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': 'En-tête utilisateur collant',
|
'settings.openchamber.visual.field.stickyUserHeader': 'En-tête utilisateur collant',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': 'Navigateur de prompts',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': 'Navigateur de prompts',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Toujours afficher la barre d’outils de l’éditeur',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Toujours afficher la barre d’outils de l’éditeur',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Toujours afficher la barre d’outils de l’éditeur (ancrée sous les onglets de fichiers)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Toujours afficher la barre d’outils de l’éditeur (ancrée sous les onglets de fichiers)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': 'Large disposition de discussion',
|
'settings.openchamber.visual.field.wideChatLayoutAria': 'Large disposition de discussion',
|
||||||
|
|||||||
@@ -1383,6 +1383,7 @@ export const dict = {
|
|||||||
'helpDialog.item.createNewSession': 'Créer une nouvelle session',
|
'helpDialog.item.createNewSession': 'Créer une nouvelle session',
|
||||||
'helpDialog.item.createNewWorktreeDraft': 'Créer un nouveau brouillon d\'worktree',
|
'helpDialog.item.createNewWorktreeDraft': 'Créer un nouveau brouillon d\'worktree',
|
||||||
'helpDialog.item.focusChatInput': 'Concentration sur la saisie du chat',
|
'helpDialog.item.focusChatInput': 'Concentration sur la saisie du chat',
|
||||||
|
'helpDialog.item.togglePromptNavigator': 'Afficher ou masquer le navigateur de prompts',
|
||||||
'helpDialog.item.abortActiveRun': 'Abandonner l’exécution active (double pression)',
|
'helpDialog.item.abortActiveRun': 'Abandonner l’exécution active (double pression)',
|
||||||
'helpDialog.item.toggleRightSidebar': 'Basculer la barre latérale droite',
|
'helpDialog.item.toggleRightSidebar': 'Basculer la barre latérale droite',
|
||||||
'helpDialog.item.openRightSidebarGitTab': 'Ouvrir l\'onglet Git de la barre latérale droite',
|
'helpDialog.item.openRightSidebarGitTab': 'Ouvrir l\'onglet Git de la barre latérale droite',
|
||||||
@@ -1562,6 +1563,9 @@ export const dict = {
|
|||||||
'chat.emptyState.draftTitle': 'Sur quoi travaillons-nous ?',
|
'chat.emptyState.draftTitle': 'Sur quoi travaillons-nous ?',
|
||||||
'chat.emptyState.draftTitleWithProject': 'Sur quoi travaillons-nous dans {project} ?',
|
'chat.emptyState.draftTitleWithProject': 'Sur quoi travaillons-nous dans {project} ?',
|
||||||
'chat.scrollToBottom.aria': 'Faire défiler vers le bas',
|
'chat.scrollToBottom.aria': 'Faire défiler vers le bas',
|
||||||
|
'chat.promptNavigator.aria': 'Navigation des prompts',
|
||||||
|
'chat.promptNavigator.currentPrompt': 'Prompt actuel',
|
||||||
|
'chat.promptNavigator.loadMore': 'Charger plus de prompts',
|
||||||
'chat.timeline.relative.justNow': 'à l’instant',
|
'chat.timeline.relative.justNow': 'à l’instant',
|
||||||
'chat.timeline.relative.minutesAgo': 'il y a {count} min',
|
'chat.timeline.relative.minutesAgo': 'il y a {count} min',
|
||||||
'chat.timeline.relative.hoursAgo': 'il y a {count} h',
|
'chat.timeline.relative.hoursAgo': 'il y a {count} h',
|
||||||
|
|||||||
@@ -1059,6 +1059,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '入力を拡大',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '入力を拡大',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'モデルセレクターを開く',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': 'モデルセレクターを開く',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '会話タイムラインを開く',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '会話タイムラインを開く',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': 'プロンプトナビゲーターの表示切替',
|
||||||
'settings.projects.sidebar.total': '合計 {count}',
|
'settings.projects.sidebar.total': '合計 {count}',
|
||||||
'settings.projects.sidebar.actions.addProject': 'プロジェクトを追加',
|
'settings.projects.sidebar.actions.addProject': 'プロジェクトを追加',
|
||||||
'settings.projects.page.empty.noProjects': '利用可能なプロジェクトがありません。',
|
'settings.projects.page.empty.noProjects': '利用可能なプロジェクトがありません。',
|
||||||
@@ -1803,6 +1804,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': '長いユーザーメッセージを折りたたむ',
|
'settings.openchamber.visual.field.collapsibleUserMessages': '長いユーザーメッセージを折りたたむ',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': 'ユーザーヘッダー固定',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': 'ユーザーヘッダー固定',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': 'ユーザーヘッダー固定',
|
'settings.openchamber.visual.field.stickyUserHeader': 'ユーザーヘッダー固定',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': 'プロンプトナビゲーター',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': 'プロンプトナビゲーター',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'エディターツールバーを常に表示',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'エディターツールバーを常に表示',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'エディターツールバーを常に表示(ファイルタブの下にドッキング)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'エディターツールバーを常に表示(ファイルタブの下にドッキング)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': 'ワイドチャットレイアウト',
|
'settings.openchamber.visual.field.wideChatLayoutAria': 'ワイドチャットレイアウト',
|
||||||
|
|||||||
@@ -1559,6 +1559,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'helpDialog.item.createNewSession': '新しいセッションを作成',
|
'helpDialog.item.createNewSession': '新しいセッションを作成',
|
||||||
'helpDialog.item.createNewWorktreeDraft': '新しいワークツリー下書きを作成',
|
'helpDialog.item.createNewWorktreeDraft': '新しいワークツリー下書きを作成',
|
||||||
'helpDialog.item.focusChatInput': 'チャット入力にフォーカス',
|
'helpDialog.item.focusChatInput': 'チャット入力にフォーカス',
|
||||||
|
'helpDialog.item.togglePromptNavigator': 'プロンプトナビゲーターの表示切替',
|
||||||
'helpDialog.item.abortActiveRun': 'アクティブな実行を中止(ダブルプレス)',
|
'helpDialog.item.abortActiveRun': 'アクティブな実行を中止(ダブルプレス)',
|
||||||
'helpDialog.item.toggleRightSidebar': '右サイドバーの切り替え',
|
'helpDialog.item.toggleRightSidebar': '右サイドバーの切り替え',
|
||||||
'helpDialog.item.openRightSidebarGitTab': '右サイドバーのGitタブを開く',
|
'helpDialog.item.openRightSidebarGitTab': '右サイドバーのGitタブを開く',
|
||||||
@@ -1753,6 +1754,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.draftStarters.sectionSkills': 'スキル',
|
'chat.draftStarters.sectionSkills': 'スキル',
|
||||||
'chat.draftStarters.remove': '削除',
|
'chat.draftStarters.remove': '削除',
|
||||||
'chat.scrollToBottom.aria': '一番下にスクロール',
|
'chat.scrollToBottom.aria': '一番下にスクロール',
|
||||||
|
'chat.promptNavigator.aria': 'プロンプトナビゲーション',
|
||||||
|
'chat.promptNavigator.currentPrompt': '現在のプロンプト',
|
||||||
|
'chat.promptNavigator.loadMore': 'さらにプロンプトを読み込む',
|
||||||
'chat.timeline.relative.justNow': 'たった今',
|
'chat.timeline.relative.justNow': 'たった今',
|
||||||
'chat.timeline.relative.minutesAgo': '{count}分前',
|
'chat.timeline.relative.minutesAgo': '{count}分前',
|
||||||
'chat.timeline.relative.hoursAgo': '{count}時間前',
|
'chat.timeline.relative.hoursAgo': '{count}時間前',
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '모델 선택기 열기',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '모델 선택기 열기',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '입력 확장',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '입력 확장',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '대화 타임라인 열기',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '대화 타임라인 열기',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': '프롬프트 탐색기 표시/숨기기',
|
||||||
'settings.projects.sidebar.total': '총 {count}개',
|
'settings.projects.sidebar.total': '총 {count}개',
|
||||||
'settings.projects.sidebar.actions.addProject': '프로젝트 추가',
|
'settings.projects.sidebar.actions.addProject': '프로젝트 추가',
|
||||||
'settings.projects.page.empty.noProjects': '사용 가능한 프로젝트가 없습니다.',
|
'settings.projects.page.empty.noProjects': '사용 가능한 프로젝트가 없습니다.',
|
||||||
@@ -1770,6 +1771,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': '긴 사용자 메시지 접기',
|
'settings.openchamber.visual.field.collapsibleUserMessages': '긴 사용자 메시지 접기',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': '고정 사용자 헤더',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': '고정 사용자 헤더',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': '고정 사용자 헤더',
|
'settings.openchamber.visual.field.stickyUserHeader': '고정 사용자 헤더',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': '프롬프트 탐색기',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': '프롬프트 탐색기',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': '넓은 채팅 레이아웃',
|
'settings.openchamber.visual.field.wideChatLayoutAria': '넓은 채팅 레이아웃',
|
||||||
|
|||||||
@@ -1565,6 +1565,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'helpDialog.item.createNewSession': '새 세션 만들기',
|
'helpDialog.item.createNewSession': '새 세션 만들기',
|
||||||
'helpDialog.item.createNewWorktreeDraft': '새 워크트리 드래프트 만들기',
|
'helpDialog.item.createNewWorktreeDraft': '새 워크트리 드래프트 만들기',
|
||||||
'helpDialog.item.focusChatInput': '채팅 입력창으로 포커스 이동',
|
'helpDialog.item.focusChatInput': '채팅 입력창으로 포커스 이동',
|
||||||
|
'helpDialog.item.togglePromptNavigator': '프롬프트 탐색기 표시/숨기기',
|
||||||
'helpDialog.item.abortActiveRun': '활성 실행 중단(두 번 누르기)',
|
'helpDialog.item.abortActiveRun': '활성 실행 중단(두 번 누르기)',
|
||||||
'helpDialog.item.toggleRightSidebar': '오른쪽 사이드바 전환',
|
'helpDialog.item.toggleRightSidebar': '오른쪽 사이드바 전환',
|
||||||
'helpDialog.item.openRightSidebarGitTab': '오른쪽 사이드바 Git 탭 열기',
|
'helpDialog.item.openRightSidebarGitTab': '오른쪽 사이드바 Git 탭 열기',
|
||||||
@@ -1759,6 +1760,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.draftStarters.sectionSkills': 'Skills',
|
'chat.draftStarters.sectionSkills': 'Skills',
|
||||||
'chat.draftStarters.remove': 'Remove',
|
'chat.draftStarters.remove': 'Remove',
|
||||||
'chat.scrollToBottom.aria': '맨 아래로 스크롤',
|
'chat.scrollToBottom.aria': '맨 아래로 스크롤',
|
||||||
|
'chat.promptNavigator.aria': '프롬프트 탐색',
|
||||||
|
'chat.promptNavigator.currentPrompt': '현재 프롬프트',
|
||||||
|
'chat.promptNavigator.loadMore': '더 많은 프롬프트 불러오기',
|
||||||
'chat.timeline.relative.justNow': '방금 전',
|
'chat.timeline.relative.justNow': '방금 전',
|
||||||
'chat.timeline.relative.minutesAgo': '{count}분 전',
|
'chat.timeline.relative.minutesAgo': '{count}분 전',
|
||||||
'chat.timeline.relative.hoursAgo': '{count}시간 전',
|
'chat.timeline.relative.hoursAgo': '{count}시간 전',
|
||||||
|
|||||||
@@ -761,6 +761,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Przełącz motyw',
|
'settings.openchamber.keyboardShortcuts.action.cycle_theme.label': 'Przełącz motyw',
|
||||||
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Przełącz agenta',
|
'settings.openchamber.keyboardShortcuts.action.cycle_agent.label': 'Przełącz agenta',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Rozwiń pole wprowadzania',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': 'Rozwiń pole wprowadzania',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': 'Przełącz nawigator promptów',
|
||||||
'settings.openchamber.keyboardShortcuts.action.focus_input.label': 'Skup pole wprowadzania',
|
'settings.openchamber.keyboardShortcuts.action.focus_input.label': 'Skup pole wprowadzania',
|
||||||
'settings.openchamber.keyboardShortcuts.action.new_chat.label': 'Nowa sesja',
|
'settings.openchamber.keyboardShortcuts.action.new_chat.label': 'Nowa sesja',
|
||||||
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': 'Nowy szkic obszaru roboczego',
|
'settings.openchamber.keyboardShortcuts.action.new_chat_worktree.label': 'Nowy szkic obszaru roboczego',
|
||||||
@@ -1034,6 +1035,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.spacingDensity': 'Gęstość odstępów',
|
'settings.openchamber.visual.field.spacingDensity': 'Gęstość odstępów',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': 'Przyklejony nagłówek użytkownika',
|
'settings.openchamber.visual.field.stickyUserHeader': 'Przyklejony nagłówek użytkownika',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': 'Przyklejony nagłówek użytkownika',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': 'Przyklejony nagłówek użytkownika',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': 'Nawigator promptów',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': 'Nawigator promptów',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
||||||
'settings.openchamber.visual.field.terminalFontSize': 'Rozmiar czcionki terminala',
|
'settings.openchamber.visual.field.terminalFontSize': 'Rozmiar czcionki terminala',
|
||||||
|
|||||||
@@ -651,6 +651,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.draftStarters.sectionSkills': 'Skills',
|
'chat.draftStarters.sectionSkills': 'Skills',
|
||||||
'chat.draftStarters.remove': 'Remove',
|
'chat.draftStarters.remove': 'Remove',
|
||||||
'chat.scrollToBottom.aria': 'Przewiń na dół',
|
'chat.scrollToBottom.aria': 'Przewiń na dół',
|
||||||
|
'chat.promptNavigator.aria': 'Nawigacja promptów',
|
||||||
|
'chat.promptNavigator.currentPrompt': 'Bieżący prompt',
|
||||||
|
'chat.promptNavigator.loadMore': 'Wczytaj więcej promptów',
|
||||||
'chat.timeline.relative.justNow': 'przed chwilą',
|
'chat.timeline.relative.justNow': 'przed chwilą',
|
||||||
'chat.timeline.relative.minutesAgo': '{count}m temu',
|
'chat.timeline.relative.minutesAgo': '{count}m temu',
|
||||||
'chat.timeline.relative.hoursAgo': '{count}h temu',
|
'chat.timeline.relative.hoursAgo': '{count}h temu',
|
||||||
@@ -2183,6 +2186,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'helpDialog.item.cycleTheme': 'Przełącz motyw (Jasny → Ciemny → Systemowy)',
|
'helpDialog.item.cycleTheme': 'Przełącz motyw (Jasny → Ciemny → Systemowy)',
|
||||||
'helpDialog.item.cycleThinkingVariant': 'Przełącz wariant myślenia (skrót globalny)',
|
'helpDialog.item.cycleThinkingVariant': 'Przełącz wariant myślenia (skrót globalny)',
|
||||||
'helpDialog.item.focusChatInput': 'Ustaw fokus na polu czatu',
|
'helpDialog.item.focusChatInput': 'Ustaw fokus na polu czatu',
|
||||||
|
'helpDialog.item.togglePromptNavigator': 'Przełącz nawigator promptów',
|
||||||
'helpDialog.item.navigateModels': 'Nawiguj po modelach (w selektorze)',
|
'helpDialog.item.navigateModels': 'Nawiguj po modelach (w selektorze)',
|
||||||
'helpDialog.item.newWindow': 'Nowe okno (tylko desktop)',
|
'helpDialog.item.newWindow': 'Nowe okno (tylko desktop)',
|
||||||
'helpDialog.item.openCommandPalette': 'Otwórz paletę poleceń',
|
'helpDialog.item.openCommandPalette': 'Otwórz paletę poleceń',
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir seletor de modelo",
|
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Abrir seletor de modelo",
|
||||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Expandir entrada",
|
||||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir linha do tempo da conversa",
|
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Abrir linha do tempo da conversa",
|
||||||
|
"settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label": "Mostrar ou ocultar navegador de prompts",
|
||||||
"settings.projects.sidebar.total": "Total {count}",
|
"settings.projects.sidebar.total": "Total {count}",
|
||||||
"settings.projects.sidebar.actions.addProject": "Adicionar projeto",
|
"settings.projects.sidebar.actions.addProject": "Adicionar projeto",
|
||||||
"settings.projects.page.empty.noProjects": "Não há projetos disponíveis.",
|
"settings.projects.page.empty.noProjects": "Não há projetos disponíveis.",
|
||||||
@@ -1770,6 +1771,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.collapsibleUserMessages": "Recolher mensagens longas do usuário",
|
"settings.openchamber.visual.field.collapsibleUserMessages": "Recolher mensagens longas do usuário",
|
||||||
"settings.openchamber.visual.field.stickyUserHeaderAria": "Cabeçalho do usuário fixo",
|
"settings.openchamber.visual.field.stickyUserHeaderAria": "Cabeçalho do usuário fixo",
|
||||||
"settings.openchamber.visual.field.stickyUserHeader": "Cabeçalho do usuário fixo",
|
"settings.openchamber.visual.field.stickyUserHeader": "Cabeçalho do usuário fixo",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabledAria": "Navegador de prompts",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabled": "Navegador de prompts",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Mostrar sempre a barra de ferramentas do editor",
|
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Mostrar sempre a barra de ferramentas do editor",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbar": "Mostrar sempre a barra de ferramentas do editor (ancorada sob as abas)",
|
"settings.openchamber.visual.field.expandedEditorToolbar": "Mostrar sempre a barra de ferramentas do editor (ancorada sob as abas)",
|
||||||
"settings.openchamber.visual.field.wideChatLayoutAria": "Layout de chat amplo",
|
"settings.openchamber.visual.field.wideChatLayoutAria": "Layout de chat amplo",
|
||||||
|
|||||||
@@ -1541,6 +1541,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"helpDialog.item.createNewSession": "Criar nova sessão",
|
"helpDialog.item.createNewSession": "Criar nova sessão",
|
||||||
"helpDialog.item.createNewWorktreeDraft": "Criar novo rascunho de worktree",
|
"helpDialog.item.createNewWorktreeDraft": "Criar novo rascunho de worktree",
|
||||||
"helpDialog.item.focusChatInput": "Focar entrada do chat",
|
"helpDialog.item.focusChatInput": "Focar entrada do chat",
|
||||||
|
"helpDialog.item.togglePromptNavigator": "Mostrar ou ocultar navegador de prompts",
|
||||||
"helpDialog.item.abortActiveRun": "Interromper execução ativa (duplo clique)",
|
"helpDialog.item.abortActiveRun": "Interromper execução ativa (duplo clique)",
|
||||||
"helpDialog.item.toggleRightSidebar": "Mostrar ou ocultar barra lateral direita",
|
"helpDialog.item.toggleRightSidebar": "Mostrar ou ocultar barra lateral direita",
|
||||||
"helpDialog.item.openRightSidebarGitTab": "Abrir aba de Git em barra lateral direita",
|
"helpDialog.item.openRightSidebarGitTab": "Abrir aba de Git em barra lateral direita",
|
||||||
@@ -1735,6 +1736,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.draftStarters.sectionSkills": "Skills",
|
"chat.draftStarters.sectionSkills": "Skills",
|
||||||
"chat.draftStarters.remove": "Remove",
|
"chat.draftStarters.remove": "Remove",
|
||||||
"chat.scrollToBottom.aria": "Ir ao final",
|
"chat.scrollToBottom.aria": "Ir ao final",
|
||||||
|
"chat.promptNavigator.aria": "Navegação de prompts",
|
||||||
|
"chat.promptNavigator.currentPrompt": "Prompt atual",
|
||||||
|
"chat.promptNavigator.loadMore": "Carregar mais prompts",
|
||||||
"chat.timeline.relative.justNow": "agora mesmo",
|
"chat.timeline.relative.justNow": "agora mesmo",
|
||||||
"chat.timeline.relative.minutesAgo": "há {count}m",
|
"chat.timeline.relative.minutesAgo": "há {count}m",
|
||||||
"chat.timeline.relative.hoursAgo": "há {count}h",
|
"chat.timeline.relative.hoursAgo": "há {count}h",
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Відкрити вибір моделі",
|
"settings.openchamber.keyboardShortcuts.action.open_model_selector.label": "Відкрити вибір моделі",
|
||||||
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Розгорнути введення",
|
"settings.openchamber.keyboardShortcuts.action.expand_input.label": "Розгорнути введення",
|
||||||
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Відкрити хронологію розмови",
|
"settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label": "Відкрити хронологію розмови",
|
||||||
|
"settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label": "Показати або приховати навігатор промптів",
|
||||||
"settings.projects.sidebar.total": "Усього {count}",
|
"settings.projects.sidebar.total": "Усього {count}",
|
||||||
"settings.projects.sidebar.actions.addProject": "Додати проєкт",
|
"settings.projects.sidebar.actions.addProject": "Додати проєкт",
|
||||||
"settings.projects.page.empty.noProjects": "Немає доступних проєктів.",
|
"settings.projects.page.empty.noProjects": "Немає доступних проєктів.",
|
||||||
@@ -1770,6 +1771,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.collapsibleUserMessages": "Згортати довгі повідомлення користувача",
|
"settings.openchamber.visual.field.collapsibleUserMessages": "Згортати довгі повідомлення користувача",
|
||||||
"settings.openchamber.visual.field.stickyUserHeaderAria": "Закріплений заголовок користувача",
|
"settings.openchamber.visual.field.stickyUserHeaderAria": "Закріплений заголовок користувача",
|
||||||
"settings.openchamber.visual.field.stickyUserHeader": "Закріплений заголовок користувача",
|
"settings.openchamber.visual.field.stickyUserHeader": "Закріплений заголовок користувача",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabledAria": "Навігатор промптів",
|
||||||
|
"settings.openchamber.visual.field.promptNavigatorEnabled": "Навігатор промптів",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Завжди показувати панель інструментів редактора",
|
"settings.openchamber.visual.field.expandedEditorToolbarAria": "Завжди показувати панель інструментів редактора",
|
||||||
"settings.openchamber.visual.field.expandedEditorToolbar": "Завжди показувати панель інструментів редактора (закріплена під вкладками)",
|
"settings.openchamber.visual.field.expandedEditorToolbar": "Завжди показувати панель інструментів редактора (закріплена під вкладками)",
|
||||||
"settings.openchamber.visual.field.wideChatLayoutAria": "Широкий макет чату",
|
"settings.openchamber.visual.field.wideChatLayoutAria": "Широкий макет чату",
|
||||||
|
|||||||
@@ -1541,6 +1541,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"helpDialog.item.createNewSession": "Створити нову сесію",
|
"helpDialog.item.createNewSession": "Створити нову сесію",
|
||||||
"helpDialog.item.createNewWorktreeDraft": "Створити нову чернетку worktree",
|
"helpDialog.item.createNewWorktreeDraft": "Створити нову чернетку worktree",
|
||||||
"helpDialog.item.focusChatInput": "Фокус на полі вводу чату",
|
"helpDialog.item.focusChatInput": "Фокус на полі вводу чату",
|
||||||
|
"helpDialog.item.togglePromptNavigator": "Показати або приховати навігатор промптів",
|
||||||
"helpDialog.item.abortActiveRun": "Перервати активний запуск (подвійне натискання)",
|
"helpDialog.item.abortActiveRun": "Перервати активний запуск (подвійне натискання)",
|
||||||
"helpDialog.item.toggleRightSidebar": "Перемкнути праву бічну панель",
|
"helpDialog.item.toggleRightSidebar": "Перемкнути праву бічну панель",
|
||||||
"helpDialog.item.openRightSidebarGitTab": "Відкрити вкладку Git у правій бічній панелі",
|
"helpDialog.item.openRightSidebarGitTab": "Відкрити вкладку Git у правій бічній панелі",
|
||||||
@@ -1735,6 +1736,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"chat.draftStarters.sectionSkills": "Скіли",
|
"chat.draftStarters.sectionSkills": "Скіли",
|
||||||
"chat.draftStarters.remove": "Прибрати",
|
"chat.draftStarters.remove": "Прибрати",
|
||||||
"chat.scrollToBottom.aria": "Прокрутити вниз",
|
"chat.scrollToBottom.aria": "Прокрутити вниз",
|
||||||
|
"chat.promptNavigator.aria": "Навігація за промптами",
|
||||||
|
"chat.promptNavigator.currentPrompt": "Поточний промпт",
|
||||||
|
"chat.promptNavigator.loadMore": "Завантажити більше промптів",
|
||||||
"chat.timeline.relative.justNow": "щойно",
|
"chat.timeline.relative.justNow": "щойно",
|
||||||
"chat.timeline.relative.minutesAgo": "{count} хв тому",
|
"chat.timeline.relative.minutesAgo": "{count} хв тому",
|
||||||
"chat.timeline.relative.hoursAgo": "{count} год тому",
|
"chat.timeline.relative.hoursAgo": "{count} год тому",
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '打开模型选择器',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '打开模型选择器',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展开输入框',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展开输入框',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '打开对话时间线',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '打开对话时间线',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': '显示或隐藏提示词导航',
|
||||||
'settings.projects.sidebar.total': '总计 {count}',
|
'settings.projects.sidebar.total': '总计 {count}',
|
||||||
'settings.projects.sidebar.actions.addProject': '添加项目',
|
'settings.projects.sidebar.actions.addProject': '添加项目',
|
||||||
'settings.projects.page.empty.noProjects': '暂无项目。',
|
'settings.projects.page.empty.noProjects': '暂无项目。',
|
||||||
@@ -1770,6 +1771,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': '折叠较长的用户消息',
|
'settings.openchamber.visual.field.collapsibleUserMessages': '折叠较长的用户消息',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': '固定用户消息头',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': '固定用户消息头',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': '固定用户消息头',
|
'settings.openchamber.visual.field.stickyUserHeader': '固定用户消息头',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': '提示词导航',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': '提示词导航',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': '宽聊天布局',
|
'settings.openchamber.visual.field.wideChatLayoutAria': '宽聊天布局',
|
||||||
|
|||||||
@@ -1529,6 +1529,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'helpDialog.item.createNewSession': '创建新会话',
|
'helpDialog.item.createNewSession': '创建新会话',
|
||||||
'helpDialog.item.createNewWorktreeDraft': '创建新工作树草稿',
|
'helpDialog.item.createNewWorktreeDraft': '创建新工作树草稿',
|
||||||
'helpDialog.item.focusChatInput': '聚焦聊天输入框',
|
'helpDialog.item.focusChatInput': '聚焦聊天输入框',
|
||||||
|
'helpDialog.item.togglePromptNavigator': '显示或隐藏提示词导航',
|
||||||
'helpDialog.item.abortActiveRun': '中止当前运行(双击)',
|
'helpDialog.item.abortActiveRun': '中止当前运行(双击)',
|
||||||
'helpDialog.item.toggleRightSidebar': '切换右侧边栏',
|
'helpDialog.item.toggleRightSidebar': '切换右侧边栏',
|
||||||
'helpDialog.item.openRightSidebarGitTab': '打开右侧边栏 Git 标签',
|
'helpDialog.item.openRightSidebarGitTab': '打开右侧边栏 Git 标签',
|
||||||
@@ -1723,6 +1724,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.draftStarters.sectionSkills': 'Skills',
|
'chat.draftStarters.sectionSkills': 'Skills',
|
||||||
'chat.draftStarters.remove': 'Remove',
|
'chat.draftStarters.remove': 'Remove',
|
||||||
'chat.scrollToBottom.aria': '滚动到底部',
|
'chat.scrollToBottom.aria': '滚动到底部',
|
||||||
|
'chat.promptNavigator.aria': '提示词导航',
|
||||||
|
'chat.promptNavigator.currentPrompt': '当前提示',
|
||||||
|
'chat.promptNavigator.loadMore': '加载更多提示',
|
||||||
'chat.timeline.relative.justNow': '刚刚',
|
'chat.timeline.relative.justNow': '刚刚',
|
||||||
'chat.timeline.relative.minutesAgo': '{count} 分钟前',
|
'chat.timeline.relative.minutesAgo': '{count} 分钟前',
|
||||||
'chat.timeline.relative.hoursAgo': '{count} 小时前',
|
'chat.timeline.relative.hoursAgo': '{count} 小时前',
|
||||||
|
|||||||
@@ -938,6 +938,7 @@
|
|||||||
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '開啟模型選擇器',
|
'settings.openchamber.keyboardShortcuts.action.open_model_selector.label': '開啟模型選擇器',
|
||||||
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展開輸入方塊',
|
'settings.openchamber.keyboardShortcuts.action.expand_input.label': '展開輸入方塊',
|
||||||
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '開啟對話時間軸',
|
'settings.openchamber.keyboardShortcuts.action.open_timeline_dialog.label': '開啟對話時間軸',
|
||||||
|
'settings.openchamber.keyboardShortcuts.action.toggle_prompt_navigator.label': '顯示或隱藏提示詞導覽',
|
||||||
'settings.projects.sidebar.total': '總計 {count}',
|
'settings.projects.sidebar.total': '總計 {count}',
|
||||||
'settings.projects.sidebar.actions.addProject': '新增專案',
|
'settings.projects.sidebar.actions.addProject': '新增專案',
|
||||||
'settings.projects.page.empty.noProjects': '暫無專案。',
|
'settings.projects.page.empty.noProjects': '暫無專案。',
|
||||||
@@ -1681,6 +1682,8 @@
|
|||||||
'settings.openchamber.visual.field.collapsibleUserMessages': '收合較長的使用者訊息',
|
'settings.openchamber.visual.field.collapsibleUserMessages': '收合較長的使用者訊息',
|
||||||
'settings.openchamber.visual.field.stickyUserHeaderAria': '固定使用者訊息標頭',
|
'settings.openchamber.visual.field.stickyUserHeaderAria': '固定使用者訊息標頭',
|
||||||
'settings.openchamber.visual.field.stickyUserHeader': '固定使用者訊息標頭',
|
'settings.openchamber.visual.field.stickyUserHeader': '固定使用者訊息標頭',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabledAria': '提示詞導覽',
|
||||||
|
'settings.openchamber.visual.field.promptNavigatorEnabled': '提示詞導覽',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar',
|
||||||
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)',
|
||||||
'settings.openchamber.visual.field.wideChatLayoutAria': '寬聊天佈局',
|
'settings.openchamber.visual.field.wideChatLayoutAria': '寬聊天佈局',
|
||||||
|
|||||||
@@ -1533,6 +1533,7 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'helpDialog.item.createNewSession': '建立新會話',
|
'helpDialog.item.createNewSession': '建立新會話',
|
||||||
'helpDialog.item.createNewWorktreeDraft': '建立新 worktree 草稿',
|
'helpDialog.item.createNewWorktreeDraft': '建立新 worktree 草稿',
|
||||||
'helpDialog.item.focusChatInput': '聚焦聊天輸入框',
|
'helpDialog.item.focusChatInput': '聚焦聊天輸入框',
|
||||||
|
'helpDialog.item.togglePromptNavigator': '顯示或隱藏提示詞導覽',
|
||||||
'helpDialog.item.abortActiveRun': '中止目前執行(連按兩下)',
|
'helpDialog.item.abortActiveRun': '中止目前執行(連按兩下)',
|
||||||
'helpDialog.item.toggleRightSidebar': '切換右側邊欄',
|
'helpDialog.item.toggleRightSidebar': '切換右側邊欄',
|
||||||
'helpDialog.item.openRightSidebarGitTab': '開啟右側邊欄 Git 標籤',
|
'helpDialog.item.openRightSidebarGitTab': '開啟右側邊欄 Git 標籤',
|
||||||
@@ -1727,6 +1728,9 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'chat.draftStarters.sectionSkills': 'Skills',
|
'chat.draftStarters.sectionSkills': 'Skills',
|
||||||
'chat.draftStarters.remove': 'Remove',
|
'chat.draftStarters.remove': 'Remove',
|
||||||
'chat.scrollToBottom.aria': '捲動到底部',
|
'chat.scrollToBottom.aria': '捲動到底部',
|
||||||
|
'chat.promptNavigator.aria': '提示詞導覽',
|
||||||
|
'chat.promptNavigator.currentPrompt': '目前提示',
|
||||||
|
'chat.promptNavigator.loadMore': '載入更多提示',
|
||||||
'chat.timeline.relative.justNow': '剛剛',
|
'chat.timeline.relative.justNow': '剛剛',
|
||||||
'chat.timeline.relative.minutesAgo': '{count} 分鐘前',
|
'chat.timeline.relative.minutesAgo': '{count} 分鐘前',
|
||||||
'chat.timeline.relative.hoursAgo': '{count} 小時前',
|
'chat.timeline.relative.hoursAgo': '{count} 小時前',
|
||||||
|
|||||||
@@ -582,6 +582,9 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
|
|||||||
if (typeof settings.stickyUserHeader === 'boolean' && settings.stickyUserHeader !== store.stickyUserHeader) {
|
if (typeof settings.stickyUserHeader === 'boolean' && settings.stickyUserHeader !== store.stickyUserHeader) {
|
||||||
store.setStickyUserHeader(settings.stickyUserHeader);
|
store.setStickyUserHeader(settings.stickyUserHeader);
|
||||||
}
|
}
|
||||||
|
if (typeof settings.promptNavigatorEnabled === 'boolean' && settings.promptNavigatorEnabled !== store.promptNavigatorEnabled) {
|
||||||
|
store.setPromptNavigatorEnabled(settings.promptNavigatorEnabled);
|
||||||
|
}
|
||||||
if (typeof settings.expandedEditorToolbar === 'boolean' && settings.expandedEditorToolbar !== store.expandedEditorToolbar) {
|
if (typeof settings.expandedEditorToolbar === 'boolean' && settings.expandedEditorToolbar !== store.expandedEditorToolbar) {
|
||||||
store.setExpandedEditorToolbar(settings.expandedEditorToolbar);
|
store.setExpandedEditorToolbar(settings.expandedEditorToolbar);
|
||||||
}
|
}
|
||||||
@@ -1150,6 +1153,9 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
|||||||
if (typeof candidate.stickyUserHeader === 'boolean') {
|
if (typeof candidate.stickyUserHeader === 'boolean') {
|
||||||
result.stickyUserHeader = candidate.stickyUserHeader;
|
result.stickyUserHeader = candidate.stickyUserHeader;
|
||||||
}
|
}
|
||||||
|
if (typeof candidate.promptNavigatorEnabled === 'boolean') {
|
||||||
|
result.promptNavigatorEnabled = candidate.promptNavigatorEnabled;
|
||||||
|
}
|
||||||
if (typeof candidate.wideChatLayoutEnabled === 'boolean') {
|
if (typeof candidate.wideChatLayoutEnabled === 'boolean') {
|
||||||
result.wideChatLayoutEnabled = candidate.wideChatLayoutEnabled;
|
result.wideChatLayoutEnabled = candidate.wideChatLayoutEnabled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,6 +224,13 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
|
|||||||
titleKey: 'settings.openchamber.visual.field.stickyUserHeader',
|
titleKey: 'settings.openchamber.visual.field.stickyUserHeader',
|
||||||
keywords: ['messages', 'header'],
|
keywords: ['messages', 'header'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'chat.prompt-navigator',
|
||||||
|
page: 'chat',
|
||||||
|
titleKey: 'settings.openchamber.visual.field.promptNavigatorEnabled',
|
||||||
|
keywords: ['prompt', 'navigator', 'navigation', 'timeline', 'scroll'],
|
||||||
|
isAvailable: (ctx) => !ctx.isVSCode,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'chat.collapsible-user-messages',
|
id: 'chat.collapsible-user-messages',
|
||||||
page: 'chat',
|
page: 'chat',
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ const SHORTCUT_ACTIONS: ReadonlyArray<ShortcutAction> = [
|
|||||||
description: 'Search and navigate within current conversation',
|
description: 'Search and navigate within current conversation',
|
||||||
customizable: true,
|
customizable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'toggle_prompt_navigator',
|
||||||
|
defaultCombo: 'mod+alt+p',
|
||||||
|
label: 'Toggle prompt navigator',
|
||||||
|
description: 'Show or hide the prompt navigator panel in chat',
|
||||||
|
customizable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'toggle_right_sidebar',
|
id: 'toggle_right_sidebar',
|
||||||
defaultCombo: 'mod+b',
|
defaultCombo: 'mod+b',
|
||||||
|
|||||||
@@ -612,6 +612,7 @@ interface UIStore {
|
|||||||
diffWrapLines: boolean;
|
diffWrapLines: boolean;
|
||||||
gitChangesViewMode: 'flat' | 'tree';
|
gitChangesViewMode: 'flat' | 'tree';
|
||||||
isTimelineDialogOpen: boolean;
|
isTimelineDialogOpen: boolean;
|
||||||
|
isPromptNavigatorPanelOpen: boolean;
|
||||||
isImagePreviewOpen: boolean;
|
isImagePreviewOpen: boolean;
|
||||||
nativeNotificationsEnabled: boolean;
|
nativeNotificationsEnabled: boolean;
|
||||||
notificationMode: 'always' | 'hidden-only';
|
notificationMode: 'always' | 'hidden-only';
|
||||||
@@ -655,6 +656,7 @@ interface UIStore {
|
|||||||
userMessageRenderingMode: UserMessageRenderingMode;
|
userMessageRenderingMode: UserMessageRenderingMode;
|
||||||
collapsibleUserMessages: boolean;
|
collapsibleUserMessages: boolean;
|
||||||
stickyUserHeader: boolean;
|
stickyUserHeader: boolean;
|
||||||
|
promptNavigatorEnabled: boolean;
|
||||||
expandedEditorToolbar: boolean;
|
expandedEditorToolbar: boolean;
|
||||||
showSplitAssistantMessageActions: boolean;
|
showSplitAssistantMessageActions: boolean;
|
||||||
allowPromptingSubagentSessions: boolean;
|
allowPromptingSubagentSessions: boolean;
|
||||||
@@ -780,6 +782,8 @@ interface UIStore {
|
|||||||
setGitChangesViewMode: (mode: 'flat' | 'tree') => void;
|
setGitChangesViewMode: (mode: 'flat' | 'tree') => void;
|
||||||
setMultiRunLauncherOpen: (open: boolean) => void;
|
setMultiRunLauncherOpen: (open: boolean) => void;
|
||||||
setTimelineDialogOpen: (open: boolean) => void;
|
setTimelineDialogOpen: (open: boolean) => void;
|
||||||
|
setPromptNavigatorPanelOpen: (open: boolean) => void;
|
||||||
|
togglePromptNavigatorPanel: () => void;
|
||||||
setImagePreviewOpen: (open: boolean) => void;
|
setImagePreviewOpen: (open: boolean) => void;
|
||||||
setNativeNotificationsEnabled: (value: boolean) => void;
|
setNativeNotificationsEnabled: (value: boolean) => void;
|
||||||
setNotificationMode: (mode: 'always' | 'hidden-only') => void;
|
setNotificationMode: (mode: 'always' | 'hidden-only') => void;
|
||||||
@@ -810,6 +814,7 @@ interface UIStore {
|
|||||||
setUserMessageRenderingMode: (value: UserMessageRenderingMode) => void;
|
setUserMessageRenderingMode: (value: UserMessageRenderingMode) => void;
|
||||||
setCollapsibleUserMessages: (value: boolean) => void;
|
setCollapsibleUserMessages: (value: boolean) => void;
|
||||||
setStickyUserHeader: (value: boolean) => void;
|
setStickyUserHeader: (value: boolean) => void;
|
||||||
|
setPromptNavigatorEnabled: (value: boolean) => void;
|
||||||
setExpandedEditorToolbar: (value: boolean) => void;
|
setExpandedEditorToolbar: (value: boolean) => void;
|
||||||
setShowSplitAssistantMessageActions: (value: boolean) => void;
|
setShowSplitAssistantMessageActions: (value: boolean) => void;
|
||||||
setAllowPromptingSubagentSessions: (value: boolean) => void;
|
setAllowPromptingSubagentSessions: (value: boolean) => void;
|
||||||
@@ -918,6 +923,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
diffWrapLines: false,
|
diffWrapLines: false,
|
||||||
gitChangesViewMode: 'flat',
|
gitChangesViewMode: 'flat',
|
||||||
isTimelineDialogOpen: false,
|
isTimelineDialogOpen: false,
|
||||||
|
isPromptNavigatorPanelOpen: false,
|
||||||
isImagePreviewOpen: false,
|
isImagePreviewOpen: false,
|
||||||
nativeNotificationsEnabled: false,
|
nativeNotificationsEnabled: false,
|
||||||
notificationMode: 'hidden-only',
|
notificationMode: 'hidden-only',
|
||||||
@@ -958,6 +964,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
userMessageRenderingMode: 'markdown',
|
userMessageRenderingMode: 'markdown',
|
||||||
collapsibleUserMessages: true,
|
collapsibleUserMessages: true,
|
||||||
stickyUserHeader: false,
|
stickyUserHeader: false,
|
||||||
|
promptNavigatorEnabled: false,
|
||||||
expandedEditorToolbar: false,
|
expandedEditorToolbar: false,
|
||||||
showSplitAssistantMessageActions: false,
|
showSplitAssistantMessageActions: false,
|
||||||
allowPromptingSubagentSessions: false,
|
allowPromptingSubagentSessions: false,
|
||||||
@@ -2037,6 +2044,14 @@ export const useUIStore = create<UIStore>()(
|
|||||||
set({ isTimelineDialogOpen: open });
|
set({ isTimelineDialogOpen: open });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setPromptNavigatorPanelOpen: (open) => {
|
||||||
|
set({ isPromptNavigatorPanelOpen: open });
|
||||||
|
},
|
||||||
|
|
||||||
|
togglePromptNavigatorPanel: () => {
|
||||||
|
set((state) => ({ isPromptNavigatorPanelOpen: !state.isPromptNavigatorPanelOpen }));
|
||||||
|
},
|
||||||
|
|
||||||
setImagePreviewOpen: (open) => {
|
setImagePreviewOpen: (open) => {
|
||||||
set({ isImagePreviewOpen: open });
|
set({ isImagePreviewOpen: open });
|
||||||
},
|
},
|
||||||
@@ -2119,6 +2134,9 @@ export const useUIStore = create<UIStore>()(
|
|||||||
setStickyUserHeader: (value) => {
|
setStickyUserHeader: (value) => {
|
||||||
set({ stickyUserHeader: value });
|
set({ stickyUserHeader: value });
|
||||||
},
|
},
|
||||||
|
setPromptNavigatorEnabled: (value) => {
|
||||||
|
set({ promptNavigatorEnabled: value });
|
||||||
|
},
|
||||||
setExpandedEditorToolbar: (value: boolean) => {
|
setExpandedEditorToolbar: (value: boolean) => {
|
||||||
set({ expandedEditorToolbar: value });
|
set({ expandedEditorToolbar: value });
|
||||||
},
|
},
|
||||||
@@ -2369,6 +2387,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
userMessageRenderingMode: state.userMessageRenderingMode,
|
userMessageRenderingMode: state.userMessageRenderingMode,
|
||||||
collapsibleUserMessages: state.collapsibleUserMessages,
|
collapsibleUserMessages: state.collapsibleUserMessages,
|
||||||
stickyUserHeader: state.stickyUserHeader,
|
stickyUserHeader: state.stickyUserHeader,
|
||||||
|
promptNavigatorEnabled: state.promptNavigatorEnabled,
|
||||||
expandedEditorToolbar: state.expandedEditorToolbar,
|
expandedEditorToolbar: state.expandedEditorToolbar,
|
||||||
showSplitAssistantMessageActions: state.showSplitAssistantMessageActions,
|
showSplitAssistantMessageActions: state.showSplitAssistantMessageActions,
|
||||||
allowPromptingSubagentSessions: state.allowPromptingSubagentSessions,
|
allowPromptingSubagentSessions: state.allowPromptingSubagentSessions,
|
||||||
|
|||||||
@@ -532,6 +532,9 @@ export const createSettingsHelpers = (dependencies) => {
|
|||||||
if (typeof candidate.stickyUserHeader === 'boolean') {
|
if (typeof candidate.stickyUserHeader === 'boolean') {
|
||||||
result.stickyUserHeader = candidate.stickyUserHeader;
|
result.stickyUserHeader = candidate.stickyUserHeader;
|
||||||
}
|
}
|
||||||
|
if (typeof candidate.promptNavigatorEnabled === 'boolean') {
|
||||||
|
result.promptNavigatorEnabled = candidate.promptNavigatorEnabled;
|
||||||
|
}
|
||||||
if (typeof candidate.expandedEditorToolbar === 'boolean') {
|
if (typeof candidate.expandedEditorToolbar === 'boolean') {
|
||||||
result.expandedEditorToolbar = candidate.expandedEditorToolbar;
|
result.expandedEditorToolbar = candidate.expandedEditorToolbar;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user