@pheem49/mint 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +3 -3
- package/Cargo.toml +1 -1
- package/README.md +0 -17
- package/Release_Note.md +137 -58
- package/bin/mint +0 -0
- package/crates/mint-cli/src/agent.rs +150 -45
- package/crates/mint-cli/src/image.rs +8 -8
- package/crates/mint-cli/src/main.rs +929 -412
- package/crates/mint-cli/src/onboard.rs +108 -108
- package/crates/mint-cli/src/setup.rs +143 -143
- package/crates/mint-core/src/agent_loop.rs +1 -0
- package/crates/mint-core/src/api_server.rs +108 -52
- package/crates/mint-core/src/browser.rs +56 -1
- package/crates/mint-core/src/channels.rs +1 -0
- package/crates/mint-core/src/chat.rs +126 -8
- package/crates/mint-core/src/code_tools.rs +18 -20
- package/crates/mint-core/src/config.rs +245 -17
- package/crates/mint-core/src/image_gen.rs +5 -5
- package/crates/mint-core/src/lib.rs +16 -2
- package/crates/mint-core/src/memory.rs +6 -1
- package/crates/mint-core/src/orchestration.rs +260 -72
- package/crates/mint-core/src/pictures.rs +3 -4
- package/crates/mint-core/src/semantic.rs +1 -1
- package/crates/mint-core/src/shell.rs +1 -1
- package/crates/mint-core/src/skills.rs +46 -19
- package/crates/mint-core/src/tts.rs +3 -1
- package/flatpak/com.pheem49.mint.desktop +9 -0
- package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
- package/flatpak/com.pheem49.mint.yaml +52 -0
- package/package.json +1 -1
- package/shared-modules/.github/workflows/ci.yml +120 -0
- package/shared-modules/.github/workflows/update.yaml +28 -0
- package/shared-modules/CODEOWNERS +23 -0
- package/shared-modules/README.md +79 -0
- package/shared-modules/SDL/README.md +3 -0
- package/shared-modules/SDL/SDL-1.2.15.json +51 -0
- package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
- package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
- package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
- package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
- package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
- package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
- package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
- package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
- package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
- package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
- package/shared-modules/SDL/sdl12-compat.json +22 -0
- package/shared-modules/SDL2/SDL2_image.json +27 -0
- package/shared-modules/SDL2/SDL2_ttf.json +29 -0
- package/shared-modules/cld2/CMakeLists.txt +157 -0
- package/shared-modules/cld2/cld2.json +24 -0
- package/shared-modules/clutter/clutter.json +72 -0
- package/shared-modules/dbus-glib/dbus-glib.json +29 -0
- package/shared-modules/glew/glew.json +36 -0
- package/shared-modules/glu/glu-9.json +12 -0
- package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
- package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
- package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
- package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
- package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
- package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
- package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
- package/shared-modules/gtk2/gtk2.json +120 -0
- package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
- package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
- package/shared-modules/gtk2/readme.md +10 -0
- package/shared-modules/intltool/intltool-0.51.json +20 -0
- package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
- package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
- package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
- package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
- package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
- package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
- package/shared-modules/lame/lame-3.99.5.json +32 -0
- package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
- package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
- package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
- package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
- package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
- package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
- package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
- package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
- package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
- package/shared-modules/libcanberra/libcanberra.json +44 -0
- package/shared-modules/libmad/libmad.json +19 -0
- package/shared-modules/libsecret/libsecret.json +23 -0
- package/shared-modules/libsoup/libsoup-2.4.json +14 -0
- package/shared-modules/libusb/libusb.json +27 -0
- package/shared-modules/linux-audio/dssi.json +31 -0
- package/shared-modules/linux-audio/fftw3f-static.json +44 -0
- package/shared-modules/linux-audio/fftw3f.json +42 -0
- package/shared-modules/linux-audio/fluidsynth2-static.json +36 -0
- package/shared-modules/linux-audio/fluidsynth2.json +25 -0
- package/shared-modules/linux-audio/ladspa.json +26 -0
- package/shared-modules/linux-audio/lash.json +35 -0
- package/shared-modules/linux-audio/libinstpatch.json +16 -0
- package/shared-modules/linux-audio/liblo-static.json +22 -0
- package/shared-modules/linux-audio/liblo.json +17 -0
- package/shared-modules/linux-audio/lilv-static.json +94 -0
- package/shared-modules/linux-audio/lilv.json +89 -0
- package/shared-modules/linux-audio/lrdf.json +57 -0
- package/shared-modules/linux-audio/lv2.json +20 -0
- package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
- package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
- package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
- package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
- package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
- package/shared-modules/linux-audio/stk.json +28 -0
- package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
- package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
- package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
- package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
- package/shared-modules/lua5.3/lua.pc.in +8 -0
- package/shared-modules/lua5.3/luaconf.patch +47 -0
- package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
- package/shared-modules/lua5.4/lua-5.4.json +41 -0
- package/shared-modules/lua5.4/lua.pc.in +8 -0
- package/shared-modules/lua5.4/luaconf.patch +34 -0
- package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
- package/shared-modules/luajit/luajit.json +30 -0
- package/shared-modules/lzo/lzo.json +27 -0
- package/shared-modules/mac/mac.json +18 -0
- package/shared-modules/physfs/physfs.json +26 -0
- package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
- package/shared-modules/pygame/cython-fixes.patch +34 -0
- package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
- package/shared-modules/pygame/glib.patch +25 -0
- package/shared-modules/pygame/pygame1-python2.json +157 -0
- package/shared-modules/pygtk/pygtk-c99.patch +15 -0
- package/shared-modules/pygtk/pygtk.json +117 -0
- package/shared-modules/pygtk/pygtk.patch +39 -0
- package/shared-modules/python2.7/python-2.7.json +48 -0
- package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
- package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
- package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
- package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
- package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
- package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
- package/shared-modules/update-checker.sh +50 -0
- package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
- package/shared-modules/vorbisgain/0002-errno.patch +33 -0
- package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
- package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
- package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
- package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
- package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
- package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
- package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
- package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
- package/shared-modules/vorbisgain/README.md +23 -0
- package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
- package/src/renderer/shared/agentProgress.ts +145 -0
- package/src/renderer/shared/calculator.ts +52 -0
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
- package/src/renderer/shared/components/AgentActivityTable.tsx +85 -0
- package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
- package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
- package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
- package/src/renderer/shared/constants/models.ts +91 -0
- package/src/renderer/shared/platform.ts +82 -0
- package/src/renderer/shared/types.ts +140 -0
- package/src/renderer/shared/utils/agentActivity.ts +187 -0
- package/src/renderer/shared/utils/approval.ts +1 -0
- package/src/renderer/shared/utils/approval.tsx +64 -0
- package/src/renderer/shared/utils/fileIcons.ts +135 -0
- package/src/renderer/shared/utils/markdown.tsx +347 -0
- package/src/renderer/shared/utils/providers.ts +36 -0
- package/src/renderer/shared/utils/speech.ts +216 -0
- package/src/renderer/shared/utils/ui.ts +147 -0
- package/src/renderer/src/agentProgress.ts +11 -39
- package/src/renderer/src/calculator.ts +3 -46
- package/src/renderer/src/components/ChatPanel.tsx +219 -1308
- package/src/renderer/src/components/MintDashboard.tsx +67 -170
- package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
- package/src/renderer/src/components/Settings/AgentsTab.tsx +381 -0
- package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src/components/Settings/GeneralTab.tsx +256 -2
- package/src/renderer/src/components/SettingsWindow.tsx +120 -3
- package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
- package/src/renderer/src/css/styles.css +100 -0
- package/src/renderer/src/tauri.ts +128 -143
- package/src/renderer/src-web/agentProgress.ts +11 -39
- package/src/renderer/src-web/calculator.ts +3 -46
- package/src/renderer/src-web/components/ChatPanel.tsx +219 -1306
- package/src/renderer/src-web/components/MintDashboard.tsx +42 -170
- package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +381 -0
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +254 -3
- package/src/renderer/src-web/components/SettingsWindow.tsx +127 -3
- package/src/renderer/src-web/css/styles.css +100 -0
- package/src/renderer/src-web/tauri.ts +180 -102
- package/src-tauri/src/desktop.rs +18 -16
- package/src-tauri/src/discord_rpc.rs +1 -1
- package/src-tauri/src/lib.rs +168 -40
- package/src-tauri/src/webhooks.rs +1 -0
- package/src-tauri/src/workflows.rs +1 -0
- package/src-tauri/tauri.conf.json +1 -1
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
DEEPSEEK_MODELS,
|
|
8
8
|
ANTHROPIC_MODELS,
|
|
9
9
|
HF_MODELS,
|
|
10
|
-
LOCAL_MODELS
|
|
10
|
+
LOCAL_MODELS,
|
|
11
|
+
CustomProviderConfig,
|
|
12
|
+
CustomProviderModel,
|
|
13
|
+
CustomProviderHeader,
|
|
11
14
|
} from '../SettingsWindow'
|
|
12
15
|
|
|
13
16
|
interface GeneralTabProps {
|
|
@@ -35,6 +38,7 @@ interface GeneralTabProps {
|
|
|
35
38
|
handleCheckUpdates: () => void
|
|
36
39
|
handleInstallUpdate: () => void
|
|
37
40
|
isDesktopApp?: boolean
|
|
41
|
+
onSaveWithoutClosing?: () => Promise<void>
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
export default function GeneralTab({
|
|
@@ -61,8 +65,18 @@ export default function GeneralTab({
|
|
|
61
65
|
updateMessage,
|
|
62
66
|
handleCheckUpdates,
|
|
63
67
|
handleInstallUpdate,
|
|
64
|
-
isDesktopApp = true
|
|
68
|
+
isDesktopApp = true,
|
|
69
|
+
onSaveWithoutClosing
|
|
65
70
|
}: GeneralTabProps) {
|
|
71
|
+
const [savedProviderId, setSavedProviderId] = React.useState<string | null>(null)
|
|
72
|
+
|
|
73
|
+
const handleSaveProvider = async (providerId: string) => {
|
|
74
|
+
if (onSaveWithoutClosing) {
|
|
75
|
+
await onSaveWithoutClosing()
|
|
76
|
+
setSavedProviderId(providerId)
|
|
77
|
+
setTimeout(() => setSavedProviderId(null), 2000)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
66
80
|
return (
|
|
67
81
|
<div className="tab-pane active">
|
|
68
82
|
<section className="setting-section">
|
|
@@ -86,8 +100,38 @@ export default function GeneralTab({
|
|
|
86
100
|
<option value="ollama">Ollama (Local / Private)</option>
|
|
87
101
|
<option value="huggingface">Hugging Face (Inference API)</option>
|
|
88
102
|
<option value="local_openai">Local (LM Studio / OpenAI Compatible)</option>
|
|
103
|
+
{(config.customProviders ?? []).map(cp => (
|
|
104
|
+
<option key={`custom:${cp.id}`} value={`custom:${cp.id}`}>
|
|
105
|
+
{cp.displayName || cp.id} (Custom)
|
|
106
|
+
</option>
|
|
107
|
+
))}
|
|
89
108
|
</select>
|
|
90
109
|
</div>
|
|
110
|
+
{/* Model selector for custom providers */}
|
|
111
|
+
{config.aiProvider.startsWith('custom:') && (() => {
|
|
112
|
+
const activeId = config.aiProvider.replace(/^custom:/, '')
|
|
113
|
+
const cp = (config.customProviders ?? []).find(p => p.id === activeId)
|
|
114
|
+
if (!cp || cp.models.length === 0) return null
|
|
115
|
+
const currentModel = (config.customModelSelections ?? {})[activeId] ?? cp.models[0]?.modelId ?? ''
|
|
116
|
+
return (
|
|
117
|
+
<div className="setting-row wide">
|
|
118
|
+
<label>Active Model</label>
|
|
119
|
+
<select
|
|
120
|
+
value={currentModel}
|
|
121
|
+
onChange={(e) => updateField('customModelSelections', {
|
|
122
|
+
...(config.customModelSelections ?? {}),
|
|
123
|
+
[activeId]: e.target.value
|
|
124
|
+
})}
|
|
125
|
+
>
|
|
126
|
+
{cp.models.map(m => (
|
|
127
|
+
<option key={m.modelId} value={m.modelId}>
|
|
128
|
+
{m.displayName || m.modelId}
|
|
129
|
+
</option>
|
|
130
|
+
))}
|
|
131
|
+
</select>
|
|
132
|
+
</div>
|
|
133
|
+
)
|
|
134
|
+
})()}
|
|
91
135
|
</div>
|
|
92
136
|
|
|
93
137
|
<div className="provider-cards-container">
|
|
@@ -877,6 +921,216 @@ export default function GeneralTab({
|
|
|
877
921
|
</div>
|
|
878
922
|
</section>
|
|
879
923
|
|
|
924
|
+
{/* ─── Custom Providers ─────────────────────────────────────────────────── */}
|
|
925
|
+
<section className="setting-section">
|
|
926
|
+
<div className="section-heading">
|
|
927
|
+
<div>
|
|
928
|
+
<p className="section-kicker">Custom AI Routing</p>
|
|
929
|
+
<h2 className="section-title">Custom Providers</h2>
|
|
930
|
+
</div>
|
|
931
|
+
<p className="section-description">
|
|
932
|
+
Add any OpenAI-compatible endpoint as a custom provider. Configure its models and optional headers.
|
|
933
|
+
</p>
|
|
934
|
+
</div>
|
|
935
|
+
|
|
936
|
+
<div className="provider-cards-container">
|
|
937
|
+
{(config.customProviders ?? []).map((cp, cpIdx) => {
|
|
938
|
+
const isActive = config.aiProvider === `custom:${cp.id}`
|
|
939
|
+
|
|
940
|
+
const updateCp = (patch: Partial<CustomProviderConfig>) => {
|
|
941
|
+
const updated = (config.customProviders ?? []).map((p, i) =>
|
|
942
|
+
i === cpIdx ? { ...p, ...patch } : p
|
|
943
|
+
)
|
|
944
|
+
updateField('customProviders', updated)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const deleteCp = () => {
|
|
948
|
+
const updated = (config.customProviders ?? []).filter((_, i) => i !== cpIdx)
|
|
949
|
+
updateField('customProviders', updated)
|
|
950
|
+
// If this was the active provider, reset to gemini
|
|
951
|
+
if (config.aiProvider === `custom:${cp.id}`) {
|
|
952
|
+
updateField('aiProvider', 'gemini')
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const updateModel = (mIdx: number, patch: Partial<CustomProviderModel>) => {
|
|
957
|
+
const updatedModels = cp.models.map((m, i) => i === mIdx ? { ...m, ...patch } : m)
|
|
958
|
+
updateCp({ models: updatedModels })
|
|
959
|
+
}
|
|
960
|
+
const deleteModel = (mIdx: number) => {
|
|
961
|
+
updateCp({ models: cp.models.filter((_, i) => i !== mIdx) })
|
|
962
|
+
}
|
|
963
|
+
const addModel = () => {
|
|
964
|
+
updateCp({ models: [...cp.models, { modelId: '', displayName: '' }] })
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const updateHeader = (hIdx: number, patch: Partial<CustomProviderHeader>) => {
|
|
968
|
+
const updatedHeaders = cp.headers.map((h, i) => i === hIdx ? { ...h, ...patch } : h)
|
|
969
|
+
updateCp({ headers: updatedHeaders })
|
|
970
|
+
}
|
|
971
|
+
const deleteHeader = (hIdx: number) => {
|
|
972
|
+
updateCp({ headers: cp.headers.filter((_, i) => i !== hIdx) })
|
|
973
|
+
}
|
|
974
|
+
const addHeader = () => {
|
|
975
|
+
updateCp({ headers: [...cp.headers, { name: '', value: '' }] })
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
return (
|
|
979
|
+
<div key={cpIdx} className={`provider-card ${isActive ? 'active-provider' : ''}`}>
|
|
980
|
+
<div className="provider-card-header">
|
|
981
|
+
<div className="provider-card-title">
|
|
982
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
983
|
+
<circle cx="12" cy="12" r="3" /><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14" />
|
|
984
|
+
</svg>
|
|
985
|
+
{cp.displayName || cp.id || 'Unnamed Provider'}
|
|
986
|
+
</div>
|
|
987
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
988
|
+
{isActive && <span className="provider-active-badge">Active</span>}
|
|
989
|
+
<button
|
|
990
|
+
className="btn btn-primary"
|
|
991
|
+
style={{ padding: '3px 8px', fontSize: '12px', background: savedProviderId === cp.id ? '#10b981' : '' }}
|
|
992
|
+
onClick={() => handleSaveProvider(cp.id)}
|
|
993
|
+
title="Save this provider settings"
|
|
994
|
+
>
|
|
995
|
+
{savedProviderId === cp.id ? 'Saved! ✓' : 'Save'}
|
|
996
|
+
</button>
|
|
997
|
+
<button
|
|
998
|
+
className="btn btn-danger"
|
|
999
|
+
style={{ padding: '3px 8px', fontSize: '12px' }}
|
|
1000
|
+
onClick={deleteCp}
|
|
1001
|
+
title="Delete this provider"
|
|
1002
|
+
>
|
|
1003
|
+
Delete
|
|
1004
|
+
</button>
|
|
1005
|
+
</div>
|
|
1006
|
+
</div>
|
|
1007
|
+
|
|
1008
|
+
<div className="provider-card-body">
|
|
1009
|
+
<div className="setting-row">
|
|
1010
|
+
<label>Name</label>
|
|
1011
|
+
<input
|
|
1012
|
+
type="text"
|
|
1013
|
+
value={cp.displayName}
|
|
1014
|
+
onChange={(e) => {
|
|
1015
|
+
const name = e.target.value
|
|
1016
|
+
const computedId = name
|
|
1017
|
+
.toLowerCase()
|
|
1018
|
+
.replace(/[^a-z0-9_-]/g, '-')
|
|
1019
|
+
.replace(/-+/g, '-')
|
|
1020
|
+
.replace(/^-|-$/g, '') || `provider-${cpIdx + 1}`
|
|
1021
|
+
updateCp({ displayName: name, id: computedId })
|
|
1022
|
+
}}
|
|
1023
|
+
placeholder="e.g. DeepSeek"
|
|
1024
|
+
/>
|
|
1025
|
+
</div>
|
|
1026
|
+
|
|
1027
|
+
{/* Endpoint */}
|
|
1028
|
+
<div className="setting-row">
|
|
1029
|
+
<label>Base URL</label>
|
|
1030
|
+
<input
|
|
1031
|
+
type="text"
|
|
1032
|
+
value={cp.baseUrl}
|
|
1033
|
+
onChange={(e) => updateCp({ baseUrl: e.target.value })}
|
|
1034
|
+
placeholder="https://api.myprovider.com/v1"
|
|
1035
|
+
/>
|
|
1036
|
+
</div>
|
|
1037
|
+
|
|
1038
|
+
<div className="setting-row">
|
|
1039
|
+
<label>API Key</label>
|
|
1040
|
+
<input
|
|
1041
|
+
type="password"
|
|
1042
|
+
value={cp.apiKey}
|
|
1043
|
+
onChange={(e) => updateCp({ apiKey: e.target.value })}
|
|
1044
|
+
placeholder="Optional. Leave empty if you manage auth via headers."
|
|
1045
|
+
/>
|
|
1046
|
+
</div>
|
|
1047
|
+
|
|
1048
|
+
{/* Models */}
|
|
1049
|
+
<div style={{ marginTop: '12px' }}>
|
|
1050
|
+
<label style={{ fontWeight: 600, fontSize: '13px', display: 'block', marginBottom: '8px' }}>Model list</label>
|
|
1051
|
+
{cp.models.map((m, mIdx) => (
|
|
1052
|
+
<div key={mIdx} style={{ display: 'flex', gap: '8px', marginBottom: '6px', alignItems: 'center' }}>
|
|
1053
|
+
<input
|
|
1054
|
+
type="text"
|
|
1055
|
+
value={m.modelId}
|
|
1056
|
+
onChange={(e) => updateModel(mIdx, { modelId: e.target.value, displayName: e.target.value })}
|
|
1057
|
+
placeholder="e.g. llama-3.1-8b"
|
|
1058
|
+
style={{ flex: 1 }}
|
|
1059
|
+
/>
|
|
1060
|
+
<button
|
|
1061
|
+
className="btn btn-danger"
|
|
1062
|
+
style={{ padding: '4px 8px', fontSize: '12px', flexShrink: 0 }}
|
|
1063
|
+
onClick={() => deleteModel(mIdx)}
|
|
1064
|
+
title="Remove model"
|
|
1065
|
+
>🗑</button>
|
|
1066
|
+
</div>
|
|
1067
|
+
))}
|
|
1068
|
+
<button
|
|
1069
|
+
className="btn btn-secondary"
|
|
1070
|
+
style={{ fontSize: '12px', marginTop: '4px' }}
|
|
1071
|
+
onClick={addModel}
|
|
1072
|
+
>
|
|
1073
|
+
+ Add model
|
|
1074
|
+
</button>
|
|
1075
|
+
</div>
|
|
1076
|
+
|
|
1077
|
+
{/* Headers */}
|
|
1078
|
+
<div style={{ marginTop: '12px' }}>
|
|
1079
|
+
<label style={{ fontWeight: 600, fontSize: '13px', display: 'block', marginBottom: '8px' }}>Headers (optional)</label>
|
|
1080
|
+
{cp.headers.map((h, hIdx) => (
|
|
1081
|
+
<div key={hIdx} style={{ display: 'flex', gap: '8px', marginBottom: '6px', alignItems: 'center' }}>
|
|
1082
|
+
<input
|
|
1083
|
+
type="text"
|
|
1084
|
+
value={h.name}
|
|
1085
|
+
onChange={(e) => updateHeader(hIdx, { name: e.target.value })}
|
|
1086
|
+
placeholder="Header-Name"
|
|
1087
|
+
style={{ flex: 1 }}
|
|
1088
|
+
/>
|
|
1089
|
+
<input
|
|
1090
|
+
type="text"
|
|
1091
|
+
value={h.value}
|
|
1092
|
+
onChange={(e) => updateHeader(hIdx, { value: e.target.value })}
|
|
1093
|
+
placeholder="value"
|
|
1094
|
+
style={{ flex: 1 }}
|
|
1095
|
+
/>
|
|
1096
|
+
<button
|
|
1097
|
+
className="btn btn-danger"
|
|
1098
|
+
style={{ padding: '4px 8px', fontSize: '12px', flexShrink: 0 }}
|
|
1099
|
+
onClick={() => deleteHeader(hIdx)}
|
|
1100
|
+
title="Remove header"
|
|
1101
|
+
>🗑</button>
|
|
1102
|
+
</div>
|
|
1103
|
+
))}
|
|
1104
|
+
<button
|
|
1105
|
+
className="btn btn-secondary"
|
|
1106
|
+
style={{ fontSize: '12px', marginTop: '4px' }}
|
|
1107
|
+
onClick={addHeader}
|
|
1108
|
+
>
|
|
1109
|
+
+ Add header
|
|
1110
|
+
</button>
|
|
1111
|
+
</div>
|
|
1112
|
+
</div>
|
|
1113
|
+
</div>
|
|
1114
|
+
)
|
|
1115
|
+
})}
|
|
1116
|
+
</div>
|
|
1117
|
+
|
|
1118
|
+
<div className="setting-actions" style={{ marginTop: '16px' }}>
|
|
1119
|
+
<button
|
|
1120
|
+
className="btn btn-secondary"
|
|
1121
|
+
onClick={() => {
|
|
1122
|
+
const newId = `provider-${(config.customProviders ?? []).length + 1}`
|
|
1123
|
+
updateField('customProviders', [
|
|
1124
|
+
...(config.customProviders ?? []),
|
|
1125
|
+
{ id: newId, displayName: '', baseUrl: '', apiKey: '', models: [], headers: [] }
|
|
1126
|
+
])
|
|
1127
|
+
}}
|
|
1128
|
+
>
|
|
1129
|
+
+ Add custom provider
|
|
1130
|
+
</button>
|
|
1131
|
+
</div>
|
|
1132
|
+
</section>
|
|
1133
|
+
|
|
880
1134
|
|
|
881
1135
|
{isDesktopApp && (
|
|
882
1136
|
<section className="setting-section">
|
|
@@ -6,6 +6,28 @@ import AudioTab from './Settings/AudioTab'
|
|
|
6
6
|
import AutomationTab from './Settings/AutomationTab'
|
|
7
7
|
import ThemeTab from './Settings/ThemeTab'
|
|
8
8
|
import PluginsTab from './Settings/PluginsTab'
|
|
9
|
+
import AgentsTab from './Settings/AgentsTab'
|
|
10
|
+
|
|
11
|
+
// ─── Custom Provider Types ────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface CustomProviderModel {
|
|
14
|
+
modelId: string
|
|
15
|
+
displayName: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CustomProviderHeader {
|
|
19
|
+
name: string
|
|
20
|
+
value: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CustomProviderConfig {
|
|
24
|
+
id: string
|
|
25
|
+
displayName: string
|
|
26
|
+
baseUrl: string
|
|
27
|
+
apiKey: string
|
|
28
|
+
models: CustomProviderModel[]
|
|
29
|
+
headers: CustomProviderHeader[]
|
|
30
|
+
}
|
|
9
31
|
|
|
10
32
|
export const DEFAULT_CONFIG = {
|
|
11
33
|
theme: 'dark',
|
|
@@ -30,7 +52,7 @@ export const DEFAULT_CONFIG = {
|
|
|
30
52
|
proactiveCooldown: 120,
|
|
31
53
|
enableVoiceReply: true,
|
|
32
54
|
enableCustomWorkflows: true,
|
|
33
|
-
enableAgentCollaboration:
|
|
55
|
+
enableAgentCollaboration: false,
|
|
34
56
|
ttsProvider: 'google',
|
|
35
57
|
ttsVolume: 1.0,
|
|
36
58
|
ttsSpeed: 1.0,
|
|
@@ -91,10 +113,44 @@ export const DEFAULT_CONFIG = {
|
|
|
91
113
|
imageGenProvider: 'gemini' as 'gemini' | 'dalle' | 'stability' | 'ideogram' | 'replicate',
|
|
92
114
|
stabilityApiKey: '',
|
|
93
115
|
ideogramApiKey: '',
|
|
94
|
-
replicateApiKey: ''
|
|
116
|
+
replicateApiKey: '',
|
|
117
|
+
// Multi-Agent Configuration
|
|
118
|
+
agents: [
|
|
119
|
+
{
|
|
120
|
+
id: 'planner',
|
|
121
|
+
name: 'Planner',
|
|
122
|
+
provider: 'gemini',
|
|
123
|
+
model: 'gemini-2.5-flash',
|
|
124
|
+
apiKey: '',
|
|
125
|
+
systemInstruction: 'You are the Planner agent. Your task is to analyze the user request, inspect the workspace structure, and design a step-by-step implementation plan. Create or update the implementation_plan.md file to document your proposal.',
|
|
126
|
+
enabled: true
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'coder',
|
|
130
|
+
name: 'Coder',
|
|
131
|
+
provider: 'gemini',
|
|
132
|
+
model: 'gemini-2.5-flash',
|
|
133
|
+
apiKey: '',
|
|
134
|
+
systemInstruction: 'You are the Coder agent. Your task is to implement the changes specified in the approved implementation plan. Read relevant files, write clean and efficient code, and run terminal commands to build or verify. Stay focused on execution.',
|
|
135
|
+
enabled: true
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'reviewer',
|
|
139
|
+
name: 'Reviewer',
|
|
140
|
+
provider: 'gemini',
|
|
141
|
+
model: 'gemini-2.5-flash',
|
|
142
|
+
apiKey: '',
|
|
143
|
+
systemInstruction: 'You are the Reviewer agent. Your task is to verify the code modifications made by the Coder. Run the automated tests, check lint errors, and ensure the implementation is correct and complete.',
|
|
144
|
+
enabled: true
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
// Custom Providers
|
|
148
|
+
customProviders: [] as CustomProviderConfig[],
|
|
149
|
+
/// Per-provider model selection: { [providerId]: selectedModelId }
|
|
150
|
+
customModelSelections: {} as Record<string, string>,
|
|
95
151
|
}
|
|
96
152
|
|
|
97
|
-
type TabType = 'sect-general' | 'sect-audio' | 'sect-automation' | 'sect-theme' | 'sect-plugins' | 'sect-shortcuts' | 'sect-memory'
|
|
153
|
+
type TabType = 'sect-general' | 'sect-audio' | 'sect-automation' | 'sect-theme' | 'sect-plugins' | 'sect-shortcuts' | 'sect-memory' | 'sect-agents'
|
|
98
154
|
|
|
99
155
|
export const GEMINI_MODELS = [
|
|
100
156
|
'gemini-2.5-flash',
|
|
@@ -370,6 +426,47 @@ export default function SettingsWindow() {
|
|
|
370
426
|
}
|
|
371
427
|
}
|
|
372
428
|
|
|
429
|
+
const handleSaveWithoutClosing = async () => {
|
|
430
|
+
const finalConfig = { ...config }
|
|
431
|
+
|
|
432
|
+
if (config.geminiModel === 'custom') {
|
|
433
|
+
finalConfig.geminiModel = customGemini || 'gemini-2.5-flash'
|
|
434
|
+
}
|
|
435
|
+
if (config.openaiModel === 'custom') {
|
|
436
|
+
finalConfig.openaiModel = customOpenAI || 'gpt-4o'
|
|
437
|
+
}
|
|
438
|
+
if (config.openrouterModel === 'custom') {
|
|
439
|
+
finalConfig.openrouterModel = customOpenRouter || 'openai/gpt-4o-mini'
|
|
440
|
+
}
|
|
441
|
+
if (config.deepseekModel === 'custom') {
|
|
442
|
+
finalConfig.deepseekModel = customDeepSeek || 'deepseek-v4-flash'
|
|
443
|
+
}
|
|
444
|
+
if (config.anthropicModel === 'custom') {
|
|
445
|
+
finalConfig.anthropicModel = customAnthropic || 'claude-3-5-sonnet-latest'
|
|
446
|
+
}
|
|
447
|
+
if (config.hfModel === 'custom') {
|
|
448
|
+
finalConfig.hfModel = customHF || 'meta-llama/Meta-Llama-3-8B-Instruct'
|
|
449
|
+
}
|
|
450
|
+
if (config.localModelName === 'custom') {
|
|
451
|
+
finalConfig.localModelName = customLocal || 'local-model'
|
|
452
|
+
}
|
|
453
|
+
if (config.ollamaModel === 'custom') {
|
|
454
|
+
finalConfig.ollamaModel = customOllama || 'llama3:latest'
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
await setProfileValue('name', userName)
|
|
459
|
+
await setProfileValue('preferences', userPreferences)
|
|
460
|
+
} catch (e) {
|
|
461
|
+
console.error("Failed to save user profile memory:", e)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (window.settingsApi) {
|
|
465
|
+
await window.settingsApi.saveSettings(finalConfig)
|
|
466
|
+
applyThemeStyles(finalConfig)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
373
470
|
const handleReset = async () => {
|
|
374
471
|
if (confirm('Reset all settings to default?')) {
|
|
375
472
|
setConfig(DEFAULT_CONFIG)
|
|
@@ -586,6 +683,17 @@ export default function SettingsWindow() {
|
|
|
586
683
|
</span>
|
|
587
684
|
<strong>Plugins</strong>
|
|
588
685
|
</button>
|
|
686
|
+
<button className={`tab-btn ${activeTab === 'sect-agents' ? 'active' : ''}`} onClick={() => setActiveTab('sect-agents')}>
|
|
687
|
+
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
|
|
688
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
689
|
+
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
|
690
|
+
<circle cx="9" cy="7" r="4"></circle>
|
|
691
|
+
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
|
692
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
|
693
|
+
</svg>
|
|
694
|
+
</span>
|
|
695
|
+
<strong>Multi-Agent (Beta)</strong>
|
|
696
|
+
</button>
|
|
589
697
|
</nav>
|
|
590
698
|
|
|
591
699
|
<div className="settings-content">
|
|
@@ -614,6 +722,7 @@ export default function SettingsWindow() {
|
|
|
614
722
|
updateMessage={updateMessage}
|
|
615
723
|
handleCheckUpdates={handleCheckUpdates}
|
|
616
724
|
handleInstallUpdate={handleInstallUpdate}
|
|
725
|
+
onSaveWithoutClosing={handleSaveWithoutClosing}
|
|
617
726
|
/>
|
|
618
727
|
)}
|
|
619
728
|
|
|
@@ -666,6 +775,14 @@ export default function SettingsWindow() {
|
|
|
666
775
|
handleConnectPlugin={handleConnectPlugin}
|
|
667
776
|
/>
|
|
668
777
|
)}
|
|
778
|
+
|
|
779
|
+
{activeTab === 'sect-agents' && (
|
|
780
|
+
<AgentsTab
|
|
781
|
+
config={config}
|
|
782
|
+
updateField={updateField}
|
|
783
|
+
dynamicOllamaModels={dynamicOllamaModels}
|
|
784
|
+
/>
|
|
785
|
+
)}
|
|
669
786
|
</div>
|
|
670
787
|
</main>
|
|
671
788
|
|
|
@@ -1,60 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react'
|
|
2
2
|
import { getWorkspaceTree, type WorkspaceTreeEntry, createWorkspaceFile, createWorkspaceFolder, deleteWorkspaceItem } from '../tauri'
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import folderDistOpenIcon from 'material-icon-theme/icons/folder-dist-open.svg?url'
|
|
11
|
-
import folderDocsIcon from 'material-icon-theme/icons/folder-docs.svg?url'
|
|
12
|
-
import folderDocsOpenIcon from 'material-icon-theme/icons/folder-docs-open.svg?url'
|
|
13
|
-
import folderGitIcon from 'material-icon-theme/icons/folder-git.svg?url'
|
|
14
|
-
import folderGitOpenIcon from 'material-icon-theme/icons/folder-git-open.svg?url'
|
|
15
|
-
import folderGithubIcon from 'material-icon-theme/icons/folder-github.svg?url'
|
|
16
|
-
import folderGithubOpenIcon from 'material-icon-theme/icons/folder-github-open.svg?url'
|
|
17
|
-
import folderImagesIcon from 'material-icon-theme/icons/folder-images.svg?url'
|
|
18
|
-
import folderImagesOpenIcon from 'material-icon-theme/icons/folder-images-open.svg?url'
|
|
19
|
-
import folderLogIcon from 'material-icon-theme/icons/folder-log.svg?url'
|
|
20
|
-
import folderLogOpenIcon from 'material-icon-theme/icons/folder-log-open.svg?url'
|
|
21
|
-
import folderNodeIcon from 'material-icon-theme/icons/folder-node.svg?url'
|
|
22
|
-
import folderNodeOpenIcon from 'material-icon-theme/icons/folder-node-open.svg?url'
|
|
23
|
-
import folderPublicIcon from 'material-icon-theme/icons/folder-public.svg?url'
|
|
24
|
-
import folderPublicOpenIcon from 'material-icon-theme/icons/folder-public-open.svg?url'
|
|
25
|
-
import folderRustIcon from 'material-icon-theme/icons/folder-rust.svg?url'
|
|
26
|
-
import folderRustOpenIcon from 'material-icon-theme/icons/folder-rust-open.svg?url'
|
|
27
|
-
import folderScriptsIcon from 'material-icon-theme/icons/folder-scripts.svg?url'
|
|
28
|
-
import folderScriptsOpenIcon from 'material-icon-theme/icons/folder-scripts-open.svg?url'
|
|
29
|
-
import folderSrcIcon from 'material-icon-theme/icons/folder-src.svg?url'
|
|
30
|
-
import folderSrcOpenIcon from 'material-icon-theme/icons/folder-src-open.svg?url'
|
|
31
|
-
import folderSrcTauriIcon from 'material-icon-theme/icons/folder-src-tauri.svg?url'
|
|
32
|
-
import folderSrcTauriOpenIcon from 'material-icon-theme/icons/folder-src-tauri-open.svg?url'
|
|
33
|
-
import folderTestIcon from 'material-icon-theme/icons/folder-test.svg?url'
|
|
34
|
-
import folderTestOpenIcon from 'material-icon-theme/icons/folder-test-open.svg?url'
|
|
35
|
-
import folderUiIcon from 'material-icon-theme/icons/folder-ui.svg?url'
|
|
36
|
-
import folderUiOpenIcon from 'material-icon-theme/icons/folder-ui-open.svg?url'
|
|
37
|
-
import folderUtilsIcon from 'material-icon-theme/icons/folder-utils.svg?url'
|
|
38
|
-
import folderUtilsOpenIcon from 'material-icon-theme/icons/folder-utils-open.svg?url'
|
|
39
|
-
import cssIcon from 'material-icon-theme/icons/css.svg?url'
|
|
40
|
-
import documentIcon from 'material-icon-theme/icons/document.svg?url'
|
|
41
|
-
import htmlIcon from 'material-icon-theme/icons/html.svg?url'
|
|
42
|
-
import imageIcon from 'material-icon-theme/icons/image.svg?url'
|
|
43
|
-
import javascriptIcon from 'material-icon-theme/icons/javascript.svg?url'
|
|
44
|
-
import jsonIcon from 'material-icon-theme/icons/json.svg?url'
|
|
45
|
-
import lockIcon from 'material-icon-theme/icons/lock.svg?url'
|
|
46
|
-
import logIcon from 'material-icon-theme/icons/log.svg?url'
|
|
47
|
-
import markdownIcon from 'material-icon-theme/icons/markdown.svg?url'
|
|
48
|
-
import npmIcon from 'material-icon-theme/icons/npm.svg?url'
|
|
49
|
-
import reactIcon from 'material-icon-theme/icons/react.svg?url'
|
|
50
|
-
import reactTsIcon from 'material-icon-theme/icons/react_ts.svg?url'
|
|
51
|
-
import rustIcon from 'material-icon-theme/icons/rust.svg?url'
|
|
52
|
-
import settingsIcon from 'material-icon-theme/icons/settings.svg?url'
|
|
53
|
-
import tauriIcon from 'material-icon-theme/icons/tauri.svg?url'
|
|
54
|
-
import tomlIcon from 'material-icon-theme/icons/toml.svg?url'
|
|
55
|
-
import typescriptIcon from 'material-icon-theme/icons/typescript.svg?url'
|
|
56
|
-
import viteIcon from 'material-icon-theme/icons/vite.svg?url'
|
|
57
|
-
import yamlIcon from 'material-icon-theme/icons/yaml.svg?url'
|
|
3
|
+
import {
|
|
4
|
+
materialFolderIcon,
|
|
5
|
+
materialFileIcon,
|
|
6
|
+
getExtension as extension,
|
|
7
|
+
folderOpenIcon
|
|
8
|
+
} from '../../shared/utils/fileIcons'
|
|
9
|
+
|
|
58
10
|
|
|
59
11
|
interface WorkspacePanelProps {
|
|
60
12
|
agentMode: boolean
|
|
@@ -76,76 +28,6 @@ const FILE_LABEL: Record<string, string> = {
|
|
|
76
28
|
tsx: 'TS',
|
|
77
29
|
}
|
|
78
30
|
|
|
79
|
-
const FOLDER_ICONS: Record<string, { closed: string; open: string }> = {
|
|
80
|
-
'.cargo_home': { closed: folderRustIcon, open: folderRustOpenIcon },
|
|
81
|
-
'.github': { closed: folderGithubIcon, open: folderGithubOpenIcon },
|
|
82
|
-
'.git': { closed: folderGitIcon, open: folderGitOpenIcon },
|
|
83
|
-
'.rustup': { closed: folderRustIcon, open: folderRustOpenIcon },
|
|
84
|
-
'.rustup_copy': { closed: folderRustIcon, open: folderRustOpenIcon },
|
|
85
|
-
'.rustup_home': { closed: folderRustIcon, open: folderRustOpenIcon },
|
|
86
|
-
assets: { closed: folderImagesIcon, open: folderImagesOpenIcon },
|
|
87
|
-
components: { closed: folderComponentsIcon, open: folderComponentsOpenIcon },
|
|
88
|
-
crates: { closed: folderRustIcon, open: folderRustOpenIcon },
|
|
89
|
-
css: { closed: folderUiIcon, open: folderUiOpenIcon },
|
|
90
|
-
dist: { closed: folderDistIcon, open: folderDistOpenIcon },
|
|
91
|
-
docs: { closed: folderDocsIcon, open: folderDocsOpenIcon },
|
|
92
|
-
logs: { closed: folderLogIcon, open: folderLogOpenIcon },
|
|
93
|
-
node_modules: { closed: folderNodeIcon, open: folderNodeOpenIcon },
|
|
94
|
-
out: { closed: folderDistIcon, open: folderDistOpenIcon },
|
|
95
|
-
public: { closed: folderPublicIcon, open: folderPublicOpenIcon },
|
|
96
|
-
renderer: { closed: folderUiIcon, open: folderUiOpenIcon },
|
|
97
|
-
scripts: { closed: folderScriptsIcon, open: folderScriptsOpenIcon },
|
|
98
|
-
src: { closed: folderSrcIcon, open: folderSrcOpenIcon },
|
|
99
|
-
'src-tauri': { closed: folderSrcTauriIcon, open: folderSrcTauriOpenIcon },
|
|
100
|
-
tests: { closed: folderTestIcon, open: folderTestOpenIcon },
|
|
101
|
-
utils: { closed: folderUtilsIcon, open: folderUtilsOpenIcon },
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const FILE_ICONS_BY_EXTENSION: Record<string, string> = {
|
|
105
|
-
css: cssIcon,
|
|
106
|
-
html: htmlIcon,
|
|
107
|
-
jpeg: imageIcon,
|
|
108
|
-
jpg: imageIcon,
|
|
109
|
-
js: javascriptIcon,
|
|
110
|
-
json: jsonIcon,
|
|
111
|
-
lock: lockIcon,
|
|
112
|
-
log: logIcon,
|
|
113
|
-
md: markdownIcon,
|
|
114
|
-
png: imageIcon,
|
|
115
|
-
rs: rustIcon,
|
|
116
|
-
svg: imageIcon,
|
|
117
|
-
toml: tomlIcon,
|
|
118
|
-
ts: typescriptIcon,
|
|
119
|
-
tsx: reactTsIcon,
|
|
120
|
-
yaml: yamlIcon,
|
|
121
|
-
yml: yamlIcon,
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const FILE_ICONS_BY_NAME: Record<string, string> = {
|
|
125
|
-
'package-lock.json': npmIcon,
|
|
126
|
-
'package.json': npmIcon,
|
|
127
|
-
'tauri.conf.json': tauriIcon,
|
|
128
|
-
'vite.config.ts': viteIcon,
|
|
129
|
-
'vite.config.web.ts': viteIcon,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function extension(name: string) {
|
|
133
|
-
const index = name.lastIndexOf('.')
|
|
134
|
-
return index === -1 ? '' : name.slice(index + 1).toLowerCase()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function materialFolderIcon(name: string, open: boolean) {
|
|
138
|
-
const icon = FOLDER_ICONS[name] || FOLDER_ICONS[name.toLowerCase()]
|
|
139
|
-
if (!icon) return open ? folderOpenIcon : folderIcon
|
|
140
|
-
return open ? icon.open : icon.closed
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function materialFileIcon(name: string, fileExtension: string) {
|
|
144
|
-
if (name.endsWith('.config.ts') || name.endsWith('.config.js')) return settingsIcon
|
|
145
|
-
if (name.endsWith('.tsx')) return reactTsIcon
|
|
146
|
-
if (name.endsWith('.jsx')) return reactIcon
|
|
147
|
-
return FILE_ICONS_BY_NAME[name.toLowerCase()] || FILE_ICONS_BY_EXTENSION[fileExtension] || documentIcon
|
|
148
|
-
}
|
|
149
31
|
|
|
150
32
|
function TreeNode({ entry, level, onRefresh, workspacePath }: { entry: WorkspaceTreeEntry; level: number; onRefresh: () => void; workspacePath: string; key?: string }) {
|
|
151
33
|
const [open, setOpen] = useState(level < 1)
|