@autumnsgrove/groveengine 0.8.6 → 0.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.
Files changed (80) hide show
  1. package/dist/components/admin/GutterManager.svelte +213 -101
  2. package/dist/components/admin/MarkdownEditor.svelte +6 -3
  3. package/dist/components/custom/GutterItem.svelte +8 -2
  4. package/dist/components/quota/UpgradePrompt.svelte +1 -0
  5. package/dist/config/domain-blocklist.d.ts +59 -0
  6. package/dist/config/domain-blocklist.js +731 -0
  7. package/dist/config/index.d.ts +3 -1
  8. package/dist/config/index.js +2 -1
  9. package/dist/config/offensive-blocklist.d.ts +44 -0
  10. package/dist/config/offensive-blocklist.js +751 -0
  11. package/dist/config/terrarium.d.ts +109 -0
  12. package/dist/config/terrarium.js +125 -0
  13. package/dist/styles/tokens.css +90 -0
  14. package/dist/types/dom-to-image-more.d.ts +39 -0
  15. package/dist/ui/components/chrome/Footer.svelte +137 -0
  16. package/dist/ui/components/chrome/Footer.svelte.d.ts +11 -0
  17. package/dist/ui/components/chrome/FooterMinimal.svelte +75 -0
  18. package/dist/ui/components/chrome/FooterMinimal.svelte.d.ts +10 -0
  19. package/dist/ui/components/chrome/Header.svelte +113 -0
  20. package/dist/ui/components/chrome/Header.svelte.d.ts +11 -0
  21. package/dist/ui/components/chrome/HeaderMinimal.svelte +68 -0
  22. package/dist/ui/components/chrome/HeaderMinimal.svelte.d.ts +9 -0
  23. package/dist/ui/components/chrome/MobileMenu.svelte +145 -0
  24. package/dist/ui/components/chrome/MobileMenu.svelte.d.ts +9 -0
  25. package/dist/ui/components/chrome/ThemeToggle.svelte +34 -0
  26. package/dist/ui/components/chrome/ThemeToggle.svelte.d.ts +3 -0
  27. package/dist/ui/components/chrome/defaults.d.ts +6 -0
  28. package/dist/ui/components/chrome/defaults.js +65 -0
  29. package/dist/ui/components/chrome/index.d.ts +13 -0
  30. package/dist/ui/components/chrome/index.js +14 -0
  31. package/dist/ui/components/chrome/types.d.ts +19 -0
  32. package/dist/ui/components/chrome/types.js +8 -0
  33. package/dist/ui/components/content/RoadmapPreview.svelte +2 -1
  34. package/dist/ui/components/forms/ContentSearch.svelte +406 -0
  35. package/dist/ui/components/forms/ContentSearch.svelte.d.ts +71 -0
  36. package/dist/ui/components/forms/filterUtils.d.ts +138 -0
  37. package/dist/ui/components/forms/filterUtils.js +240 -0
  38. package/dist/ui/components/forms/index.d.ts +2 -0
  39. package/dist/ui/components/forms/index.js +5 -1
  40. package/dist/ui/components/gallery/ImageGallery.svelte +3 -0
  41. package/dist/ui/components/gallery/Lightbox.svelte +3 -0
  42. package/dist/ui/components/gallery/ZoomableImage.svelte +1 -0
  43. package/dist/ui/components/icons/index.d.ts +2 -1
  44. package/dist/ui/components/icons/index.js +14 -3
  45. package/dist/ui/components/icons/lucide.d.ts +213 -0
  46. package/dist/ui/components/icons/lucide.js +224 -0
  47. package/dist/ui/components/terrarium/AssetPalette.svelte +207 -0
  48. package/dist/ui/components/terrarium/AssetPalette.svelte.d.ts +7 -0
  49. package/dist/ui/components/terrarium/Canvas.svelte +231 -0
  50. package/dist/ui/components/terrarium/Canvas.svelte.d.ts +14 -0
  51. package/dist/ui/components/terrarium/ExportDialog.svelte +307 -0
  52. package/dist/ui/components/terrarium/ExportDialog.svelte.d.ts +18 -0
  53. package/dist/ui/components/terrarium/PaletteItem.svelte +169 -0
  54. package/dist/ui/components/terrarium/PaletteItem.svelte.d.ts +9 -0
  55. package/dist/ui/components/terrarium/PlacedAsset.svelte +222 -0
  56. package/dist/ui/components/terrarium/PlacedAsset.svelte.d.ts +11 -0
  57. package/dist/ui/components/terrarium/Terrarium.svelte +266 -0
  58. package/dist/ui/components/terrarium/Terrarium.svelte.d.ts +3 -0
  59. package/dist/ui/components/terrarium/Toolbar.svelte +299 -0
  60. package/dist/ui/components/terrarium/Toolbar.svelte.d.ts +24 -0
  61. package/dist/ui/components/terrarium/index.d.ts +31 -0
  62. package/dist/ui/components/terrarium/index.js +33 -0
  63. package/dist/ui/components/terrarium/terrariumState.svelte.d.ts +45 -0
  64. package/dist/ui/components/terrarium/terrariumState.svelte.js +291 -0
  65. package/dist/ui/components/terrarium/types.d.ts +139 -0
  66. package/dist/ui/components/terrarium/types.js +43 -0
  67. package/dist/ui/components/terrarium/utils/export.d.ts +48 -0
  68. package/dist/ui/components/terrarium/utils/export.js +148 -0
  69. package/dist/ui/components/ui/CollapsibleSection.svelte +2 -0
  70. package/dist/ui/components/ui/GlassConfirmDialog.svelte +9 -0
  71. package/dist/ui/components/ui/GlassOverlay.svelte +2 -1
  72. package/dist/ui/components/ui/Input.svelte +9 -1
  73. package/dist/ui/components/ui/Input.svelte.d.ts +2 -0
  74. package/dist/ui/components/ui/Textarea.svelte +9 -1
  75. package/dist/ui/components/ui/Textarea.svelte.d.ts +2 -0
  76. package/dist/ui/stores/index.d.ts +6 -0
  77. package/dist/ui/stores/index.js +6 -0
  78. package/dist/ui/stores/season.d.ts +14 -0
  79. package/dist/ui/stores/season.js +65 -0
  80. package/package.json +27 -4
@@ -0,0 +1,299 @@
1
+ <!--
2
+ Grove — A place to Be
3
+ Copyright (c) 2025 Autumn Brown
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published
7
+ by the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ -->
18
+
19
+ <script lang="ts">
20
+ import { Play, Pause, Grid3X3, Trash2, Copy, Download, Save } from 'lucide-svelte';
21
+ import { cn } from '../../utils';
22
+
23
+ /**
24
+ * Terrarium Toolbar
25
+ *
26
+ * Top toolbar for Terrarium canvas actions and settings.
27
+ * Features scene naming, animation/grid controls, and action buttons.
28
+ */
29
+
30
+ interface Props {
31
+ sceneName: string;
32
+ animationsEnabled: boolean;
33
+ gridEnabled: boolean;
34
+ gridSize: 16 | 32 | 64;
35
+ hasSelection: boolean;
36
+ onToggleAnimations: () => void;
37
+ onToggleGrid: () => void;
38
+ onSetGridSize: (size: 16 | 32 | 64) => void;
39
+ onDelete: () => void;
40
+ onDuplicate: () => void;
41
+ onExport: () => void;
42
+ onSave: () => void;
43
+ onRename: (name: string) => void;
44
+ }
45
+
46
+ let {
47
+ sceneName = $bindable(),
48
+ animationsEnabled,
49
+ gridEnabled,
50
+ gridSize,
51
+ hasSelection,
52
+ onToggleAnimations,
53
+ onToggleGrid,
54
+ onSetGridSize,
55
+ onDelete,
56
+ onDuplicate,
57
+ onExport,
58
+ onSave,
59
+ onRename
60
+ }: Props = $props();
61
+
62
+ let isEditingName = $state(false);
63
+ let nameInput: HTMLInputElement | null = $state(null);
64
+ let editedName = $state(sceneName);
65
+
66
+ // Grid size options
67
+ const gridSizes: Array<16 | 32 | 64> = [16, 32, 64];
68
+
69
+ // Start editing scene name
70
+ function startEditingName() {
71
+ isEditingName = true;
72
+ editedName = sceneName;
73
+ setTimeout(() => {
74
+ nameInput?.focus();
75
+ nameInput?.select();
76
+ }, 0);
77
+ }
78
+
79
+ // Finish editing scene name
80
+ function finishEditingName() {
81
+ if (isEditingName) {
82
+ const trimmed = editedName.trim();
83
+ if (trimmed && trimmed !== sceneName) {
84
+ onRename(trimmed);
85
+ } else {
86
+ editedName = sceneName;
87
+ }
88
+ isEditingName = false;
89
+ }
90
+ }
91
+
92
+ // Handle name input keydown
93
+ function handleNameKeydown(e: KeyboardEvent) {
94
+ if (e.key === 'Enter') {
95
+ e.preventDefault();
96
+ finishEditingName();
97
+ } else if (e.key === 'Escape') {
98
+ e.preventDefault();
99
+ editedName = sceneName;
100
+ isEditingName = false;
101
+ }
102
+ }
103
+
104
+ // Glass button styles
105
+ const buttonClass = cn(
106
+ 'inline-flex items-center justify-center',
107
+ 'h-9 px-3 rounded-lg',
108
+ 'bg-white/60 dark:bg-emerald-950/25',
109
+ 'border border-white/40 dark:border-emerald-800/25',
110
+ 'text-foreground text-sm font-medium',
111
+ 'hover:bg-white/75 dark:hover:bg-emerald-950/35',
112
+ 'hover:border-white/50 dark:hover:border-emerald-700/30',
113
+ 'transition-all duration-200',
114
+ 'backdrop-blur-md shadow-sm hover:shadow-md',
115
+ 'disabled:opacity-50 disabled:pointer-events-none',
116
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50',
117
+ '[&_svg]:w-4 [&_svg]:h-4'
118
+ );
119
+
120
+ const iconButtonClass = cn(
121
+ 'inline-flex items-center justify-center',
122
+ 'h-9 w-9 rounded-lg',
123
+ 'bg-white/60 dark:bg-emerald-950/25',
124
+ 'border border-white/40 dark:border-emerald-800/25',
125
+ 'text-foreground',
126
+ 'hover:bg-white/75 dark:hover:bg-emerald-950/35',
127
+ 'hover:border-white/50 dark:hover:border-emerald-700/30',
128
+ 'transition-all duration-200',
129
+ 'backdrop-blur-md shadow-sm hover:shadow-md',
130
+ 'disabled:opacity-50 disabled:pointer-events-none',
131
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50',
132
+ '[&_svg]:w-4 [&_svg]:h-4'
133
+ );
134
+
135
+ const activeButtonClass = cn(
136
+ iconButtonClass,
137
+ 'bg-accent/70 dark:bg-accent/60',
138
+ 'border-accent/40 dark:border-accent/30',
139
+ 'text-white',
140
+ 'hover:bg-accent/85 dark:hover:bg-accent/75',
141
+ 'hover:border-accent/60 dark:hover:border-accent/50'
142
+ );
143
+ </script>
144
+
145
+ <nav
146
+ class={cn(
147
+ 'flex items-center gap-3 px-4 py-3',
148
+ 'bg-white/60 dark:bg-emerald-950/25',
149
+ 'border-b border-white/40 dark:border-emerald-800/25',
150
+ 'backdrop-blur-md shadow-sm'
151
+ )}
152
+ >
153
+ <!-- Scene Name -->
154
+ <div class="flex items-center gap-2 min-w-0">
155
+ {#if isEditingName}
156
+ <input
157
+ bind:this={nameInput}
158
+ bind:value={editedName}
159
+ onblur={finishEditingName}
160
+ onkeydown={handleNameKeydown}
161
+ class={cn(
162
+ 'px-2 py-1 rounded text-lg font-semibold',
163
+ 'bg-white/80 dark:bg-emerald-950/40',
164
+ 'border border-accent/40 dark:border-accent/30',
165
+ 'focus:outline-none focus:ring-2 focus:ring-accent/50',
166
+ 'min-w-[200px] max-w-[400px]'
167
+ )}
168
+ maxlength="100"
169
+ />
170
+ {:else}
171
+ <button
172
+ onclick={startEditingName}
173
+ class={cn(
174
+ 'px-2 py-1 rounded text-lg font-semibold truncate',
175
+ 'hover:bg-white/40 dark:hover:bg-emerald-950/20',
176
+ 'transition-colors duration-200',
177
+ 'text-left max-w-[400px]'
178
+ )}
179
+ title="Click to rename scene"
180
+ >
181
+ {sceneName}
182
+ </button>
183
+ {/if}
184
+ </div>
185
+
186
+ <div class="flex-1"></div>
187
+
188
+ <!-- Controls Section -->
189
+ <div class="flex items-center gap-2">
190
+ <!-- Animation Toggle -->
191
+ <button
192
+ onclick={onToggleAnimations}
193
+ class={animationsEnabled ? activeButtonClass : iconButtonClass}
194
+ title={animationsEnabled ? 'Pause animations (Space)' : 'Play animations (Space)'}
195
+ aria-label={animationsEnabled ? 'Pause animations' : 'Play animations'}
196
+ >
197
+ {#if animationsEnabled}
198
+ <Pause />
199
+ {:else}
200
+ <Play />
201
+ {/if}
202
+ </button>
203
+
204
+ <!-- Grid Toggle -->
205
+ <button
206
+ onclick={onToggleGrid}
207
+ class={gridEnabled ? activeButtonClass : iconButtonClass}
208
+ title={gridEnabled ? 'Hide grid (G)' : 'Show grid (G)'}
209
+ aria-label={gridEnabled ? 'Hide grid' : 'Show grid'}
210
+ >
211
+ <Grid3X3 />
212
+ </button>
213
+
214
+ <!-- Grid Size Dropdown -->
215
+ {#if gridEnabled}
216
+ <select
217
+ value={gridSize}
218
+ onchange={(e) => onSetGridSize(Number(e.currentTarget.value) as 16 | 32 | 64)}
219
+ class={cn(
220
+ 'h-9 px-3 rounded-lg',
221
+ 'bg-white/60 dark:bg-emerald-950/25',
222
+ 'border border-white/40 dark:border-emerald-800/25',
223
+ 'text-foreground text-sm font-medium',
224
+ 'hover:bg-white/75 dark:hover:bg-emerald-950/35',
225
+ 'transition-all duration-200',
226
+ 'backdrop-blur-md shadow-sm',
227
+ 'focus:outline-none focus:ring-2 focus:ring-accent/50',
228
+ 'cursor-pointer'
229
+ )}
230
+ title="Grid size"
231
+ aria-label="Select grid size"
232
+ >
233
+ {#each gridSizes as size}
234
+ <option value={size}>{size}px</option>
235
+ {/each}
236
+ </select>
237
+ {/if}
238
+ </div>
239
+
240
+ <!-- Divider -->
241
+ <div class="w-px h-6 bg-white/40 dark:bg-emerald-800/25"></div>
242
+
243
+ <!-- Action Buttons Section -->
244
+ <div class="flex items-center gap-2">
245
+ <!-- Delete -->
246
+ <button
247
+ onclick={onDelete}
248
+ disabled={!hasSelection}
249
+ class={iconButtonClass}
250
+ title="Delete selected (⌫)"
251
+ aria-label="Delete selected asset"
252
+ >
253
+ <Trash2 />
254
+ </button>
255
+
256
+ <!-- Duplicate -->
257
+ <button
258
+ onclick={onDuplicate}
259
+ disabled={!hasSelection}
260
+ class={iconButtonClass}
261
+ title="Duplicate selected (⌘D)"
262
+ aria-label="Duplicate selected asset"
263
+ >
264
+ <Copy />
265
+ </button>
266
+
267
+ <!-- Export PNG -->
268
+ <button
269
+ onclick={onExport}
270
+ class={buttonClass}
271
+ title="Export as PNG (⌘E)"
272
+ aria-label="Export scene as PNG"
273
+ >
274
+ <Download />
275
+ <span>Export PNG</span>
276
+ </button>
277
+ </div>
278
+
279
+ <!-- Divider -->
280
+ <div class="w-px h-6 bg-white/40 dark:bg-emerald-800/25"></div>
281
+
282
+ <!-- Save Button -->
283
+ <button
284
+ onclick={onSave}
285
+ class={cn(
286
+ buttonClass,
287
+ 'bg-accent/70 dark:bg-accent/60',
288
+ 'border-accent/40 dark:border-accent/30',
289
+ 'text-white',
290
+ 'hover:bg-accent/85 dark:hover:bg-accent/75',
291
+ 'hover:border-accent/60 dark:hover:border-accent/50'
292
+ )}
293
+ title="Save scene (⌘S)"
294
+ aria-label="Save scene"
295
+ >
296
+ <Save />
297
+ <span>Save</span>
298
+ </button>
299
+ </nav>
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Terrarium Toolbar
3
+ *
4
+ * Top toolbar for Terrarium canvas actions and settings.
5
+ * Features scene naming, animation/grid controls, and action buttons.
6
+ */
7
+ interface Props {
8
+ sceneName: string;
9
+ animationsEnabled: boolean;
10
+ gridEnabled: boolean;
11
+ gridSize: 16 | 32 | 64;
12
+ hasSelection: boolean;
13
+ onToggleAnimations: () => void;
14
+ onToggleGrid: () => void;
15
+ onSetGridSize: (size: 16 | 32 | 64) => void;
16
+ onDelete: () => void;
17
+ onDuplicate: () => void;
18
+ onExport: () => void;
19
+ onSave: () => void;
20
+ onRename: (name: string) => void;
21
+ }
22
+ declare const Toolbar: import("svelte").Component<Props, {}, "sceneName">;
23
+ type Toolbar = ReturnType<typeof Toolbar>;
24
+ export default Toolbar;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Terrarium — Creative Canvas for Grove
3
+ *
4
+ * A drag-and-drop canvas where users compose nature scenes
5
+ * that become decorations for their blogs.
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { Terrarium } from '@autumnsgrove/groveengine/ui/terrarium';
11
+ * </script>
12
+ *
13
+ * <Terrarium />
14
+ * ```
15
+ *
16
+ * Grove — A place to Be
17
+ * Copyright (c) 2025 Autumn Brown
18
+ * Licensed under AGPL-3.0
19
+ */
20
+ export { default as Terrarium } from './Terrarium.svelte';
21
+ export { default as Canvas } from './Canvas.svelte';
22
+ export { default as AssetPalette } from './AssetPalette.svelte';
23
+ export { default as PaletteItem } from './PaletteItem.svelte';
24
+ export { default as PlacedAssetComponent } from './PlacedAsset.svelte';
25
+ export { default as Toolbar } from './Toolbar.svelte';
26
+ export { default as ExportDialog } from './ExportDialog.svelte';
27
+ export { createTerrariumState } from './terrariumState.svelte';
28
+ export type { TerrariumState } from './terrariumState.svelte';
29
+ export type { AssetCategory, AssetDefinition, AssetMeta, CanvasSettings, Decoration, DecorationOptions, DecorationZone, DragState, ExportOptions, PanState, PlacedAsset, Point, PropDefinition, SelectionState, Size, TerrariumScene, ToolbarAction, ToolMode } from './types';
30
+ export { CANVAS_BACKGROUNDS, DEFAULT_SCENE } from './types';
31
+ export { exportSceneAsPNG, generateDataUrl, sanitizeFilename } from './utils/export';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Terrarium — Creative Canvas for Grove
3
+ *
4
+ * A drag-and-drop canvas where users compose nature scenes
5
+ * that become decorations for their blogs.
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <script>
10
+ * import { Terrarium } from '@autumnsgrove/groveengine/ui/terrarium';
11
+ * </script>
12
+ *
13
+ * <Terrarium />
14
+ * ```
15
+ *
16
+ * Grove — A place to Be
17
+ * Copyright (c) 2025 Autumn Brown
18
+ * Licensed under AGPL-3.0
19
+ */
20
+ // Main component
21
+ export { default as Terrarium } from './Terrarium.svelte';
22
+ // Sub-components (for advanced usage)
23
+ export { default as Canvas } from './Canvas.svelte';
24
+ export { default as AssetPalette } from './AssetPalette.svelte';
25
+ export { default as PaletteItem } from './PaletteItem.svelte';
26
+ export { default as PlacedAssetComponent } from './PlacedAsset.svelte';
27
+ export { default as Toolbar } from './Toolbar.svelte';
28
+ export { default as ExportDialog } from './ExportDialog.svelte';
29
+ // State management
30
+ export { createTerrariumState } from './terrariumState.svelte';
31
+ export { CANVAS_BACKGROUNDS, DEFAULT_SCENE } from './types';
32
+ // Utilities
33
+ export { exportSceneAsPNG, generateDataUrl, sanitizeFilename } from './utils/export';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Grove — A place to Be
3
+ * Copyright (c) 2025 Autumn Brown
4
+ * Licensed under AGPL-3.0
5
+ */
6
+ /**
7
+ * Terrarium State Management
8
+ *
9
+ * Centralized state management for the Terrarium creative canvas using Svelte 5 runes.
10
+ * Manages scene, assets, selection, and canvas interaction state.
11
+ *
12
+ * Note on mutation strategy: Svelte 5 runes track mutations deeply, so we use direct
13
+ * mutations (push, splice, property assignment) for performance. The setScene function
14
+ * uses immutable patterns to ensure a clean state when loading external data.
15
+ */
16
+ import type { TerrariumScene, PlacedAsset, Point, AssetCategory, ToolMode } from './types';
17
+ /**
18
+ * Create the Terrarium state manager
19
+ */
20
+ export declare function createTerrariumState(): {
21
+ readonly scene: TerrariumScene;
22
+ readonly selectedAssetId: string | null;
23
+ isDragging: boolean;
24
+ readonly animationsEnabled: boolean;
25
+ readonly panOffset: Point;
26
+ readonly toolMode: ToolMode;
27
+ readonly selectedAsset: PlacedAsset | null;
28
+ readonly assetCount: number;
29
+ readonly canAddAsset: boolean;
30
+ readonly complexityUsage: number;
31
+ addAsset: (componentName: string, category: AssetCategory, position: Point) => string;
32
+ updateAsset: (id: string, updates: Partial<PlacedAsset>) => void;
33
+ deleteAsset: (id: string) => void;
34
+ duplicateAsset: (id: string) => string;
35
+ selectAsset: (id: string | null) => void;
36
+ moveLayer: (id: string, direction: "up" | "down" | "top" | "bottom") => void;
37
+ setScene: (newScene: TerrariumScene) => void;
38
+ resetScene: () => void;
39
+ toggleAnimations: () => void;
40
+ toggleGrid: () => void;
41
+ setGridSize: (size: 16 | 32 | 64) => void;
42
+ setPanOffset: (offset: Point) => void;
43
+ setToolMode: (mode: ToolMode) => void;
44
+ };
45
+ export type TerrariumState = ReturnType<typeof createTerrariumState>;