mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
Address terminal encoding review feedback
This commit is contained in:
@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createStoredStringSyncHandlers,
|
||||
readOptionalStoredStringValue,
|
||||
readStoredStringValue,
|
||||
resolveStoredStringUpdate,
|
||||
} from "./useStoredString.ts";
|
||||
@@ -85,12 +86,15 @@ test("stored string helpers read fallback and resolve updater-style toggles", (t
|
||||
|
||||
const storageKey = "netcatty:test-mode";
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "edit");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), null);
|
||||
|
||||
env.storage.setItem(storageKey, "preview");
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "preview");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), "preview");
|
||||
|
||||
env.storage.setItem(storageKey, "invalid");
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "edit");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), null);
|
||||
|
||||
assert.equal(resolveStoredStringUpdate<TestMode>("edit", "preview"), "preview");
|
||||
assert.equal(
|
||||
|
||||
@@ -18,6 +18,15 @@ export const readStoredStringValue = <T extends string>(
|
||||
return isAllowedValue(stored) ? stored : fallback;
|
||||
};
|
||||
|
||||
export const readOptionalStoredStringValue = <T extends string>(
|
||||
storageKey: string,
|
||||
isAllowedValue: (value: string | null) => value is T,
|
||||
): T | null => {
|
||||
if (!canUseLocalStorage()) return null;
|
||||
const stored = localStorageAdapter.readString(storageKey);
|
||||
return isAllowedValue(stored) ? stored : null;
|
||||
};
|
||||
|
||||
export const resolveStoredStringUpdate = <T extends string>(
|
||||
currentValue: T,
|
||||
nextValue: T | ((currentValue: T) => T),
|
||||
|
||||
@@ -44,7 +44,7 @@ import { supportsZmodemTerminalDragDrop } from "../lib/zmodemDragDrop";
|
||||
import { resolveHostAuth } from "../domain/sshAuth";
|
||||
import { useTerminalBackend } from "../application/state/useTerminalBackend";
|
||||
import { useStoredBoolean } from "../application/state/useStoredBoolean";
|
||||
import { readStoredStringValue, useStoredString } from "../application/state/useStoredString";
|
||||
import { readOptionalStoredStringValue, useStoredString } from "../application/state/useStoredString";
|
||||
import { useSessionLogBackend } from "../application/state/useSessionLogBackend";
|
||||
import { useTerminalLayoutSuppressActive } from "../application/state/terminalLayoutSuppressStore";
|
||||
// SFTPModal removed - SFTP is now handled by SftpSidePanel in TerminalLayer
|
||||
@@ -597,9 +597,8 @@ const TerminalComponent: React.FC<TerminalProps> = ({
|
||||
const [terminalEncoding, setTerminalEncoding] = useState<TerminalEncodingPreference>(() => {
|
||||
return resolveInitialTerminalEncoding(
|
||||
host?.charset,
|
||||
readStoredStringValue(
|
||||
readOptionalStoredStringValue(
|
||||
terminalEncodingStorageKey,
|
||||
'utf-8',
|
||||
isTerminalEncodingPreference,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -7,11 +7,18 @@
|
||||
*/
|
||||
import type { Snippet } from "../../../domain/models";
|
||||
import type { CompletionSuggestion } from "./completionEngine";
|
||||
import { snippetAppliesToHost } from "../../../domain/snippetTargets";
|
||||
|
||||
const SNIPPET_BASE_SCORE = 2000; // Above history (1000+freq) per "snippet > history".
|
||||
const SNIPPET_PREFIX_BONUS = 100;
|
||||
|
||||
function snippetAvailableForAutocomplete(snippet: Snippet, hostId?: string): boolean {
|
||||
if (snippet.targetsAllHosts) return true;
|
||||
if (snippet.targets && snippet.targets.length > 0) {
|
||||
return hostId !== undefined && snippet.targets.includes(hostId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getSnippetSuggestions(
|
||||
input: string,
|
||||
snippets: Snippet[],
|
||||
@@ -22,7 +29,7 @@ export function getSnippetSuggestions(
|
||||
|
||||
const out: CompletionSuggestion[] = [];
|
||||
for (const snippet of snippets) {
|
||||
if (!snippetAppliesToHost(snippet, options.hostId)) continue;
|
||||
if (!snippetAvailableForAutocomplete(snippet, options.hostId)) continue;
|
||||
const label = (snippet.label || "").toLowerCase();
|
||||
const firstLine = (snippet.command || "").split("\n")[0].trim().toLowerCase();
|
||||
|
||||
|
||||
@@ -1343,7 +1343,7 @@ export function useTerminalEffects(ctx: TerminalEffectsContext) {
|
||||
|
||||
const handleWindowFocus = () => {
|
||||
if (!shouldRecoverOnAppResume()) return;
|
||||
const term = termRef.current;
|
||||
let term = termRef.current;
|
||||
if (term) {
|
||||
cancelScheduledUnfocusedRepaint(term);
|
||||
forceTerminalRepaintBypassingAnimationFrame(term);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getTerminalSidePanelShellWidth } from './TerminalLayerSidePanelSection'
|
||||
type TerminalLayerEffectsContext = Record<string, any>;
|
||||
|
||||
export function useTerminalLayerEffects(ctx: TerminalLayerEffectsContext) {
|
||||
const { activeSidePanelTab, activeTabId, activeTabIdRef, activeWorkspace, activityTrackedSessions, cancelAnimationFrame, ChunkedEscapeFilter, clearTimeout, document, dropHint, filterTabsMap, focusedSessionId, getSessionActivityIdsToClear, handleToggleAiFromTopBar, handleToggleScriptsSidePanel, handleToggleSidePanel, hasNotifiableTerminalOutput, isComposeBarOpen, isFocusMode, isTerminalLayerVisible, lastSidePanelTabRef, Map, onSessionData, onSplitSessionRef, onToggleBroadcastRef, onToggleWorkspaceViewModeRef, prevFocusedSessionIdRef, refocusActiveTerminalSession, requestAnimationFrame, ResizeObserver, sessionActivityStore, sessions, Set, setAiMountedTabIds, setDropHint, setNotesMountedTabIds, setScriptsMountedTabIds, setSystemMountedTabIds, setSftpHostForTab, setSftpInitialLocationForTab, setSftpPendingUploadsForTab, setSidePanelOpenTabs, setThemeMountedTabIds, setTimeout, setupMcpApprovalBridge, setWorkspaceArea, sidePanelPosition, sidePanelWidth, sftpActiveHost, sftpHostForTab, shouldMarkSessionActivity, sidePanelOpenTabs, splitHorizontalHandlersRef, splitVerticalHandlersRef, terminalRendererCwdBySessionRef, toggleScriptsSidePanelRef, toggleSidePanelRef, validAIScopeTargetIds, validSessionActivityIds, window, workspaceBroadcastHandlersRef, workspaceFocusHandlersRef, workspaceInnerRef, workspaces } = ctx;
|
||||
const { activeSidePanelTab, activeTabId, activeTabIdRef, activeWorkspace, activityTrackedSessions, cancelAnimationFrame, ChunkedEscapeFilter, clearTimeout, clearTopTabsPreviewVars, document, dropHint, filterTabsMap, focusedSessionId, getSessionActivityIdsToClear, handleToggleAiFromTopBar, handleToggleScriptsSidePanel, handleToggleSidePanel, hasNotifiableTerminalOutput, isComposeBarOpen, isFocusMode, isTerminalLayerVisible, lastSidePanelTabRef, Map, onSessionData, onSplitSessionRef, onToggleBroadcastRef, onToggleWorkspaceViewModeRef, prevFocusedSessionIdRef, refocusActiveTerminalSession, requestAnimationFrame, ResizeObserver, sessionActivityStore, sessions, Set, setAiMountedTabIds, setDropHint, setNotesMountedTabIds, setScriptsMountedTabIds, setSystemMountedTabIds, setSftpHostForTab, setSftpInitialLocationForTab, setSftpPendingUploadsForTab, setSidePanelOpenTabs, setThemeMountedTabIds, setTimeout, setupMcpApprovalBridge, setWorkspaceArea, sidePanelPosition, sidePanelWidth, sftpActiveHost, sftpHostForTab, shouldMarkSessionActivity, sidePanelOpenTabs, splitHorizontalHandlersRef, splitVerticalHandlersRef, terminalRendererCwdBySessionRef, toggleScriptsSidePanelRef, toggleSidePanelRef, validAIScopeTargetIds, validSessionActivityIds, window, workspaceBroadcastHandlersRef, workspaceFocusHandlersRef, workspaceInnerRef, workspaces } = ctx;
|
||||
|
||||
const activeWorkspaceId = activeWorkspace?.id;
|
||||
const activeWorkspaceViewMode = activeWorkspace?.viewMode;
|
||||
@@ -60,6 +60,14 @@ export function useTerminalLayerEffects(ctx: TerminalLayerEffectsContext) {
|
||||
});
|
||||
}, [remeasureWorkspaceArea, requestAnimationFrame]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTerminalLayerVisible) {
|
||||
if (typeof clearTopTabsPreviewVars === 'function') {
|
||||
clearTopTabsPreviewVars();
|
||||
}
|
||||
}
|
||||
}, [clearTopTabsPreviewVars, isTerminalLayerVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
const liveSessionIds = new Set(sessions.map((session) => session.id));
|
||||
for (const sessionId of terminalRendererCwdBySessionRef.current.keys()) {
|
||||
|
||||
@@ -24,6 +24,10 @@ test("per-host remembered terminal encoding wins for supported or empty host cha
|
||||
assert.equal(resolveInitialTerminalEncoding(undefined, "gb18030"), "gb18030");
|
||||
});
|
||||
|
||||
test("missing per-host remembered encoding preserves configured GB18030 charset", () => {
|
||||
assert.equal(resolveInitialTerminalEncoding("GB18030", null), "gb18030");
|
||||
});
|
||||
|
||||
test("per-host remembered UTF-8 can override a supported GB18030 host charset", () => {
|
||||
assert.equal(resolveInitialTerminalEncoding("GB18030", "utf-8"), "utf-8");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user