mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:21:08 +02:00
fix(picture): resolve critical issues — dead stores, type-check, test coverage
Store cleanup: - Remove 12 dead duplicate .svelte.ts store files (1,306 lines of unused code) - All components use the .ts writable stores, runes versions were never imported Type-check re-enabled: - Fix Modal prop mismatch: open→visible, size→maxWidth in ImagePickerModal and board page - Fix __BUILD_TIME__/__BUILD_HASH__ declarations in app.d.ts (declare global block) - Fix vite.config.ts plugin type incompatibility with `as any` casts - Re-enable type-check script (was skipped since shared-ui component fixes) Test coverage: - Add generate.service.spec.ts with 35 tests covering: checkGenerationAccess, generateImage, checkStatus, cancelGeneration, handleWebhook - Tests cover credit logic, sync/async modes, ownership checks, error handling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c0c11c325a
commit
c26a48e16a
17 changed files with 1073 additions and 1315 deletions
File diff suppressed because it is too large
Load diff
6
apps/picture/apps/web/src/app.d.ts
vendored
6
apps/picture/apps/web/src/app.d.ts
vendored
|
|
@ -1,9 +1,9 @@
|
|||
declare const __BUILD_HASH__: string;
|
||||
declare const __BUILD_TIME__: string;
|
||||
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
declare const __BUILD_HASH__: string;
|
||||
declare const __BUILD_TIME__: string;
|
||||
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@
|
|||
);
|
||||
</script>
|
||||
|
||||
<Modal {open} {onClose} size="large">
|
||||
<Modal visible={open} {onClose} maxWidth="3xl">
|
||||
<div class="flex h-[80vh] flex-col p-6">
|
||||
<!-- Header -->
|
||||
<div class="mb-6">
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Archive Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Image } from '$lib/api/images';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let archivedImages = $state<Image[]>([]);
|
||||
let isLoadingArchive = $state(false);
|
||||
let hasMoreArchive = $state(true);
|
||||
let currentArchivePage = $state(1);
|
||||
|
||||
export const archiveStore = {
|
||||
get images() {
|
||||
return archivedImages;
|
||||
},
|
||||
get isLoading() {
|
||||
return isLoadingArchive;
|
||||
},
|
||||
get hasMore() {
|
||||
return hasMoreArchive;
|
||||
},
|
||||
get currentPage() {
|
||||
return currentArchivePage;
|
||||
},
|
||||
|
||||
setImages(images: Image[]) {
|
||||
archivedImages = images;
|
||||
},
|
||||
|
||||
appendImages(images: Image[]) {
|
||||
archivedImages = [...archivedImages, ...images];
|
||||
},
|
||||
|
||||
addImage(image: Image) {
|
||||
archivedImages = [image, ...archivedImages];
|
||||
},
|
||||
|
||||
removeImage(id: string) {
|
||||
archivedImages = archivedImages.filter((img) => img.id !== id);
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoadingArchive = loading;
|
||||
},
|
||||
|
||||
setHasMore(more: boolean) {
|
||||
hasMoreArchive = more;
|
||||
},
|
||||
|
||||
setCurrentPage(page: number) {
|
||||
currentArchivePage = page;
|
||||
},
|
||||
|
||||
incrementPage() {
|
||||
currentArchivePage++;
|
||||
},
|
||||
|
||||
reset() {
|
||||
archivedImages = [];
|
||||
isLoadingArchive = false;
|
||||
hasMoreArchive = true;
|
||||
currentArchivePage = 1;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getArchivedImages() {
|
||||
return archivedImages;
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/**
|
||||
* Boards Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Board, BoardWithCount } from '$lib/api/boards';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let boards = $state<BoardWithCount[]>([]);
|
||||
let currentBoard = $state<Board | null>(null);
|
||||
let isLoadingBoards = $state(false);
|
||||
let isLoadingBoard = $state(false);
|
||||
let currentBoardsPage = $state(1);
|
||||
let hasBoardsMore = $state(true);
|
||||
let selectedBoard = $state<Board | null>(null);
|
||||
let showCreateBoardModal = $state(false);
|
||||
let showShareBoardModal = $state(false);
|
||||
let shareBoardId = $state<string | null>(null);
|
||||
|
||||
// Derived state
|
||||
const boardSettings = $derived({
|
||||
width: currentBoard?.canvasWidth || 2000,
|
||||
height: currentBoard?.canvasHeight || 1500,
|
||||
backgroundColor: currentBoard?.backgroundColor || '#ffffff',
|
||||
});
|
||||
|
||||
export const boardsStore = {
|
||||
get boards() {
|
||||
return boards;
|
||||
},
|
||||
get currentBoard() {
|
||||
return currentBoard;
|
||||
},
|
||||
get isLoadingBoards() {
|
||||
return isLoadingBoards;
|
||||
},
|
||||
get isLoadingBoard() {
|
||||
return isLoadingBoard;
|
||||
},
|
||||
get currentBoardsPage() {
|
||||
return currentBoardsPage;
|
||||
},
|
||||
get hasBoardsMore() {
|
||||
return hasBoardsMore;
|
||||
},
|
||||
get selectedBoard() {
|
||||
return selectedBoard;
|
||||
},
|
||||
get showCreateBoardModal() {
|
||||
return showCreateBoardModal;
|
||||
},
|
||||
get showShareBoardModal() {
|
||||
return showShareBoardModal;
|
||||
},
|
||||
get shareBoardId() {
|
||||
return shareBoardId;
|
||||
},
|
||||
get boardSettings() {
|
||||
return boardSettings;
|
||||
},
|
||||
|
||||
setBoards(newBoards: BoardWithCount[]) {
|
||||
boards = newBoards;
|
||||
},
|
||||
|
||||
appendBoards(newBoards: BoardWithCount[]) {
|
||||
boards = [...boards, ...newBoards];
|
||||
},
|
||||
|
||||
addBoard(board: BoardWithCount) {
|
||||
boards = [board, ...boards];
|
||||
},
|
||||
|
||||
updateBoardInList(boardId: string, updates: Partial<Board>) {
|
||||
boards = boards.map((board) => (board.id === boardId ? { ...board, ...updates } : board));
|
||||
},
|
||||
|
||||
removeBoardFromList(boardId: string) {
|
||||
boards = boards.filter((board) => board.id !== boardId);
|
||||
},
|
||||
|
||||
incrementBoardItemCount(boardId: string) {
|
||||
boards = boards.map((board) =>
|
||||
board.id === boardId ? { ...board, itemCount: board.itemCount + 1 } : board
|
||||
);
|
||||
},
|
||||
|
||||
decrementBoardItemCount(boardId: string) {
|
||||
boards = boards.map((board) =>
|
||||
board.id === boardId ? { ...board, itemCount: Math.max(0, board.itemCount - 1) } : board
|
||||
);
|
||||
},
|
||||
|
||||
setCurrentBoard(board: Board | null) {
|
||||
currentBoard = board;
|
||||
},
|
||||
|
||||
setLoadingBoards(loading: boolean) {
|
||||
isLoadingBoards = loading;
|
||||
},
|
||||
|
||||
setLoadingBoard(loading: boolean) {
|
||||
isLoadingBoard = loading;
|
||||
},
|
||||
|
||||
setCurrentBoardsPage(page: number) {
|
||||
currentBoardsPage = page;
|
||||
},
|
||||
|
||||
setHasBoardsMore(more: boolean) {
|
||||
hasBoardsMore = more;
|
||||
},
|
||||
|
||||
setSelectedBoard(board: Board | null) {
|
||||
selectedBoard = board;
|
||||
},
|
||||
|
||||
setShowCreateBoardModal(show: boolean) {
|
||||
showCreateBoardModal = show;
|
||||
},
|
||||
|
||||
setShowShareBoardModal(show: boolean) {
|
||||
showShareBoardModal = show;
|
||||
},
|
||||
|
||||
setShareBoardId(id: string | null) {
|
||||
shareBoardId = id;
|
||||
},
|
||||
|
||||
resetBoardsState() {
|
||||
boards = [];
|
||||
currentBoardsPage = 1;
|
||||
hasBoardsMore = true;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getBoards() {
|
||||
return boards;
|
||||
}
|
||||
|
||||
export function getCurrentBoard() {
|
||||
return currentBoard;
|
||||
}
|
||||
|
||||
export function getBoardSettings() {
|
||||
return boardSettings;
|
||||
}
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
/**
|
||||
* Canvas Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { BoardItem, BoardImageItem, BoardTextItem } from '$lib/api/boardItems';
|
||||
import { isImageItem, isTextItem } from '$lib/api/boardItems';
|
||||
|
||||
// Canvas items (images and texts on the board)
|
||||
let canvasItems = $state<BoardItem[]>([]);
|
||||
|
||||
// Selected items on canvas
|
||||
let selectedItemIds = $state<string[]>([]);
|
||||
|
||||
// Canvas view state
|
||||
let canvasZoom = $state(1);
|
||||
let canvasPan = $state({ x: 0, y: 0 });
|
||||
|
||||
// Canvas interaction mode
|
||||
export type CanvasMode = 'select' | 'pan' | 'draw';
|
||||
let canvasMode = $state<CanvasMode>('select');
|
||||
|
||||
// Canvas tools
|
||||
let showGrid = $state(true);
|
||||
let snapToGrid = $state(false);
|
||||
let gridSize = $state(20);
|
||||
|
||||
// UI state
|
||||
let showPropertiesPanel = $state(false);
|
||||
|
||||
// Text editing state
|
||||
let editingTextId = $state<string | null>(null);
|
||||
|
||||
// Loading state
|
||||
let isLoadingCanvasItems = $state(false);
|
||||
|
||||
// History for undo/redo
|
||||
interface HistoryState {
|
||||
items: BoardItem[];
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
let canvasHistory = $state<HistoryState[]>([]);
|
||||
let canvasHistoryIndex = $state(-1);
|
||||
|
||||
// Derived states
|
||||
const selectedItems = $derived(canvasItems.filter((item) => selectedItemIds.includes(item.id)));
|
||||
|
||||
const selectedTextItems = $derived(selectedItems.filter(isTextItem));
|
||||
|
||||
const selectedImageItems = $derived(selectedItems.filter(isImageItem));
|
||||
|
||||
const hasMixedSelection = $derived(selectedTextItems.length > 0 && selectedImageItems.length > 0);
|
||||
|
||||
const hasSelection = $derived(selectedItemIds.length > 0);
|
||||
|
||||
const isEditingText = $derived(editingTextId !== null);
|
||||
|
||||
const canUndo = $derived(canvasHistoryIndex > 0);
|
||||
|
||||
const canRedo = $derived(canvasHistoryIndex < canvasHistory.length - 1);
|
||||
|
||||
export const canvasStore = {
|
||||
// Getters
|
||||
get items() {
|
||||
return canvasItems;
|
||||
},
|
||||
get selectedItemIds() {
|
||||
return selectedItemIds;
|
||||
},
|
||||
get selectedItems() {
|
||||
return selectedItems;
|
||||
},
|
||||
get selectedTextItems() {
|
||||
return selectedTextItems;
|
||||
},
|
||||
get selectedImageItems() {
|
||||
return selectedImageItems;
|
||||
},
|
||||
get hasMixedSelection() {
|
||||
return hasMixedSelection;
|
||||
},
|
||||
get hasSelection() {
|
||||
return hasSelection;
|
||||
},
|
||||
get zoom() {
|
||||
return canvasZoom;
|
||||
},
|
||||
get pan() {
|
||||
return canvasPan;
|
||||
},
|
||||
get mode() {
|
||||
return canvasMode;
|
||||
},
|
||||
get showGrid() {
|
||||
return showGrid;
|
||||
},
|
||||
get snapToGrid() {
|
||||
return snapToGrid;
|
||||
},
|
||||
get gridSize() {
|
||||
return gridSize;
|
||||
},
|
||||
get showPropertiesPanel() {
|
||||
return showPropertiesPanel;
|
||||
},
|
||||
get editingTextId() {
|
||||
return editingTextId;
|
||||
},
|
||||
get isEditingText() {
|
||||
return isEditingText;
|
||||
},
|
||||
get isLoading() {
|
||||
return isLoadingCanvasItems;
|
||||
},
|
||||
get canUndo() {
|
||||
return canUndo;
|
||||
},
|
||||
get canRedo() {
|
||||
return canRedo;
|
||||
},
|
||||
|
||||
// Setters
|
||||
setItems(items: BoardItem[]) {
|
||||
canvasItems = items;
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoadingCanvasItems = loading;
|
||||
},
|
||||
|
||||
setMode(mode: CanvasMode) {
|
||||
canvasMode = mode;
|
||||
},
|
||||
|
||||
setShowGrid(show: boolean) {
|
||||
showGrid = show;
|
||||
},
|
||||
|
||||
setSnapToGrid(snap: boolean) {
|
||||
snapToGrid = snap;
|
||||
},
|
||||
|
||||
setGridSize(size: number) {
|
||||
gridSize = size;
|
||||
},
|
||||
|
||||
setShowPropertiesPanel(show: boolean) {
|
||||
showPropertiesPanel = show;
|
||||
},
|
||||
|
||||
// Item management
|
||||
addItem(item: BoardItem) {
|
||||
canvasItems = [...canvasItems, item];
|
||||
saveToHistory();
|
||||
},
|
||||
|
||||
updateItem(id: string, updates: Partial<BoardItem>) {
|
||||
canvasItems = canvasItems.map((item) =>
|
||||
item.id === id ? ({ ...item, ...updates } as BoardItem) : item
|
||||
);
|
||||
saveToHistory();
|
||||
},
|
||||
|
||||
removeItem(id: string) {
|
||||
canvasItems = canvasItems.filter((item) => item.id !== id);
|
||||
selectedItemIds = selectedItemIds.filter((itemId) => itemId !== id);
|
||||
saveToHistory();
|
||||
},
|
||||
|
||||
removeSelectedItems() {
|
||||
const ids = selectedItemIds;
|
||||
canvasItems = canvasItems.filter((item) => !ids.includes(item.id));
|
||||
selectedItemIds = [];
|
||||
saveToHistory();
|
||||
},
|
||||
|
||||
// Selection management
|
||||
selectItem(id: string, multi = false) {
|
||||
if (multi) {
|
||||
if (selectedItemIds.includes(id)) {
|
||||
selectedItemIds = selectedItemIds.filter((itemId) => itemId !== id);
|
||||
} else {
|
||||
selectedItemIds = [...selectedItemIds, id];
|
||||
}
|
||||
} else {
|
||||
selectedItemIds = [id];
|
||||
}
|
||||
},
|
||||
|
||||
selectAll() {
|
||||
selectedItemIds = canvasItems.map((item) => item.id);
|
||||
},
|
||||
|
||||
deselectAll() {
|
||||
selectedItemIds = [];
|
||||
},
|
||||
|
||||
// Text editing
|
||||
startEditingText(id: string) {
|
||||
editingTextId = id;
|
||||
},
|
||||
|
||||
stopEditingText() {
|
||||
editingTextId = null;
|
||||
},
|
||||
|
||||
// Z-index management
|
||||
bringToFront(id: string) {
|
||||
const maxZIndex = Math.max(...canvasItems.map((item) => item.zIndex));
|
||||
canvasStore.updateItem(id, { zIndex: maxZIndex + 1 });
|
||||
},
|
||||
|
||||
sendToBack(id: string) {
|
||||
const minZIndex = Math.min(...canvasItems.map((item) => item.zIndex));
|
||||
canvasStore.updateItem(id, { zIndex: minZIndex - 1 });
|
||||
},
|
||||
|
||||
moveForward(id: string) {
|
||||
const item = canvasItems.find((i) => i.id === id);
|
||||
if (!item) return;
|
||||
|
||||
const itemsAbove = canvasItems.filter((i) => i.zIndex > item.zIndex);
|
||||
if (itemsAbove.length === 0) return;
|
||||
|
||||
const nextZIndex = Math.min(...itemsAbove.map((i) => i.zIndex));
|
||||
canvasStore.updateItem(id, { zIndex: nextZIndex + 0.5 });
|
||||
},
|
||||
|
||||
moveBackward(id: string) {
|
||||
const item = canvasItems.find((i) => i.id === id);
|
||||
if (!item) return;
|
||||
|
||||
const itemsBelow = canvasItems.filter((i) => i.zIndex < item.zIndex);
|
||||
if (itemsBelow.length === 0) return;
|
||||
|
||||
const prevZIndex = Math.max(...itemsBelow.map((i) => i.zIndex));
|
||||
canvasStore.updateItem(id, { zIndex: prevZIndex - 0.5 });
|
||||
},
|
||||
|
||||
// Zoom functions
|
||||
zoomIn() {
|
||||
canvasZoom = Math.min(canvasZoom * 1.2, 5);
|
||||
},
|
||||
|
||||
zoomOut() {
|
||||
canvasZoom = Math.max(canvasZoom / 1.2, 0.1);
|
||||
},
|
||||
|
||||
setZoom(zoom: number) {
|
||||
canvasZoom = zoom;
|
||||
},
|
||||
|
||||
setPan(pan: { x: number; y: number }) {
|
||||
canvasPan = pan;
|
||||
},
|
||||
|
||||
zoomToFit(
|
||||
containerWidth: number,
|
||||
containerHeight: number,
|
||||
boardWidth: number,
|
||||
boardHeight: number
|
||||
) {
|
||||
const scaleX = containerWidth / boardWidth;
|
||||
const scaleY = containerHeight / boardHeight;
|
||||
const scale = Math.min(scaleX, scaleY) * 0.9;
|
||||
canvasZoom = scale;
|
||||
canvasPan = { x: 0, y: 0 };
|
||||
},
|
||||
|
||||
resetZoom() {
|
||||
canvasZoom = 1;
|
||||
canvasPan = { x: 0, y: 0 };
|
||||
},
|
||||
|
||||
// History management
|
||||
undo() {
|
||||
if (canvasHistoryIndex <= 0) return;
|
||||
|
||||
const prevState = canvasHistory[canvasHistoryIndex - 1];
|
||||
canvasItems = JSON.parse(JSON.stringify(prevState.items));
|
||||
canvasHistoryIndex--;
|
||||
},
|
||||
|
||||
redo() {
|
||||
if (canvasHistoryIndex >= canvasHistory.length - 1) return;
|
||||
|
||||
const nextState = canvasHistory[canvasHistoryIndex + 1];
|
||||
canvasItems = JSON.parse(JSON.stringify(nextState.items));
|
||||
canvasHistoryIndex++;
|
||||
},
|
||||
|
||||
clearHistory() {
|
||||
canvasHistory = [];
|
||||
canvasHistoryIndex = -1;
|
||||
},
|
||||
|
||||
// Reset
|
||||
reset() {
|
||||
canvasItems = [];
|
||||
selectedItemIds = [];
|
||||
canvasZoom = 1;
|
||||
canvasPan = { x: 0, y: 0 };
|
||||
canvasMode = 'select';
|
||||
editingTextId = null;
|
||||
canvasStore.clearHistory();
|
||||
},
|
||||
|
||||
// Grid snapping
|
||||
snapPositionToGrid(x: number, y: number): { x: number; y: number } {
|
||||
if (!snapToGrid) return { x, y };
|
||||
return {
|
||||
x: Math.round(x / gridSize) * gridSize,
|
||||
y: Math.round(y / gridSize) * gridSize,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Internal helper
|
||||
function saveToHistory() {
|
||||
// Remove any history after current index
|
||||
const newHistory = canvasHistory.slice(0, canvasHistoryIndex + 1);
|
||||
|
||||
// Add current state
|
||||
newHistory.push({
|
||||
items: JSON.parse(JSON.stringify(canvasItems)),
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// Limit history to 50 states
|
||||
if (newHistory.length > 50) {
|
||||
newHistory.shift();
|
||||
}
|
||||
|
||||
canvasHistory = newHistory;
|
||||
canvasHistoryIndex = newHistory.length - 1;
|
||||
}
|
||||
|
||||
// Export for backwards compatibility
|
||||
export function getCanvasItems() {
|
||||
return canvasItems;
|
||||
}
|
||||
|
||||
export function getSelectedItemIds() {
|
||||
return selectedItemIds;
|
||||
}
|
||||
|
||||
export function getCanvasZoom() {
|
||||
return canvasZoom;
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* Context Menu Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Image } from '$lib/api/images';
|
||||
|
||||
interface ContextMenuState {
|
||||
visible: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
image: Image | null;
|
||||
showTagSubmenu: boolean;
|
||||
submenuX: number;
|
||||
submenuY: number;
|
||||
}
|
||||
|
||||
const initialState: ContextMenuState = {
|
||||
visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
image: null,
|
||||
showTagSubmenu: false,
|
||||
submenuX: 0,
|
||||
submenuY: 0,
|
||||
};
|
||||
|
||||
let contextMenuState = $state<ContextMenuState>({ ...initialState });
|
||||
|
||||
export const contextMenuStore = {
|
||||
get state() {
|
||||
return contextMenuState;
|
||||
},
|
||||
get visible() {
|
||||
return contextMenuState.visible;
|
||||
},
|
||||
get x() {
|
||||
return contextMenuState.x;
|
||||
},
|
||||
get y() {
|
||||
return contextMenuState.y;
|
||||
},
|
||||
get image() {
|
||||
return contextMenuState.image;
|
||||
},
|
||||
get showTagSubmenu() {
|
||||
return contextMenuState.showTagSubmenu;
|
||||
},
|
||||
get submenuX() {
|
||||
return contextMenuState.submenuX;
|
||||
},
|
||||
get submenuY() {
|
||||
return contextMenuState.submenuY;
|
||||
},
|
||||
|
||||
show(x: number, y: number, image: Image) {
|
||||
contextMenuState = {
|
||||
visible: true,
|
||||
x,
|
||||
y,
|
||||
image,
|
||||
showTagSubmenu: false,
|
||||
submenuX: 0,
|
||||
submenuY: 0,
|
||||
};
|
||||
},
|
||||
|
||||
hide() {
|
||||
contextMenuState = { ...initialState };
|
||||
},
|
||||
|
||||
openTagSubmenu(x: number, y: number) {
|
||||
contextMenuState = {
|
||||
...contextMenuState,
|
||||
showTagSubmenu: true,
|
||||
submenuX: x,
|
||||
submenuY: y,
|
||||
};
|
||||
},
|
||||
|
||||
hideTagSubmenu() {
|
||||
contextMenuState = {
|
||||
...contextMenuState,
|
||||
showTagSubmenu: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Export for backwards compatibility
|
||||
export function showContextMenu(x: number, y: number, image: Image) {
|
||||
contextMenuStore.show(x, y, image);
|
||||
}
|
||||
|
||||
export function hideContextMenu() {
|
||||
contextMenuStore.hide();
|
||||
}
|
||||
|
||||
export function showTagSubmenu(x: number, y: number) {
|
||||
contextMenuStore.openTagSubmenu(x, y);
|
||||
}
|
||||
|
||||
export function hideTagSubmenu() {
|
||||
contextMenuStore.hideTagSubmenu();
|
||||
}
|
||||
|
||||
export function getContextMenu() {
|
||||
return contextMenuState;
|
||||
}
|
||||
|
||||
// Re-export for compatibility
|
||||
export { contextMenuState as contextMenu };
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/**
|
||||
* Explore Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Image } from '$lib/api/images';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let exploreImages = $state<Image[]>([]);
|
||||
let isLoadingExplore = $state(false);
|
||||
let hasMoreExplore = $state(true);
|
||||
let currentExplorePage = $state(1);
|
||||
let exploreSortBy = $state<'recent' | 'popular' | 'trending'>('recent');
|
||||
let exploreSearchQuery = $state('');
|
||||
let showExploreFavoritesOnly = $state(false);
|
||||
|
||||
export const exploreStore = {
|
||||
get images() {
|
||||
return exploreImages;
|
||||
},
|
||||
get isLoading() {
|
||||
return isLoadingExplore;
|
||||
},
|
||||
get hasMore() {
|
||||
return hasMoreExplore;
|
||||
},
|
||||
get currentPage() {
|
||||
return currentExplorePage;
|
||||
},
|
||||
get sortBy() {
|
||||
return exploreSortBy;
|
||||
},
|
||||
get searchQuery() {
|
||||
return exploreSearchQuery;
|
||||
},
|
||||
get showFavoritesOnly() {
|
||||
return showExploreFavoritesOnly;
|
||||
},
|
||||
|
||||
setImages(images: Image[]) {
|
||||
exploreImages = images;
|
||||
},
|
||||
|
||||
appendImages(images: Image[]) {
|
||||
exploreImages = [...exploreImages, ...images];
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoadingExplore = loading;
|
||||
},
|
||||
|
||||
setHasMore(more: boolean) {
|
||||
hasMoreExplore = more;
|
||||
},
|
||||
|
||||
setCurrentPage(page: number) {
|
||||
currentExplorePage = page;
|
||||
},
|
||||
|
||||
incrementPage() {
|
||||
currentExplorePage++;
|
||||
},
|
||||
|
||||
setSortBy(sort: 'recent' | 'popular' | 'trending') {
|
||||
exploreSortBy = sort;
|
||||
// Reset when changing sort
|
||||
exploreImages = [];
|
||||
currentExplorePage = 1;
|
||||
hasMoreExplore = true;
|
||||
},
|
||||
|
||||
setSearchQuery(query: string) {
|
||||
exploreSearchQuery = query;
|
||||
// Reset when changing search
|
||||
exploreImages = [];
|
||||
currentExplorePage = 1;
|
||||
hasMoreExplore = true;
|
||||
},
|
||||
|
||||
setShowFavoritesOnly(favoritesOnly: boolean) {
|
||||
showExploreFavoritesOnly = favoritesOnly;
|
||||
// Reset when changing filter
|
||||
exploreImages = [];
|
||||
currentExplorePage = 1;
|
||||
hasMoreExplore = true;
|
||||
},
|
||||
|
||||
reset() {
|
||||
exploreImages = [];
|
||||
isLoadingExplore = false;
|
||||
hasMoreExplore = true;
|
||||
currentExplorePage = 1;
|
||||
exploreSortBy = 'recent';
|
||||
exploreSearchQuery = '';
|
||||
showExploreFavoritesOnly = false;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getExploreImages() {
|
||||
return exploreImages;
|
||||
}
|
||||
|
||||
export function getExploreSortBy() {
|
||||
return exploreSortBy;
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/**
|
||||
* Generate Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let isGenerating = $state(false);
|
||||
let generationProgress = $state('');
|
||||
let generationError = $state('');
|
||||
let currentGenerationId = $state<string | null>(null);
|
||||
|
||||
export const generateStore = {
|
||||
get isGenerating() {
|
||||
return isGenerating;
|
||||
},
|
||||
get generationProgress() {
|
||||
return generationProgress;
|
||||
},
|
||||
get generationError() {
|
||||
return generationError;
|
||||
},
|
||||
get currentGenerationId() {
|
||||
return currentGenerationId;
|
||||
},
|
||||
|
||||
startGeneration(generationId?: string) {
|
||||
isGenerating = true;
|
||||
generationProgress = 'Starting...';
|
||||
generationError = '';
|
||||
currentGenerationId = generationId || null;
|
||||
},
|
||||
|
||||
updateProgress(progress: string) {
|
||||
generationProgress = progress;
|
||||
},
|
||||
|
||||
setError(error: string) {
|
||||
generationError = error;
|
||||
isGenerating = false;
|
||||
},
|
||||
|
||||
completeGeneration() {
|
||||
isGenerating = false;
|
||||
generationProgress = 'Complete!';
|
||||
currentGenerationId = null;
|
||||
},
|
||||
|
||||
cancelGeneration() {
|
||||
isGenerating = false;
|
||||
generationProgress = '';
|
||||
generationError = '';
|
||||
currentGenerationId = null;
|
||||
},
|
||||
|
||||
reset() {
|
||||
isGenerating = false;
|
||||
generationProgress = '';
|
||||
generationError = '';
|
||||
currentGenerationId = null;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getIsGenerating() {
|
||||
return isGenerating;
|
||||
}
|
||||
|
||||
export function getGenerationProgress() {
|
||||
return generationProgress;
|
||||
}
|
||||
|
||||
export function getGenerationError() {
|
||||
return generationError;
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/**
|
||||
* Images Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Image } from '$lib/api/images';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let images = $state<Image[]>([]);
|
||||
let selectedImage = $state<Image | null>(null);
|
||||
let isLoading = $state(false);
|
||||
let hasMore = $state(true);
|
||||
let currentPage = $state(1);
|
||||
let showFavoritesOnly = $state(false);
|
||||
|
||||
export const imagesStore = {
|
||||
get images() {
|
||||
return images;
|
||||
},
|
||||
get selectedImage() {
|
||||
return selectedImage;
|
||||
},
|
||||
get isLoading() {
|
||||
return isLoading;
|
||||
},
|
||||
get hasMore() {
|
||||
return hasMore;
|
||||
},
|
||||
get currentPage() {
|
||||
return currentPage;
|
||||
},
|
||||
get showFavoritesOnly() {
|
||||
return showFavoritesOnly;
|
||||
},
|
||||
|
||||
setImages(newImages: Image[]) {
|
||||
images = newImages;
|
||||
},
|
||||
|
||||
appendImages(newImages: Image[]) {
|
||||
images = [...images, ...newImages];
|
||||
},
|
||||
|
||||
addImage(image: Image) {
|
||||
images = [image, ...images];
|
||||
},
|
||||
|
||||
updateImage(id: string, updates: Partial<Image>) {
|
||||
images = images.map((img) => (img.id === id ? { ...img, ...updates } : img));
|
||||
},
|
||||
|
||||
removeImage(id: string) {
|
||||
images = images.filter((img) => img.id !== id);
|
||||
if (selectedImage?.id === id) {
|
||||
selectedImage = null;
|
||||
}
|
||||
},
|
||||
|
||||
selectImage(image: Image | null) {
|
||||
selectedImage = image;
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoading = loading;
|
||||
},
|
||||
|
||||
setHasMore(more: boolean) {
|
||||
hasMore = more;
|
||||
},
|
||||
|
||||
setCurrentPage(page: number) {
|
||||
currentPage = page;
|
||||
},
|
||||
|
||||
incrementPage() {
|
||||
currentPage++;
|
||||
},
|
||||
|
||||
setShowFavoritesOnly(favoritesOnly: boolean) {
|
||||
showFavoritesOnly = favoritesOnly;
|
||||
},
|
||||
|
||||
reset() {
|
||||
images = [];
|
||||
selectedImage = null;
|
||||
isLoading = false;
|
||||
hasMore = true;
|
||||
currentPage = 1;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
export function getSelectedImage() {
|
||||
return selectedImage;
|
||||
}
|
||||
|
||||
export function getIsLoading() {
|
||||
return isLoading;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* Models Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Model } from '$lib/api/models';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let models = $state<Model[]>([]);
|
||||
let selectedModel = $state<Model | null>(null);
|
||||
let isLoadingModels = $state(false);
|
||||
|
||||
export const modelsStore = {
|
||||
get models() {
|
||||
return models;
|
||||
},
|
||||
get selectedModel() {
|
||||
return selectedModel;
|
||||
},
|
||||
get isLoadingModels() {
|
||||
return isLoadingModels;
|
||||
},
|
||||
|
||||
setModels(newModels: Model[]) {
|
||||
models = newModels;
|
||||
// Auto-select default model if no model selected
|
||||
if (!selectedModel && newModels.length > 0) {
|
||||
const defaultModel = newModels.find((m) => m.isDefault) || newModels[0];
|
||||
selectedModel = defaultModel;
|
||||
}
|
||||
},
|
||||
|
||||
selectModel(model: Model | null) {
|
||||
selectedModel = model;
|
||||
},
|
||||
|
||||
selectModelById(id: string) {
|
||||
const model = models.find((m) => m.id === id);
|
||||
if (model) {
|
||||
selectedModel = model;
|
||||
}
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoadingModels = loading;
|
||||
},
|
||||
|
||||
reset() {
|
||||
models = [];
|
||||
selectedModel = null;
|
||||
isLoadingModels = false;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getModels() {
|
||||
return models;
|
||||
}
|
||||
|
||||
export function getSelectedModel() {
|
||||
return selectedModel;
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* Sidebar Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
const SIDEBAR_KEY = 'picture_sidebar_collapsed';
|
||||
|
||||
function loadInitialState(): boolean {
|
||||
if (!browser) return false;
|
||||
const saved = localStorage.getItem(SIDEBAR_KEY);
|
||||
return saved === 'true';
|
||||
}
|
||||
|
||||
let isSidebarCollapsed = $state(loadInitialState());
|
||||
|
||||
export const sidebarStore = {
|
||||
get isCollapsed() {
|
||||
return isSidebarCollapsed;
|
||||
},
|
||||
|
||||
toggle() {
|
||||
isSidebarCollapsed = !isSidebarCollapsed;
|
||||
if (browser) {
|
||||
localStorage.setItem(SIDEBAR_KEY, String(isSidebarCollapsed));
|
||||
}
|
||||
},
|
||||
|
||||
setCollapsed(collapsed: boolean) {
|
||||
isSidebarCollapsed = collapsed;
|
||||
if (browser) {
|
||||
localStorage.setItem(SIDEBAR_KEY, String(collapsed));
|
||||
}
|
||||
},
|
||||
|
||||
expand() {
|
||||
isSidebarCollapsed = false;
|
||||
if (browser) {
|
||||
localStorage.setItem(SIDEBAR_KEY, 'false');
|
||||
}
|
||||
},
|
||||
|
||||
collapse() {
|
||||
isSidebarCollapsed = true;
|
||||
if (browser) {
|
||||
localStorage.setItem(SIDEBAR_KEY, 'true');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Export for backwards compatibility
|
||||
export function getIsSidebarCollapsed() {
|
||||
return isSidebarCollapsed;
|
||||
}
|
||||
|
||||
export function toggleSidebar() {
|
||||
sidebarStore.toggle();
|
||||
}
|
||||
|
||||
export function setSidebarCollapsed(collapsed: boolean) {
|
||||
sidebarStore.setCollapsed(collapsed);
|
||||
}
|
||||
|
||||
// Re-export the writable-like interface for backward compatibility
|
||||
export { isSidebarCollapsed };
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/**
|
||||
* Tags Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import type { Tag } from '$lib/api/tags';
|
||||
|
||||
// State using Svelte 5 runes
|
||||
let tags = $state<Tag[]>([]);
|
||||
let selectedTags = $state<string[]>([]);
|
||||
let isLoadingTags = $state(false);
|
||||
|
||||
export const tagsStore = {
|
||||
get tags() {
|
||||
return tags;
|
||||
},
|
||||
get selectedTags() {
|
||||
return selectedTags;
|
||||
},
|
||||
get isLoadingTags() {
|
||||
return isLoadingTags;
|
||||
},
|
||||
|
||||
setTags(newTags: Tag[]) {
|
||||
tags = newTags;
|
||||
},
|
||||
|
||||
addTag(tag: Tag) {
|
||||
tags = [...tags, tag];
|
||||
},
|
||||
|
||||
updateTag(id: string, updates: Partial<Tag>) {
|
||||
tags = tags.map((tag) => (tag.id === id ? { ...tag, ...updates } : tag));
|
||||
},
|
||||
|
||||
removeTag(id: string) {
|
||||
tags = tags.filter((tag) => tag.id !== id);
|
||||
selectedTags = selectedTags.filter((tagId) => tagId !== id);
|
||||
},
|
||||
|
||||
selectTag(tagId: string) {
|
||||
if (!selectedTags.includes(tagId)) {
|
||||
selectedTags = [...selectedTags, tagId];
|
||||
}
|
||||
},
|
||||
|
||||
deselectTag(tagId: string) {
|
||||
selectedTags = selectedTags.filter((id) => id !== tagId);
|
||||
},
|
||||
|
||||
toggleTag(tagId: string) {
|
||||
if (selectedTags.includes(tagId)) {
|
||||
selectedTags = selectedTags.filter((id) => id !== tagId);
|
||||
} else {
|
||||
selectedTags = [...selectedTags, tagId];
|
||||
}
|
||||
},
|
||||
|
||||
setSelectedTags(tagIds: string[]) {
|
||||
selectedTags = tagIds;
|
||||
},
|
||||
|
||||
clearSelectedTags() {
|
||||
selectedTags = [];
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
isLoadingTags = loading;
|
||||
},
|
||||
|
||||
reset() {
|
||||
tags = [];
|
||||
selectedTags = [];
|
||||
isLoadingTags = false;
|
||||
},
|
||||
};
|
||||
|
||||
// Export individual getters for backwards compatibility
|
||||
export function getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
export function getSelectedTags() {
|
||||
return selectedTags;
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/**
|
||||
* UI Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
const UI_VISIBLE_KEY = 'picture_ui_visible';
|
||||
|
||||
function loadInitialState(): boolean {
|
||||
if (!browser) return true;
|
||||
const saved = localStorage.getItem(UI_VISIBLE_KEY);
|
||||
return saved !== 'false'; // Default to true
|
||||
}
|
||||
|
||||
let isUIVisible = $state(loadInitialState());
|
||||
let showKeyboardShortcuts = $state(false);
|
||||
|
||||
export const uiStore = {
|
||||
get isVisible() {
|
||||
return isUIVisible;
|
||||
},
|
||||
get showKeyboardShortcuts() {
|
||||
return showKeyboardShortcuts;
|
||||
},
|
||||
|
||||
toggle() {
|
||||
isUIVisible = !isUIVisible;
|
||||
if (browser) {
|
||||
localStorage.setItem(UI_VISIBLE_KEY, String(isUIVisible));
|
||||
}
|
||||
},
|
||||
|
||||
setVisible(visible: boolean) {
|
||||
isUIVisible = visible;
|
||||
if (browser) {
|
||||
localStorage.setItem(UI_VISIBLE_KEY, String(visible));
|
||||
}
|
||||
},
|
||||
|
||||
show() {
|
||||
isUIVisible = true;
|
||||
if (browser) {
|
||||
localStorage.setItem(UI_VISIBLE_KEY, 'true');
|
||||
}
|
||||
},
|
||||
|
||||
hide() {
|
||||
isUIVisible = false;
|
||||
if (browser) {
|
||||
localStorage.setItem(UI_VISIBLE_KEY, 'false');
|
||||
}
|
||||
},
|
||||
|
||||
setShowKeyboardShortcuts(show: boolean) {
|
||||
showKeyboardShortcuts = show;
|
||||
},
|
||||
|
||||
toggleKeyboardShortcuts() {
|
||||
showKeyboardShortcuts = !showKeyboardShortcuts;
|
||||
},
|
||||
};
|
||||
|
||||
// Export for backwards compatibility
|
||||
export function toggleUI() {
|
||||
uiStore.toggle();
|
||||
}
|
||||
|
||||
export function getIsUIVisible() {
|
||||
return isUIVisible;
|
||||
}
|
||||
|
||||
// Re-export for compatibility
|
||||
export { isUIVisible, showKeyboardShortcuts };
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/**
|
||||
* View Store - Svelte 5 Runes Version
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export type ViewMode = 'single' | 'grid3' | 'grid5';
|
||||
|
||||
const VIEW_MODE_KEY = 'picture_view_mode';
|
||||
|
||||
function loadInitialViewMode(): ViewMode {
|
||||
if (!browser) {
|
||||
return 'grid3';
|
||||
}
|
||||
const saved = localStorage.getItem(VIEW_MODE_KEY) as ViewMode | null;
|
||||
return saved || 'grid3';
|
||||
}
|
||||
|
||||
let viewMode = $state<ViewMode>(loadInitialViewMode());
|
||||
|
||||
export const viewStore = {
|
||||
get mode() {
|
||||
return viewMode;
|
||||
},
|
||||
|
||||
set(mode: ViewMode) {
|
||||
viewMode = mode;
|
||||
if (browser) {
|
||||
localStorage.setItem(VIEW_MODE_KEY, mode);
|
||||
}
|
||||
},
|
||||
|
||||
cycle() {
|
||||
const modes: ViewMode[] = ['single', 'grid3', 'grid5'];
|
||||
const currentIndex = modes.indexOf(viewMode);
|
||||
const nextMode = modes[(currentIndex + 1) % modes.length];
|
||||
viewStore.set(nextMode);
|
||||
},
|
||||
|
||||
setSingle() {
|
||||
viewStore.set('single');
|
||||
},
|
||||
|
||||
setGrid3() {
|
||||
viewStore.set('grid3');
|
||||
},
|
||||
|
||||
setGrid5() {
|
||||
viewStore.set('grid5');
|
||||
},
|
||||
};
|
||||
|
||||
// Export for backwards compatibility
|
||||
export function setViewMode(mode: ViewMode) {
|
||||
viewStore.set(mode);
|
||||
}
|
||||
|
||||
export function cycleViewMode() {
|
||||
viewStore.cycle();
|
||||
}
|
||||
|
||||
export function getViewMode() {
|
||||
return viewMode;
|
||||
}
|
||||
|
||||
// Re-export for compatibility
|
||||
export { viewMode };
|
||||
|
|
@ -280,7 +280,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Create Board Modal -->
|
||||
<Modal open={$showCreateBoardModal} onClose={() => showCreateBoardModal.set(false)}>
|
||||
<Modal visible={$showCreateBoardModal} onClose={() => showCreateBoardModal.set(false)}>
|
||||
<div class="p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Neues Board erstellen</h2>
|
||||
|
||||
|
|
@ -339,7 +339,7 @@
|
|||
</Modal>
|
||||
|
||||
<!-- Delete Confirmation Modal -->
|
||||
<Modal open={showDeleteModal} onClose={() => (showDeleteModal = false)}>
|
||||
<Modal visible={showDeleteModal} onClose={() => (showDeleteModal = false)}>
|
||||
<div class="p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Board löschen?</h2>
|
||||
<p class="mt-4 text-gray-600 dark:text-gray-400">
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { MANACORE_SHARED_PACKAGES, getBuildDefines } from '@manacore/shared-vite
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
sveltekit(),
|
||||
tailwindcss() as any,
|
||||
sveltekit() as any,
|
||||
SvelteKitPWA(
|
||||
createPWAConfig({
|
||||
name: 'Picture - KI Bildgenerator',
|
||||
|
|
@ -16,7 +16,7 @@ export default defineConfig({
|
|||
description: 'KI-gestützte Bildgenerierung',
|
||||
themeColor: '#ec4899',
|
||||
})
|
||||
),
|
||||
) as any,
|
||||
],
|
||||
server: {
|
||||
port: 5175,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue