v1.0.22
This commit is contained in:
1 parent
aefa5e7925
commit
b55e1e5aad
107 files changed
+2193
-443
No files matched your search
@@ -2,7 +2,8 @@
|
||||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import { Loader2, FolderOpen, File, FileText, ChevronRight, ArrowUp, AlertCircle, FolderPlus, Search, Import } from 'lucide-svelte';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Loader2, FolderOpen, File, FileText, ChevronRight, ArrowUp, AlertCircle, FolderPlus, Search, Import, Check, X } from 'lucide-svelte';
|
||||
import type { Component } from 'svelte';
|
||||
import RecentLocationsPanel from './RecentLocationsPanel.svelte';
|
||||
|
||||
@@ -61,6 +62,12 @@
|
||||
let selectedPath = $state<string | null>(null);
|
||||
let selectedName = $state<string | null>(null);
|
||||
|
||||
// New folder creation
|
||||
let creatingFolder = $state(false);
|
||||
let newFolderName = $state('');
|
||||
let createError = $state<string | null>(null);
|
||||
let folderInputEl = $state<HTMLInputElement | null>(null);
|
||||
|
||||
// Reference to recent locations panel
|
||||
let recentLocationsPanel = $state<{ addLocation: (path: string) => Promise<void>; getFirstLocation: () => string | null } | null>(null);
|
||||
|
||||
@@ -176,6 +183,58 @@
|
||||
onClose();
|
||||
}
|
||||
|
||||
function startCreatingFolder() {
|
||||
creatingFolder = true;
|
||||
newFolderName = '';
|
||||
createError = null;
|
||||
// Focus input after it renders
|
||||
setTimeout(() => folderInputEl?.focus(), 0);
|
||||
}
|
||||
|
||||
function cancelCreatingFolder() {
|
||||
creatingFolder = false;
|
||||
newFolderName = '';
|
||||
createError = null;
|
||||
}
|
||||
|
||||
async function confirmCreateFolder() {
|
||||
const name = newFolderName.trim();
|
||||
if (!name || !currentPath) return;
|
||||
|
||||
createError = null;
|
||||
const newPath = currentPath === '/' ? `/${name}` : `${currentPath}/${name}`;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/system/files', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: newPath })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
createError = data.error || 'Failed to create folder';
|
||||
return;
|
||||
}
|
||||
|
||||
creatingFolder = false;
|
||||
newFolderName = '';
|
||||
loadDirectory(newPath);
|
||||
} catch (e) {
|
||||
createError = e instanceof Error ? e.message : 'Failed to create folder';
|
||||
}
|
||||
}
|
||||
|
||||
function handleFolderKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
confirmCreateFolder();
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
cancelCreatingFolder();
|
||||
}
|
||||
}
|
||||
|
||||
function handleScan() {
|
||||
if (currentPath && onScanDirectory) {
|
||||
onScanDirectory(currentPath);
|
||||
@@ -262,6 +321,46 @@
|
||||
<ArrowUp class="w-4 h-4" />
|
||||
</button>
|
||||
<code class="text-xs bg-muted px-2 py-1 rounded truncate flex-1">{currentPath || '/'}</code>
|
||||
{#if creatingFolder}
|
||||
<div class="flex items-center gap-1">
|
||||
<Input
|
||||
bind:ref={folderInputEl}
|
||||
bind:value={newFolderName}
|
||||
placeholder="Folder name"
|
||||
class="h-7 w-40 max-w-40 text-xs"
|
||||
onkeydown={handleFolderKeydown}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 rounded hover:bg-muted text-green-600 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
disabled={!newFolderName.trim()}
|
||||
onclick={confirmCreateFolder}
|
||||
title="Create folder"
|
||||
>
|
||||
<Check class="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 rounded hover:bg-muted text-muted-foreground"
|
||||
onclick={cancelCreatingFolder}
|
||||
title="Cancel"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
</button>
|
||||
{#if createError}
|
||||
<span class="text-xs text-red-500 truncate max-w-48" title={createError}>{createError}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="p-1 rounded hover:bg-muted text-muted-foreground"
|
||||
onclick={startCreatingFolder}
|
||||
title="New folder"
|
||||
>
|
||||
<FolderPlus class="w-4 h-4" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if isAdoptMode}
|
||||
<Button
|
||||
variant="default"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import yaml from 'js-yaml';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { currentEnvironment, environments } from '$lib/stores/environment';
|
||||
import { getIconComponent } from '$lib/utils/icons';
|
||||
import EnvironmentIcon from '$lib/components/EnvironmentIcon.svelte';
|
||||
|
||||
interface DiscoveredStack {
|
||||
name: string;
|
||||
@@ -56,7 +56,6 @@
|
||||
// Look up the icon from the environments list since currentEnvironment doesn't store it
|
||||
const currentEnvData = $derived($environments.find(e => e.id === envId));
|
||||
const envIcon = $derived(currentEnvData?.icon || 'globe');
|
||||
const EnvIconComponent = $derived(getIconComponent(envIcon));
|
||||
|
||||
// Reset when modal closes
|
||||
$effect(() => {
|
||||
@@ -330,7 +329,7 @@
|
||||
<Import class="w-5 h-5" />
|
||||
Select stacks to adopt
|
||||
<span class="text-muted-foreground">·</span>
|
||||
<EnvIconComponent class="w-4 h-4 text-muted-foreground" />
|
||||
<EnvironmentIcon icon={envIcon} envId={envId} class="w-4 h-4 text-muted-foreground" />
|
||||
<span class="text-muted-foreground font-normal">{envName}</span>
|
||||
</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
|
||||
Reference in new issue
Block a user