feat(resources): inline edit drawer and Boxes fallback icon

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-05-04 11:25:38 +02:00
parent 392888d113
commit a2a4842d36
2 changed files with 105 additions and 99 deletions
@@ -1,5 +1,5 @@
<script>
import { FileText, FolderOpen } from 'lucide-svelte'
import { Boxes, FileText, FolderOpen } from 'lucide-svelte'
import { APP_TO_ICON_COMPONENT } from './icons'
/**
* @typedef {Object} Props
@@ -56,7 +56,9 @@
<FileText {height} {width} />
</span>
{:else}
<span style="width: {width}; height: {height}" class="bg-gray-100 rounded-full"></span>
<span class="text-hint">
<Boxes size={widthInPixels} />
</span>
{/if}
{#if !silent && after}
{name}
@@ -85,6 +85,7 @@
let allLabels: string[] = $state([])
let resourceTypeViewer: Drawer | undefined = $state(undefined)
let resourceTypeDrawerMode: 'view' | 'edit' = $state('view')
let resourceTypeViewerObj = $state({
rt: '',
description: '',
@@ -94,7 +95,6 @@
})
let resourceTypeDrawer: Drawer | undefined = $state(undefined)
let editResourceTypeDrawer: Drawer | undefined = $state(undefined)
let newResourceType = $state({
name: '',
schema: emptySchema(),
@@ -104,14 +104,6 @@
})
let isNewResourceTypeNameValid: boolean = $state(false)
let resourceTypeNameExists: boolean = $state(false)
let editResourceType = $state({
name: '',
schema: emptySchema(),
description: '',
formatExtension: undefined as string | undefined,
isFileset: false
})
let resourceEditor: ResourceEditorDrawer | undefined = $state(undefined)
let shareModal: ShareModal | undefined = $state(undefined)
let appConnect: AppConnect | undefined = $state(undefined)
@@ -292,13 +284,14 @@
async function updateResourceType(): Promise<void> {
await ResourceService.updateResourceType({
workspace: $workspaceStore!,
path: editResourceType.name,
path: resourceTypeViewerObj.rt,
requestBody: {
schema: editResourceType.schema,
description: editResourceType.description
schema: resourceTypeViewerObj.schema,
description: resourceTypeViewerObj.description
}
})
editResourceTypeDrawer?.closeDrawer?.()
resourceTypeViewer?.closeDrawer?.()
resourceTypeDrawerMode = 'view'
sendUserToast('Resource type updated')
loadResourceTypes()
}
@@ -352,16 +345,28 @@
async function startEditResourceType(name: string) {
const rt = await ResourceService.getResourceType({ workspace: $workspaceStore!, path: name })
editResourceType = {
name: rt.name,
resourceTypeViewerObj = {
rt: rt.name,
schema: rt.schema as any,
description: rt.description ?? '',
formatExtension: rt.format_extension,
isFileset: rt.is_fileset ?? false
}
editResourceTypeDrawer?.openDrawer?.()
resourceTypeDrawerMode = 'edit'
resourceTypeViewer?.openDrawer?.()
}
let canEditCurrentResourceType = $derived.by(() => {
if (!resourceTypeViewerObj.rt) return false
const rt = resourceTypes?.find((r) => r.name === resourceTypeViewerObj.rt)
if (!rt) return false
return (
rt.canWrite &&
showCreateButtons &&
($userStore?.is_admin === true || $userStore?.is_super_admin === true)
)
})
onMount(() => {
const callback = page.url.searchParams.get('callback')
if (callback == 'supabase_wizard') {
@@ -636,7 +641,30 @@
</Drawer>
<Drawer bind:this={resourceTypeViewer} size="800px">
<DrawerContent title={resourceTypeViewerObj.rt} on:close={resourceTypeViewer.closeDrawer}>
<DrawerContent
title={resourceTypeViewerObj.rt}
on:close={() => {
resourceTypeDrawerMode = 'view'
resourceTypeViewer?.closeDrawer?.()
}}
>
{#snippet actions()}
{#if resourceTypeDrawerMode === 'edit'}
<Button
startIcon={{ icon: Save }}
on:click={updateResourceType}
unifiedSize="md"
variant="accent">Update</Button
>
{:else if canEditCurrentResourceType}
<Button
startIcon={{ icon: Pen }}
on:click={() => startEditResourceType(resourceTypeViewerObj.rt)}
unifiedSize="md"
variant="default">Edit</Button
>
{/if}
{/snippet}
<div>
<h1 class="mb-8 mt-4"
><IconedResourceType
@@ -645,87 +673,62 @@
isFileset={resourceTypeViewerObj.isFileset}
/></h1
>
{#if resourceTypeViewerObj.description}
<div class="py-2 box prose mb-8 text-secondary">
<GfmMarkdown md={resourceTypeViewerObj.description ?? ''} />
</div>
{/if}
{#if resourceTypeViewerObj.isFileset}
<Alert type="info" title="Fileset resource type">
This resource type represents a collection of files. Each file is identified by its
relative path and contains text content. In the CLI, filesets are stored as directories.
</Alert>
{:else if resourceTypeViewerObj.formatExtension}
<Alert
type="info"
title="Plain text file resource (.{resourceTypeViewerObj.formatExtension})"
>
This resource type represents a plain text file with a <b
>.{resourceTypeViewerObj.formatExtension}</b
>
extension. (e.g. <b>my_file.{resourceTypeViewerObj.formatExtension}</b>)
</Alert>
{:else}
<SchemaViewer schema={resourceTypeViewerObj.schema} />
{/if}
</div>
</DrawerContent>
</Drawer>
<Drawer bind:this={editResourceTypeDrawer} size="800px">
<DrawerContent title="Edit resource type" on:close={editResourceTypeDrawer.closeDrawer}>
{#snippet actions()}
<Button
startIcon={{ icon: Save }}
on:click={updateResourceType}
unifiedSize="md"
variant="accent">Update</Button
>
{/snippet}
<div class="flex flex-col gap-6">
<label for="inp">
<div class="mb-1 font-semibold text-emphasis text-xs gap-1 flex flex-row items-center"
>Name
<div class="flex flex-row items-center gap-x-4">
<div class="flex flex-row items-center">
<div class="inline-block">
<input
disabled
id="inp"
type="text"
bind:value={editResourceType.name}
class={classNames('!h-8 !border !border-gray-200')}
/>
</div>
</div>
</div>
</div></label
>
<label>
<div class="mb-1 font-semibold text-emphasis text-xs">Description</div>
<textarea use:autosize autocomplete="off" bind:value={editResourceType.description}
></textarea></label
>
<div>
{#if editResourceType.isFileset}
<Alert type="info" title="Fileset resource type">
This resource type represents a collection of files. The schema cannot be edited.
</Alert>
{:else if editResourceType.formatExtension}
<Alert type="info" title="Plain text file resource (.{editResourceType.formatExtension})">
This resource type represents a plain text file with a <b
>.{editResourceType.formatExtension}</b
>
extension. (e.g. <b>my_file.{editResourceType.formatExtension}</b>). The schema only
contains a `content` field and thus cannot be edited.
</Alert>
{:else}
<div class="mb-1 font-semibold text-emphasis text-xs">Schema</div>
<div class="flex flex-col gap-2">
<EditableSchemaWrapper bind:schema={editResourceType.schema} noPreview />
{#if resourceTypeDrawerMode === 'view'}
{#if resourceTypeViewerObj.description}
<div class="py-2 box prose mb-8 text-secondary">
<GfmMarkdown md={resourceTypeViewerObj.description ?? ''} />
</div>
{/if}
</div>
{#if resourceTypeViewerObj.isFileset}
<Alert type="info" title="Fileset resource type">
This resource type represents a collection of files. Each file is identified by its
relative path and contains text content. In the CLI, filesets are stored as directories.
</Alert>
{:else if resourceTypeViewerObj.formatExtension}
<Alert
type="info"
title="Plain text file resource (.{resourceTypeViewerObj.formatExtension})"
>
This resource type represents a plain text file with a <b
>.{resourceTypeViewerObj.formatExtension}</b
>
extension. (e.g. <b>my_file.{resourceTypeViewerObj.formatExtension}</b>)
</Alert>
{:else}
<SchemaViewer schema={resourceTypeViewerObj.schema} />
{/if}
{:else}
<div class="flex flex-col gap-6">
<label>
<div class="mb-1 font-semibold text-emphasis text-xs">Description</div>
<textarea use:autosize autocomplete="off" bind:value={resourceTypeViewerObj.description}
></textarea></label
>
<div>
{#if resourceTypeViewerObj.isFileset}
<Alert type="info" title="Fileset resource type">
This resource type represents a collection of files. The schema cannot be edited.
</Alert>
{:else if resourceTypeViewerObj.formatExtension}
<Alert
type="info"
title="Plain text file resource (.{resourceTypeViewerObj.formatExtension})"
>
This resource type represents a plain text file with a <b
>.{resourceTypeViewerObj.formatExtension}</b
>
extension. (e.g. <b>my_file.{resourceTypeViewerObj.formatExtension}</b>). The schema
only contains a `content` field and thus cannot be edited.
</Alert>
{:else}
<div class="mb-1 font-semibold text-emphasis text-xs">Schema</div>
<div class="flex flex-col gap-2">
<EditableSchemaWrapper bind:schema={resourceTypeViewerObj.schema} noPreview />
</div>
{/if}
</div>
</div>
{/if}
</div>
</DrawerContent>
</Drawer>
@@ -1011,6 +1014,7 @@
formatExtension: linkedRt.format_extension,
isFileset: linkedRt.is_fileset ?? false
}
resourceTypeDrawerMode = 'view'
resourceTypeViewer?.openDrawer?.()
} else {
sendUserToast(
@@ -1225,7 +1229,7 @@
formatExtension: format_extension,
isFileset: is_fileset ?? false
}
resourceTypeDrawerMode = 'view'
resourceTypeViewer?.openDrawer?.()
}}
>