Skip to content
Merged

fix #24

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions src/lib/ai/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getPresetIds } from '$lib/engine/presets';
import { tool, type InferUITools, type Tool } from 'ai';
import { layerRegistry, getAvailableLayerTypes } from '$lib/layers/registry';
import { AnchorPointSchema, extractDefaultValues } from '$lib/layers/base';
import type { LayerTypeString } from '$lib/layers/layer-types';

// ============================================
// Helper Functions
Expand Down Expand Up @@ -50,18 +51,6 @@ function extractKeyProps(schema: z.ZodObject<z.ZodRawShape>): string[] {
return Object.keys(schema.shape).slice(0, MAX_KEY_PROPS);
}

// ============================================
// Shared Schemas for Layer Creation
// ============================================

const PositionSchema = z
.object({
x: z.number().default(0).describe('X position (0 = center)'),
y: z.number().default(0).describe('Y position (0 = center)')
})
.optional()
.describe('Position on canvas. IMPORTANT: always specify to avoid stacking at center');

const AnimationSchema = z
.object({
preset: z.string().describe('Animation preset: ' + getPresetIds().join(', ')),
Expand Down Expand Up @@ -139,9 +128,8 @@ const CreateLayerInputSchema = z
function generateLayerCreationTools(): Record<string, Tool> {
const tools: Record<string, Tool> = {};

for (const layerType of getAvailableLayerTypes()) {
// Skip group type - groups are created via group_layers tool
if (layerType === 'group') continue;
for (const layerType of getAvailableLayerTypes() as LayerTypeString[]) {
if (layerType === 'project-settings') continue;
const definition = layerRegistry[layerType];
if (!definition) continue;

Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/editor/export-dialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
let serverExportAbortController = $state<AbortController | null>(null);

let exportSettings = $derived({
format: 'webm',
fps: projectStore.state.fps,
width: projectStore.state.width,
height: projectStore.state.height
Expand Down Expand Up @@ -419,8 +418,9 @@
<Input
id="export-height"
type="number"
bind:value={exportSettings.height}
value={exportSettings.height}
class="col-span-3"
readonly
/>
</div>

Expand Down
6 changes: 5 additions & 1 deletion src/lib/components/editor/panels/add-layer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
projectStore.selectedLayerId = layer.id;
}

const hiddenTypes = new Set<LiteralUnion<LayerTypeString, string>>(['captions', 'group']);
const hiddenTypes = new Set<LiteralUnion<LayerTypeString, string>>([
'captions',
'group',
'project-settings'
]);

const categoryOrder: LayerCategory[] = ['media', 'text', 'shape', 'code', 'ui'];

Expand Down
30 changes: 27 additions & 3 deletions src/lib/components/editor/panels/layers-panel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { cn } from '$lib/utils';
import type { TypedLayer } from '$lib/layers/typed-registry';
import { SvelteSet } from 'svelte/reactivity';
import { PROJECT_LAYER_ID, PROJECT_LAYER_TYPE, isProjectLayer } from '$lib/layers/project-layer';

const editorState = $derived(getEditorState());
const projectStore = $derived(editorState.project);
Expand Down Expand Up @@ -187,6 +188,8 @@
}
return topLevel;
});

const ProjectIcon = $derived(getLayerDefinition(PROJECT_LAYER_TYPE).icon);
</script>

<div
Expand All @@ -196,6 +199,27 @@
ontouchmove={handleTouchMove}
ontouchend={handleTouchEnd}
>
<!-- Project Settings Layer (always fixed at top, non-draggable, non-deletable) -->
<div
class={cn(
'flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 transition-colors hover:bg-muted/50',
{
'bg-muted': isProjectLayer(projectStore.selectedLayerId)
}
)}
onclick={() => selectLayer(PROJECT_LAYER_ID)}
onkeydown={(e) => handleKeyDown(e, PROJECT_LAYER_ID)}
role="button"
tabindex="0"
>
<ProjectIcon class="size-4 shrink-0" />
<div class="flex-1 truncate text-sm font-medium">
{projectStore.state.name}
</div>
</div>

<div class="my-2 border-t"></div>

{#each layerTree as { layer, children, index } (layer.id)}
{@const Icon = getLayerDefinition(layer.type).icon}
{@const isGroup = layer.type === 'group'}
Expand Down Expand Up @@ -460,9 +484,9 @@
{/each}

{#if projectStore.state.layers.length === 0}
<div class="py-8 text-center text-sm text-muted-foreground">
<p>No layers yet</p>
<p class="mt-1 text-xs">Add layers from the toolbar</p>
<div class="py-4 text-center text-sm text-muted-foreground">
<p>No animation layers yet</p>
<p class="mt-1 text-xs">Click the + button above to add your first layer</p>
</div>
{/if}
</div>
Loading