Skip to content

Commit 59ea1d2

Browse files
committed
Merge remote-tracking branch 'origin/main' into ACP+SKILLS+CLI
2 parents 818b1dd + 12922c0 commit 59ea1d2

74 files changed

Lines changed: 4928 additions & 829 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/sync.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Sync upstream
2+
3+
on:
4+
schedule:
5+
- cron: '0 */2 * * *' # 每2小时执行一次
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
sync:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout fork
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
ref: main
21+
22+
- name: Configure git
23+
run: |
24+
git config user.name "GitHub Actions"
25+
git config user.email "actions@github.com"
26+
27+
- name: Add upstream repo
28+
run: |
29+
git remote add upstream https://github.com/binaricat/Netcatty.git || true
30+
31+
- name: Fetch upstream
32+
run: git fetch upstream
33+
34+
- name: Merge upstream/main into main
35+
run: git merge upstream/main --no-edit
36+
37+
- name: Push changes
38+
run: git push origin main

App.tsx

Lines changed: 133 additions & 47 deletions
Large diffs are not rendered by default.

application/i18n/locales/en.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const en: Messages = {
2121
'common.clear': 'Clear',
2222
'common.optional': 'Optional',
2323
'common.selectPlaceholder': 'Select...',
24+
'common.add': 'Add',
2425
'common.rename': 'Rename',
2526
'common.refresh': 'Refresh',
2627
'common.continue': 'Continue',
@@ -196,6 +197,9 @@ const en: Messages = {
196197
'settings.application.github.subtitle': 'Source code',
197198
'settings.application.whatsNew': "What's new",
198199
'settings.application.whatsNew.subtitle': 'Show release notes',
200+
'settings.vault.title': 'Vault',
201+
'settings.vault.showRecentHosts': 'Show recently connected hosts',
202+
'settings.vault.showRecentHostsDesc': 'Display a section of recently connected hosts at the top of the vault',
199203

200204
// Update notifications
201205
'update.available.title': 'Update Available',
@@ -231,9 +235,6 @@ const en: Messages = {
231235
'settings.appearance.themeColor.desc': 'Pick a preset palette for each theme',
232236
'settings.appearance.themeColor.light': 'Light palette',
233237
'settings.appearance.themeColor.dark': 'Dark palette',
234-
'settings.appearance.immersiveMode': 'Immersive Mode',
235-
'settings.appearance.immersiveMode.desc':
236-
'When enabled, the UI chrome (tab bar, sidebar, status bar) adapts its colors to match the active terminal theme for a visually cohesive experience.',
237238
'settings.appearance.customCss': 'Custom CSS',
238239
'settings.appearance.customCss.desc':
239240
'Add custom CSS to personalize the app appearance. Changes apply immediately.',
@@ -327,13 +328,26 @@ const en: Messages = {
327328
'settings.terminal.scrollback.rows': 'Number of rows *',
328329
'settings.terminal.keywordHighlight.title': 'Keyword highlighting',
329330
'settings.terminal.keywordHighlight.resetColors': 'Reset to default colors',
331+
'settings.terminal.keywordHighlight.addCustom': 'Add Custom Rule',
332+
'settings.terminal.keywordHighlight.editCustom': 'Edit Rule',
333+
'settings.terminal.keywordHighlight.labelField': 'Label & Color',
334+
'settings.terminal.keywordHighlight.labelPlaceholder': 'Label (e.g., Down)',
335+
'settings.terminal.keywordHighlight.patternField': 'Regex Pattern',
336+
'settings.terminal.keywordHighlight.patternPlaceholder': 'Regex (e.g., \\bdown\\b)',
337+
'settings.terminal.keywordHighlight.invalidPattern': 'Invalid regex pattern',
338+
'settings.terminal.keywordHighlight.preview': 'Preview',
330339
'settings.terminal.section.localShell': 'Local Shell',
331340
'settings.terminal.localShell.shell': 'Shell executable',
332341
'settings.terminal.localShell.shell.desc': 'Path to the shell executable (e.g., /bin/zsh, pwsh.exe). Leave empty for system default.',
333342
'settings.terminal.localShell.shell.placeholder': 'System default',
334343
'settings.terminal.localShell.shell.detected': 'Detected',
335344
'settings.terminal.localShell.shell.notFound': 'Shell executable not found',
336345
'settings.terminal.localShell.shell.isDirectory': 'Path is a directory, not an executable',
346+
'settings.terminal.localShell.shell.default': 'System Default',
347+
'settings.terminal.localShell.shell.custom': 'Custom...',
348+
'settings.terminal.localShell.shell.customPath': 'Shell executable path',
349+
'settings.terminal.localShell.shell.commonPaths': 'Common paths',
350+
'settings.terminal.localShell.shell.pathValid': 'Path valid',
337351
'settings.terminal.localShell.startDir': 'Starting directory',
338352
'settings.terminal.localShell.startDir.desc': 'Directory to start in when opening a local terminal. Leave empty for home directory.',
339353
'settings.terminal.localShell.startDir.placeholder': 'Home directory',
@@ -352,7 +366,7 @@ const en: Messages = {
352366
// Settings > Terminal > Rendering
353367
'settings.terminal.section.rendering': 'Rendering',
354368
'settings.terminal.rendering.renderer': 'Renderer',
355-
'settings.terminal.rendering.renderer.desc': 'Choose the terminal rendering technology. Auto will use Canvas on low-memory devices. Changes take effect on new terminal sessions.',
369+
'settings.terminal.rendering.renderer.desc': 'Choose the terminal rendering technology. Auto will use DOM on low-memory devices. Changes take effect on new terminal sessions.',
356370
'settings.terminal.rendering.auto': 'Auto',
357371

358372
// Settings > Terminal > Workspace Focus Indicator
@@ -461,8 +475,24 @@ const en: Messages = {
461475
'vault.groups.placeholder.example': 'e.g. Production',
462476
'vault.groups.parentLabel': 'Parent',
463477
'vault.groups.pathLabel': 'Path',
478+
'vault.groups.settings': 'Group Settings',
479+
'vault.groups.details': 'Group Details',
480+
'vault.groups.details.general': 'General',
481+
'vault.groups.details.ssh': 'SSH',
482+
'vault.groups.details.telnet': 'Telnet',
483+
'vault.groups.details.advanced': 'Advanced',
484+
'vault.groups.details.appearance': 'Appearance',
485+
'vault.groups.details.mosh': 'Mosh',
486+
'vault.groups.details.parentGroup': 'Parent Group',
487+
'vault.groups.details.none': 'None',
488+
'vault.groups.details.inherited': 'Inherited from group',
489+
'vault.groups.details.addProtocol': 'Add Protocol',
490+
'vault.groups.details.removeProtocol': 'Remove Protocol',
491+
'vault.groups.details.fontFamily': 'Font Family',
492+
'vault.groups.details.fontSize': 'Font Size',
464493
'vault.groups.errors.required': 'Group name is required.',
465494
'vault.groups.errors.invalidChars': "Group name cannot include '/' or '\\\\'.",
495+
'vault.groups.errors.duplicatePath': 'A group with this name already exists at this location.',
466496

467497
'vault.managedSource.unmanage': 'Unmanage',
468498
'vault.managedSource.unmanageSuccess': 'Successfully unmanaged group',
@@ -486,6 +516,10 @@ const en: Messages = {
486516
'vault.hosts.export.toast.successWithSkipped': 'Exported {count} hosts to CSV ({skipped} unsupported hosts skipped)',
487517
'vault.hosts.export.toast.noHosts': 'No hosts to export',
488518
'vault.hosts.allHosts': 'All hosts',
519+
'vault.hosts.pinned': 'Pinned',
520+
'vault.hosts.recentlyConnected': 'Recently Connected',
521+
'vault.hosts.pinToTop': 'Pin to Top',
522+
'vault.hosts.unpin': 'Unpin',
489523
'vault.hosts.copyCredentials': 'Copy Credentials',
490524
'vault.hosts.copyCredentials.toast.success': 'Credentials copied to clipboard',
491525
'vault.hosts.copyCredentials.toast.noPassword': 'No password saved for this host',
@@ -495,6 +529,7 @@ const en: Messages = {
495529
'vault.hosts.deselectAll': 'Deselect All',
496530
'vault.hosts.deleteSelected': 'Delete ({count})',
497531
'vault.hosts.deleteMultiple.success': 'Deleted {count} hosts',
532+
'vault.hosts.moveToGroup.success': 'Moved {host} to {group}',
498533
'vault.hosts.empty.title': 'Set up your hosts',
499534
'vault.hosts.empty.desc': 'Save hosts to quickly connect to your servers, VMs, and containers.',
500535

@@ -882,6 +917,8 @@ const en: Messages = {
882917
'qs.search.placeholder': 'Search hosts or tabs',
883918
'qs.jumpTo': 'Jump To',
884919
'qs.localTerminal': 'Local Terminal',
920+
'qs.localShells': 'Local Shells',
921+
'qs.default': 'Default',
885922

886923
// Select Host panel
887924
'selectHost.title': 'Select Host',
@@ -979,6 +1016,8 @@ const en: Messages = {
9791016
'hostDetails.legacyAlgorithms': 'Allow Legacy Algorithms',
9801017
'hostDetails.legacyAlgorithms.desc': 'Enable deprecated SSH algorithms (diffie-hellman-group1, ssh-dss, 3des-cbc, etc.) for connecting to older network equipment.',
9811018
'hostDetails.legacyAlgorithms.warning': 'These algorithms have known security weaknesses. Only enable for legacy devices that do not support modern cryptography.',
1019+
'hostDetails.backspaceBehavior': 'Backspace Behavior',
1020+
'hostDetails.backspaceBehavior.default': 'Default',
9821021
'hostDetails.jumpHosts': 'Proxy via Hosts',
9831022
'hostDetails.jumpHosts.hops': '{count} hop(s)',
9841023
'hostDetails.jumpHosts.direct': 'Direct',
@@ -1186,6 +1225,7 @@ const en: Messages = {
11861225
'terminal.themeModal.globalTheme': 'Global Theme',
11871226
'terminal.themeModal.globalFont': 'Global Font',
11881227
'terminal.themeModal.fontSize': 'Font Size',
1228+
'terminal.themeModal.fontWeight': 'Font Weight',
11891229
'terminal.themeModal.livePreview': 'Live Preview',
11901230
'terminal.themeModal.themeType': '{type} theme',
11911231

application/i18n/locales/zh-CN.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const zhCN: Messages = {
1313
'common.connect': '连接',
1414
'common.terminal': '终端',
1515
'common.create': '创建',
16+
'common.add': '添加',
1617
'common.rename': '重命名',
1718
'common.refresh': '刷新',
1819
'common.continue': '继续',
@@ -180,6 +181,9 @@ const zhCN: Messages = {
180181
'settings.application.github.subtitle': '源代码',
181182
'settings.application.whatsNew': '更新内容',
182183
'settings.application.whatsNew.subtitle': '查看发布说明',
184+
'settings.vault.title': '主机库',
185+
'settings.vault.showRecentHosts': '显示最近连接的主机',
186+
'settings.vault.showRecentHostsDesc': '在主机列表顶部显示最近连接过的主机',
183187

184188
// Update notifications
185189
'update.available.title': '发现新版本',
@@ -215,9 +219,6 @@ const zhCN: Messages = {
215219
'settings.appearance.themeColor.desc': '为浅色与深色主题选择预设配色',
216220
'settings.appearance.themeColor.light': '浅色主题',
217221
'settings.appearance.themeColor.dark': '深色主题',
218-
'settings.appearance.immersiveMode': '沉浸模式',
219-
'settings.appearance.immersiveMode.desc':
220-
'启用后,UI 外观(标签栏、侧边栏、状态栏)会自动适配当前终端主题的配色,营造视觉一体化的沉浸体验。',
221222
'settings.appearance.customCss': '自定义 CSS',
222223
'settings.appearance.customCss.desc': '使用自定义 CSS 个性化界面,修改会立即生效。',
223224
'settings.appearance.customCss.placeholder':
@@ -294,8 +295,24 @@ const zhCN: Messages = {
294295
'vault.groups.placeholder.example': '例如:Production',
295296
'vault.groups.parentLabel': '父级',
296297
'vault.groups.pathLabel': '路径',
298+
'vault.groups.settings': '分组设置',
299+
'vault.groups.details': '分组详情',
300+
'vault.groups.details.general': '常规',
301+
'vault.groups.details.ssh': 'SSH',
302+
'vault.groups.details.telnet': 'Telnet',
303+
'vault.groups.details.advanced': '高级',
304+
'vault.groups.details.appearance': '外观',
305+
'vault.groups.details.mosh': 'Mosh',
306+
'vault.groups.details.parentGroup': '父分组',
307+
'vault.groups.details.none': '无',
308+
'vault.groups.details.inherited': '继承自分组',
309+
'vault.groups.details.addProtocol': '添加协议',
310+
'vault.groups.details.removeProtocol': '移除协议',
311+
'vault.groups.details.fontFamily': '字体',
312+
'vault.groups.details.fontSize': '字号',
297313
'vault.groups.errors.required': '分组名称不能为空。',
298314
'vault.groups.errors.invalidChars': "分组名称不能包含 '/' 或 '\\\\'.",
315+
'vault.groups.errors.duplicatePath': '该位置已存在同名分组。',
299316

300317
'vault.managedSource.unmanage': '取消托管',
301318
'vault.managedSource.unmanageSuccess': '已取消托管分组',
@@ -319,6 +336,10 @@ const zhCN: Messages = {
319336
'vault.hosts.export.toast.successWithSkipped': '已导出 {count} 个主机到 CSV(跳过 {skipped} 个不支持的主机)',
320337
'vault.hosts.export.toast.noHosts': '没有主机可导出',
321338
'vault.hosts.allHosts': '全部主机',
339+
'vault.hosts.pinned': '已置顶',
340+
'vault.hosts.recentlyConnected': '最近连接',
341+
'vault.hosts.pinToTop': '置顶',
342+
'vault.hosts.unpin': '取消置顶',
322343
'vault.hosts.copyCredentials': '复制账密信息',
323344
'vault.hosts.copyCredentials.toast.success': '账密信息已复制到剪贴板',
324345
'vault.hosts.copyCredentials.toast.noPassword': '该主机未保存密码',
@@ -328,6 +349,7 @@ const zhCN: Messages = {
328349
'vault.hosts.deselectAll': '取消全选',
329350
'vault.hosts.deleteSelected': '删除 ({count})',
330351
'vault.hosts.deleteMultiple.success': '已删除 {count} 个主机',
352+
'vault.hosts.moveToGroup.success': '已将 {host} 移动到 {group}',
331353
'vault.hosts.empty.title': '设置你的主机',
332354
'vault.hosts.empty.desc': '保存主机以快速连接到你的服务器、虚拟机和容器。',
333355

@@ -542,6 +564,8 @@ const zhCN: Messages = {
542564
'qs.search.placeholder': '搜索主机或标签页',
543565
'qs.jumpTo': '跳转到',
544566
'qs.localTerminal': '本地终端',
567+
'qs.localShells': '本地 Shell',
568+
'qs.default': '默认',
545569

546570
// Select Host panel
547571
'selectHost.title': '选择主机',
@@ -635,6 +659,8 @@ const zhCN: Messages = {
635659
'hostDetails.legacyAlgorithms': '允许旧版算法',
636660
'hostDetails.legacyAlgorithms.desc': '启用已弃用的 SSH 算法(diffie-hellman-group1、ssh-dss、3des-cbc 等)以连接老旧网络设备。',
637661
'hostDetails.legacyAlgorithms.warning': '这些算法存在已知安全漏洞,仅建议在老旧设备不支持现代加密时启用。',
662+
'hostDetails.backspaceBehavior': 'Backspace 行为',
663+
'hostDetails.backspaceBehavior.default': '默认',
638664
'hostDetails.jumpHosts': '通过主机代理',
639665
'hostDetails.jumpHosts.hops': '{count} 跳',
640666
'hostDetails.jumpHosts.direct': '直连',
@@ -814,6 +840,7 @@ const zhCN: Messages = {
814840
'terminal.themeModal.globalTheme': '全局主题',
815841
'terminal.themeModal.globalFont': '全局字体',
816842
'terminal.themeModal.fontSize': '字体大小',
843+
'terminal.themeModal.fontWeight': '字体粗细',
817844
'terminal.themeModal.livePreview': '实时预览',
818845
'terminal.themeModal.themeType': '{type} 主题',
819846

@@ -1283,13 +1310,26 @@ const zhCN: Messages = {
12831310
'settings.terminal.scrollback.rows': '行数 *',
12841311
'settings.terminal.keywordHighlight.title': '关键字高亮',
12851312
'settings.terminal.keywordHighlight.resetColors': '重置为默认颜色',
1313+
'settings.terminal.keywordHighlight.addCustom': '添加自定义规则',
1314+
'settings.terminal.keywordHighlight.editCustom': '编辑规则',
1315+
'settings.terminal.keywordHighlight.labelField': '标签与颜色',
1316+
'settings.terminal.keywordHighlight.labelPlaceholder': '标签(如 Down)',
1317+
'settings.terminal.keywordHighlight.patternField': '正则表达式',
1318+
'settings.terminal.keywordHighlight.patternPlaceholder': '正则表达式(如 \\bdown\\b)',
1319+
'settings.terminal.keywordHighlight.invalidPattern': '无效的正则表达式',
1320+
'settings.terminal.keywordHighlight.preview': '预览',
12861321
'settings.terminal.section.localShell': '本地 Shell',
12871322
'settings.terminal.localShell.shell': 'Shell 可执行文件',
12881323
'settings.terminal.localShell.shell.desc': 'Shell 可执行文件的路径(例如 /bin/zsh、pwsh.exe)。留空使用系统默认。',
12891324
'settings.terminal.localShell.shell.placeholder': '系统默认',
12901325
'settings.terminal.localShell.shell.detected': '检测到',
12911326
'settings.terminal.localShell.shell.notFound': '未找到 Shell 可执行文件',
12921327
'settings.terminal.localShell.shell.isDirectory': '路径是目录,不是可执行文件',
1328+
'settings.terminal.localShell.shell.default': '系统默认',
1329+
'settings.terminal.localShell.shell.custom': '自定义...',
1330+
'settings.terminal.localShell.shell.customPath': 'Shell 可执行文件路径',
1331+
'settings.terminal.localShell.shell.commonPaths': '常用路径',
1332+
'settings.terminal.localShell.shell.pathValid': '路径有效',
12931333
'settings.terminal.localShell.startDir': '起始目录',
12941334
'settings.terminal.localShell.startDir.desc': '打开本地终端时的起始目录。留空使用用户主目录。',
12951335
'settings.terminal.localShell.startDir.placeholder': '用户主目录',
@@ -1308,7 +1348,7 @@ const zhCN: Messages = {
13081348
// Settings > Terminal > Rendering
13091349
'settings.terminal.section.rendering': '渲染',
13101350
'settings.terminal.rendering.renderer': '渲染器',
1311-
'settings.terminal.rendering.renderer.desc': '选择终端渲染技术。自动模式会在低内存设备上使用 Canvas。更改将在新终端会话中生效。',
1351+
'settings.terminal.rendering.renderer.desc': '选择终端渲染技术。自动模式会在低内存设备上使用 DOM 渲染。更改将在新终端会话中生效。',
13121352
'settings.terminal.rendering.auto': '自动',
13131353

13141354
// Settings > Terminal > Autocomplete

application/state/fontStore.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ class FontStore {
6868
// Add default fonts first
6969
TERMINAL_FONTS.forEach(font => fontMap.set(font.id, font));
7070

71-
// Add local fonts with a distinct ID namespace to avoid collisions
71+
// Build a set of built-in font family names for dedup (case-insensitive)
72+
const builtinFamilyNames = new Set(
73+
TERMINAL_FONTS.map(f => f.name.toLowerCase())
74+
);
75+
76+
// Add local fonts, skipping those already covered by built-in fonts
7277
localFonts.forEach(font => {
78+
if (builtinFamilyNames.has(font.name.toLowerCase())) return;
7379
const localId = font.id.startsWith('local-') ? font.id : `local-${font.id}`;
7480
fontMap.set(localId, { ...font, id: localId });
7581
});

application/state/useAutoSync.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ interface AutoSyncConfig {
3232
snippetPackages?: SyncPayload['snippetPackages'];
3333
portForwardingRules?: SyncPayload['portForwardingRules'];
3434
knownHosts?: SyncPayload['knownHosts'];
35+
groupConfigs?: SyncPayload['groupConfigs'];
3536
/** Opaque token that changes whenever a synced setting changes. */
3637
settingsVersion?: number;
3738

@@ -95,6 +96,7 @@ export const useAutoSync = (config: AutoSyncConfig) => {
9596
snippetPackages: config.snippetPackages,
9697
portForwardingRules: effectivePFRules,
9798
knownHosts: effectiveKnownHosts,
99+
groupConfigs: config.groupConfigs,
98100
};
99101
}, [
100102
config.hosts,
@@ -105,6 +107,7 @@ export const useAutoSync = (config: AutoSyncConfig) => {
105107
config.snippetPackages,
106108
config.portForwardingRules,
107109
config.knownHosts,
110+
config.groupConfigs,
108111
]);
109112

110113
// Build sync payload

application/state/useImmersiveMode.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,10 @@ function removeImmersiveStyle() {
151151
// ---------------------------------------------------------------------------
152152

153153
export function useImmersiveMode({
154-
isImmersive,
155154
activeTabId,
156155
activeTerminalTheme,
157156
restoreOriginalTheme,
158157
}: {
159-
isImmersive: boolean;
160158
activeTabId: string;
161159
activeTerminalTheme: TerminalTheme | null;
162160
restoreOriginalTheme: () => void;
@@ -170,18 +168,18 @@ export function useImmersiveMode({
170168

171169
// APPLY: useLayoutEffect — runs before paint, O(1) Map lookup, single DOM write
172170
useLayoutEffect(() => {
173-
if (isImmersive && isTerminalTab && activeTerminalTheme) {
171+
if (isTerminalTab && activeTerminalTheme) {
174172
const fp = themeFingerprint(activeTerminalTheme);
175173
if (appliedFpRef.current === fp) return;
176174
overrideActiveRef.current = true;
177175
appliedFpRef.current = fp;
178176
applyImmersiveStyle(getImmersiveCss(activeTerminalTheme), activeTerminalTheme.type === 'dark', activeTerminalTheme.colors.background);
179177
}
180-
}, [isImmersive, isTerminalTab, activeTerminalTheme]);
178+
}, [isTerminalTab, activeTerminalTheme]);
181179

182180
// RESTORE: useEffect — runs after paint, with fade overlay
183181
useEffect(() => {
184-
if (isImmersive && isTerminalTab && activeTerminalTheme) return;
182+
if (isTerminalTab && activeTerminalTheme) return;
185183
if (!overrideActiveRef.current) return;
186184
overrideActiveRef.current = false;
187185
appliedFpRef.current = null;
@@ -198,7 +196,7 @@ export function useImmersiveMode({
198196
});
199197
const fallback = setTimeout(() => { if (overlay.parentNode) overlay.remove(); }, 400);
200198
return () => { clearTimeout(fallback); if (overlay.parentNode) overlay.remove(); };
201-
}, [isImmersive, isTerminalTab, activeTerminalTheme, restoreOriginalTheme]);
199+
}, [isTerminalTab, activeTerminalTheme, restoreOriginalTheme]);
202200

203201
// Cleanup on unmount
204202
useEffect(() => {

0 commit comments

Comments
 (0)