├── src
├── vite-env.d.ts
├── App.tsx
├── main.tsx
├── types
│ └── index.ts
├── store
│ └── projectStore.ts
├── components
│ ├── RadialMenu.tsx
│ ├── ProjectCard.tsx
│ ├── PostEditor.tsx
│ ├── PublicSitePreview.tsx
│ └── ProjectEditor.tsx
├── App.css
├── utils
│ └── index.ts
├── assets
│ └── react.svg
└── StudioDashboard.tsx
├── src-tauri
├── build.rs
├── resources
│ └── templates
│ │ └── starter-site
│ │ ├── .astro
│ │ ├── content-assets.mjs
│ │ ├── content-modules.mjs
│ │ ├── types.d.ts
│ │ ├── settings.json
│ │ └── data-store.json
│ │ ├── tsconfig.json
│ │ ├── astro.config.mjs
│ │ ├── package.json
│ │ ├── public
│ │ └── favicon.svg
│ │ ├── src
│ │ ├── layouts
│ │ │ ├── ProjectLayout.astro
│ │ │ ├── PostLayout.astro
│ │ │ └── Layout.astro
│ │ └── pages
│ │ │ └── index.astro
│ │ └── README.md
├── icons
│ ├── icon.ico
│ ├── icon.png
│ ├── 128x128.png
│ ├── 32x32.png
│ ├── icon.icns
│ ├── StoreLogo.png
│ ├── 128x128@2x.png
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ ├── Square30x30Logo.png
│ ├── Square310x310Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ └── Square89x89Logo.png
├── .gitignore
├── src
│ ├── main.rs
│ └── lib.rs
├── capabilities
│ └── default.json
├── Cargo.toml
└── tauri.conf.json
├── .cursor
└── rules
│ ├── cursor-rules-location.mdc
│ ├── agent-behavior.mdc
│ ├── no-build-commands.mdc
│ └── delegate-testing-to-user.mdc
├── .vscode
└── extensions.json
├── postcss.config.js
├── tailwind.config.js
├── tsconfig.node.json
├── .gitignore
├── README.md
├── index.html
├── tsconfig.json
├── vite.config.ts
├── package.json
├── public
├── vite.svg
└── tauri.svg
└── bun.lock
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
Project posts
36 | 37 | ))} 38 |No projects found yet. Create your first project using Studio Builder Desktop!
43 |/g, '
') 45 | 46 | // Lists 47 | .replace(/
/g, 54 | '' 55 | ) 56 | 57 | // Code blocks 58 | .replace( 59 | //g, 60 | '' 61 | ) 62 | 63 | // Inline code 64 | .replace(//g, '') 65 | 66 | // Links 67 | .replace(//g, '') 75 | .replace(//g, '') 76 | 77 | // Horizontal rules 78 | .replace(/
/g, '
') 79 | 80 | // Tables 81 | .replace( 82 | //g, 83 | '
') 86 | .replace(//g, '') 87 | .replace(//g, '') 88 | .replace(/' 84 | ) 85 | .replace(/<\/table>/g, '
/g, ' ') 89 | .replace(/ /g, ' ') 90 | .replace(/ /g, ' '); 91 | 92 | return html; 93 | } catch (error) { 94 | console.error('Error parsing markdown:', error); 95 | return ` Error parsing markdown content
`; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-tauri/resources/templates/starter-site/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | export interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |{title} 17 | 144 | 145 | 146 |147 | 148 | -------------------------------------------------------------------------------- /src/components/PostEditor.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { Post } from '../types'; 3 | 4 | interface PostEditorProps { 5 | post: Post; 6 | onClose: () => void; 7 | onSave: (content: string) => Promise ; 8 | onDelete: () => Promise ; 9 | } 10 | 11 | export default function PostEditor({ post, onClose, onSave, onDelete }: PostEditorProps) { 12 | const [content, setContent] = useState(post.content || ''); 13 | const [title, setTitle] = useState(post.title || ''); 14 | const [saving, setSaving] = useState(false); 15 | const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); 16 | 17 | useEffect(() => { 18 | const esc = (e: KeyboardEvent) => { 19 | if (e.key === 'Escape') onClose(); 20 | }; 21 | window.addEventListener('keydown', esc); 22 | return () => window.removeEventListener('keydown', esc); 23 | }, [onClose]); 24 | 25 | // Update local state when post prop changes 26 | useEffect(() => { 27 | setContent(post.content || ''); 28 | setTitle(post.title || ''); 29 | }, [post.content, post.title]); 30 | 31 | const handleSave = async () => { 32 | setSaving(true); 33 | try { 34 | // Update content with new title in frontmatter 35 | const updatedContent = updateContentWithTitle(content, title); 36 | await onSave(updatedContent); 37 | } catch (error) { 38 | console.error('Failed to save post:', error); 39 | } finally { 40 | setSaving(false); 41 | } 42 | }; 43 | 44 | const updateContentWithTitle = (content: string, newTitle: string) => { 45 | // Check if content has frontmatter 46 | if (content.startsWith('---\n')) { 47 | // Replace existing title in frontmatter 48 | const frontmatterEnd = content.indexOf('\n---\n', 4); 49 | if (frontmatterEnd !== -1) { 50 | const frontmatter = content.substring(4, frontmatterEnd); 51 | const bodyContent = content.substring(frontmatterEnd + 5); 52 | 53 | // Replace or add title in frontmatter 54 | const updatedFrontmatter = 55 | frontmatter.replace(/^title:.*$/m, `title: "${newTitle}"`) || 56 | (frontmatter.includes('title:') ? frontmatter : `title: "${newTitle}"\n${frontmatter}`); 57 | 58 | return `---\n${updatedFrontmatter}\n---\n${bodyContent}`; 59 | } 60 | } 61 | 62 | // Add frontmatter if it doesn't exist 63 | return `---\ntitle: "${newTitle}"\ndate: ${new Date().toISOString().split('T')[0]}\n---\n\n${content}`; 64 | }; 65 | 66 | const handleDelete = async () => { 67 | console.log('PostEditor handleDelete called, post:', post); 68 | console.log('Proceeding with deletion'); 69 | try { 70 | await onDelete(); 71 | console.log('onDelete function completed successfully'); 72 | onClose(); 73 | } catch (error) { 74 | console.error('Failed to delete post:', error); 75 | } 76 | }; 77 | 78 | const confirmDelete = () => { 79 | setShowDeleteConfirm(true); 80 | }; 81 | 82 | return ( 83 | 84 |144 | ); 145 | } 146 | -------------------------------------------------------------------------------- /src/components/PublicSitePreview.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { motion } from 'framer-motion'; 3 | import { X as Close } from 'lucide-react'; 4 | import { invoke } from '@tauri-apps/api/core'; 5 | import { Project, Post } from '../types'; 6 | import { parseMarkdown } from '../utils'; 7 | 8 | interface PublicSitePreviewProps { 9 | studioName: string; 10 | projects: Project[]; 11 | onClose: () => void; 12 | } 13 | 14 | type View = { type: 'home' } | { type: 'project'; project: Project } | { type: 'post'; project: Project; post: Post }; 15 | 16 | export default function PublicSitePreview({ studioName, projects, onClose }: PublicSitePreviewProps) { 17 | const [view, setView] = useState85 | 92 | 100 |101 | setTitle(e.target.value)} 105 | className="mb-6 text-2xl font-semibold text-neutral-50 flex-shrink-0 bg-transparent border-none outline-none focus:bg-neutral-800/20 rounded px-2 py-1 transition-colors" 106 | placeholder="Post title..." 107 | /> 108 |({ type: 'home' }); 18 | const [projectsWithPosts, setProjectsWithPosts] = useState ([]); 19 | const [loading, setLoading] = useState(true); 20 | 21 | // Load all posts for all projects when component mounts 22 | useEffect(() => { 23 | const loadAllContent = async () => { 24 | try { 25 | setLoading(true); 26 | const projectsWithLoadedPosts = await Promise.all( 27 | projects.map(async (project) => { 28 | try { 29 | const posts = await invoke ('list_posts', { 30 | projectName: project.folder_name, 31 | }); 32 | return { ...project, posts }; 33 | } catch (error) { 34 | console.error(`Failed to load posts for project ${project.name}:`, error); 35 | return { ...project, posts: [] }; 36 | } 37 | }) 38 | ); 39 | setProjectsWithPosts(projectsWithLoadedPosts); 40 | } catch (error) { 41 | console.error('Failed to load project content:', error); 42 | setProjectsWithPosts(projects); 43 | } finally { 44 | setLoading(false); 45 | } 46 | }; 47 | 48 | loadAllContent(); 49 | }, [projects]); 50 | 51 | const loadFullPost = async (project: Project, post: Post) => { 52 | try { 53 | const fullPost = await invoke ('read_post', { 54 | projectName: project.folder_name, 55 | slug: post.slug, 56 | }); 57 | setView({ type: 'post', project, post: fullPost }); 58 | } catch (error) { 59 | console.error('Failed to load full post:', error); 60 | setView({ type: 'post', project, post }); 61 | } 62 | }; 63 | 64 | const back = () => { 65 | if (view.type === 'post') setView({ type: 'project', project: view.project }); 66 | else if (view.type === 'project') setView({ type: 'home' }); 67 | else onClose(); 68 | }; 69 | 70 | return ( 71 | 77 | 202 | ); 203 | } 204 | -------------------------------------------------------------------------------- /src/StudioDashboard.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useRef, useEffect } from 'react'; 2 | import { motion, AnimatePresence } from 'framer-motion'; 3 | import { Plus, FolderPlus, Eye } from 'lucide-react'; 4 | import { invoke } from '@tauri-apps/api/core'; 5 | 6 | // Import our extracted components and utilities 7 | import { Project } from './types'; 8 | import { polar } from './utils'; 9 | import { useProjectStore } from './store/projectStore'; 10 | import ProjectCard from './components/ProjectCard'; 11 | import RadialMenu from './components/RadialMenu'; 12 | import ProjectEditor from './components/ProjectEditor'; 13 | import PublicSitePreview from './components/PublicSitePreview'; 14 | 15 | /* ────────────────────────────────────────────────────────── 16 | MAIN – Studio Dashboard 17 | ────────────────────────────────────────────────────────── */ 18 | export default function StudioDashboard() { 19 | // State for projects - now loaded dynamically 20 | const [projects, setProjects] = useState78 | 95 | {studioName} 96 | 105 |106 |107 | {loading ? ( 108 |201 |109 |111 | ) : ( 112 | <> 113 | {view.type === 'home' && ( 114 |Loading content...110 |115 |150 | )} 151 | > 152 | )} 153 | {view.type === 'project' && ( 154 |{studioName}
116 |117 | {projectsWithPosts.map((p) => ( 118 |143 | {projectsWithPosts.length === 0 && ( 144 |setView({ type: 'project', project: p })} 121 | className="cursor-pointer rounded-2xl border bg-neutral-50 p-6 shadow-sm transition hover:shadow-md" 122 | > 123 |141 | ))} 142 |{p.label}
124 |{p.posts.length} posts
125 | {p.posts.length > 0 && ( 126 |127 |139 | )} 140 |Recent posts:
128 |129 | {p.posts.slice(0, 3).map((post) => ( 130 |
135 | {p.posts.length > 3 && ( 136 |- 131 | • {post.title} 132 |
133 | ))} 134 |+{p.posts.length - 3} more
137 | )} 138 |145 |148 | )} 149 |No projects found
146 |Create your first project to get started!
147 |155 |176 | )} 177 | {view.type === 'post' && ( 178 |{view.project.label}
156 | {view.project.posts.length > 0 ? ( 157 |158 | {view.project.posts.map((post) => ( 159 |169 | ) : ( 170 |loadFullPost(view.project, post)} 162 | className="cursor-pointer rounded-xl border bg-neutral-50 p-5 shadow-sm transition hover:shadow-md" 163 | > 164 |167 | ))} 168 |{post.title}
165 |{post.filename} • Click to read
166 |171 |174 | )} 175 |No posts in this project yet
172 |Posts will appear here once you create them
173 |179 |199 | )} 200 |180 |184 |181 | {view.project.label} • {view.post.filename} 182 |
183 |{view.post.title}
185 |186 | {view.post.content ? ( 187 | 191 | ) : ( 192 |198 |193 |196 | )} 197 |No content available
194 |This post appears to be empty
195 |([]); 21 | const [menuOpen, setMenuOpen] = useState(false); 22 | const [openProj, setOpenProj] = useState<{ project: Project; position?: { x: number; y: number } } | null>(null); 23 | const [previewOpen, setPreviewOpen] = useState(false); 24 | const containerRef = useRef (null); 25 | 26 | // Zustand store for persisting project positions 27 | const { setProjectPosition, getProjectPosition } = useProjectStore(); 28 | 29 | // First launch handler - sets up studio workspace 30 | const initializeStudio = async () => { 31 | try { 32 | console.log('Checking studio workspace...'); 33 | 34 | const result = await invoke<{ success: boolean; message: string; is_first_time: boolean }>('initialize_studio'); 35 | 36 | if (result.success) { 37 | alert(result.message); 38 | console.log(result.is_first_time ? 'Created new studio workspace' : 'Studio workspace already exists'); 39 | } else { 40 | throw new Error('Studio initialization failed'); 41 | } 42 | } catch (error) { 43 | console.error('Studio initialization failed:', error); 44 | alert(`❌ Failed to initialize studio workspace: ${error}\n\nPlease check file permissions and try again.`); 45 | } 46 | }; 47 | 48 | // Load projects from file system 49 | const loadProjects = async () => { 50 | try { 51 | const projectList = await invoke >('list_projects'); 52 | 53 | // Convert file system projects to UI projects with positions 54 | const uiProjects: Project[] = projectList.map((proj, idx) => { 55 | // Try to get stored position first, fallback to circular layout 56 | const storedPosition = getProjectPosition(proj.folder_name); 57 | let x, y; 58 | 59 | if (storedPosition) { 60 | x = storedPosition.x; 61 | y = storedPosition.y; 62 | } else { 63 | // Default circular layout for new projects 64 | const radius = 200; 65 | const angle = (360 / projectList.length) * idx; 66 | const polarCoords = polar(radius, angle); 67 | x = polarCoords.x; 68 | y = polarCoords.y; 69 | } 70 | 71 | return { 72 | id: proj.folder_name, // Use folder_name as unique ID 73 | label: proj.name, // Display name 74 | name: proj.name, // Display name 75 | folder_name: proj.folder_name, // Actual folder name 76 | path: proj.path, 77 | x, 78 | y, 79 | posts: [], // Posts will be loaded dynamically when project is opened 80 | }; 81 | }); 82 | 83 | setProjects(uiProjects); 84 | } catch (error) { 85 | console.error('Failed to load projects:', error); 86 | } 87 | }; 88 | 89 | // Create a new project 90 | const createNewProject = async () => { 91 | console.log('createNewProject called!'); 92 | 93 | // Generate a unique "untitled" name 94 | const generateUntitledName = () => { 95 | const baseName = 'untitled'; 96 | const existingNames = projects.map((p) => p.name || p.label); 97 | 98 | if (!existingNames.includes(baseName)) { 99 | return baseName; 100 | } 101 | 102 | let counter = 1; 103 | while (existingNames.includes(`${baseName}-${counter}`)) { 104 | counter++; 105 | } 106 | return `${baseName}-${counter}`; 107 | }; 108 | 109 | const name = generateUntitledName(); 110 | console.log('Creating project with name:', name); 111 | 112 | try { 113 | const newProject = await invoke<{ name: string; folder_name: string; path: string }>('create_project', { name }); 114 | console.log('Project created successfully:', newProject); 115 | 116 | // Reload projects to update the UI 117 | await loadProjects(); 118 | 119 | // No alert - just create silently for better UX 120 | console.log(`✅ Project '${newProject.name}' created at: ${newProject.path}`); 121 | } catch (error) { 122 | console.error('Failed to create project:', error); 123 | // Still show error alerts 124 | alert(`❌ Failed to create project: ${error}`); 125 | } 126 | }; 127 | 128 | // Auto-initialize studio and load projects on first launch 129 | useEffect(() => { 130 | const initAndLoad = async () => { 131 | await initializeStudio(); 132 | await loadProjects(); 133 | }; 134 | initAndLoad(); 135 | }, []); 136 | 137 | useEffect(() => { 138 | const handler = (e: KeyboardEvent) => { 139 | if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'p') { 140 | e.preventDefault(); 141 | addProject(); 142 | } 143 | }; 144 | window.addEventListener('keydown', handler); 145 | return () => window.removeEventListener('keydown', handler); 146 | }, [projects]); 147 | 148 | const addProject = () => { 149 | console.log('addProject called!'); 150 | createNewProject(); 151 | }; 152 | 153 | const updatePos = (id: string, x: number, y: number) => { 154 | // Update local state 155 | setProjects((prev) => prev.map((p) => (p.id === id ? { ...p, x, y } : p))); 156 | // Persist position to Zustand store 157 | setProjectPosition(id, x, y); 158 | }; 159 | 160 | const handleOpenProject = (project: Project, position?: { x: number; y: number }) => { 161 | setOpenProj({ project, position }); 162 | }; 163 | 164 | const handleUpdateProject = (updatedProject: Project) => { 165 | setProjects((prev) => prev.map((p) => (p.id === updatedProject.id ? updatedProject : p))); 166 | 167 | // Update the currently open project if it's the same one 168 | if (openProj && openProj.project.id === updatedProject.id) { 169 | setOpenProj({ ...openProj, project: updatedProject }); 170 | } 171 | }; 172 | 173 | const actions = [ 174 | { icon: FolderPlus, label: 'New Project', onClick: addProject }, 175 | { icon: Eye, label: 'Preview Site', onClick: () => setPreviewOpen(true) }, 176 | ]; 177 | 178 | return ( 179 | 180 |234 | ); 235 | } 236 | -------------------------------------------------------------------------------- /src/components/ProjectEditor.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | import { motion } from 'framer-motion'; 3 | import { Plus } from 'lucide-react'; 4 | import { invoke } from '@tauri-apps/api/core'; 5 | import { Project, Post } from '../types'; 6 | import PostEditor from './PostEditor'; 7 | 8 | interface ProjectEditorProps { 9 | project: Project; 10 | onClose: () => void; 11 | initialPosition?: { x: number; y: number }; 12 | onDeleteProject: (id: string) => void; 13 | onUpdateProject?: (updatedProject: Project) => void; 14 | } 15 | 16 | export default function ProjectEditor({ project, onClose, initialPosition, onUpdateProject }: ProjectEditorProps) { 17 | const [posts, setPosts] = useState181 |196 | 205 |182 |186 | {projects.map((p) => ( 187 |183 | ✶ Studio 184 | 185 |194 | ))} 195 | 206 | 207 | {openProj && ( 208 | 227 |214 | 225 | )} 226 |setOpenProj(null)} 217 | initialPosition={openProj.position} 218 | onDeleteProject={(id) => { 219 | setProjects((prev) => prev.filter((p) => p.id !== id)); 220 | setOpenProj(null); 221 | }} 222 | onUpdateProject={handleUpdateProject} 223 | /> 224 | 228 | {previewOpen && ( 229 | 232 |setPreviewOpen(false)} /> 230 | )} 231 | ⌘P – New Project
233 |([]); 18 | const [openPost, setOpenPost] = useState (null); 19 | const [loading, setLoading] = useState(true); 20 | const [isEditingTitle, setIsEditingTitle] = useState(false); 21 | const [editedTitle, setEditedTitle] = useState(project.label); 22 | const [currentProjectName, setCurrentProjectName] = useState(project.name || project.label); 23 | 24 | // Load posts when component mounts or project changes 25 | useEffect(() => { 26 | setCurrentProjectName(project.name || project.label); 27 | loadPosts(); 28 | }, [project.folder_name, project.name, project.label]); 29 | 30 | // Update edited title when project changes (for navigation) 31 | useEffect(() => { 32 | setEditedTitle(project.label); 33 | }, [project.label]); 34 | 35 | const loadPosts = async () => { 36 | if (!project.folder_name) return; 37 | 38 | try { 39 | setLoading(true); 40 | const result = await invoke ('list_posts', { projectName: project.folder_name }); 41 | setPosts(result); 42 | if (result.length > 0 && !openPost) { 43 | setOpenPost(result[0]); 44 | } 45 | } catch (error) { 46 | console.error('Failed to load posts:', error); 47 | } finally { 48 | setLoading(false); 49 | } 50 | }; 51 | 52 | const createPost = async () => { 53 | console.log('createPost called, project folder_name:', project.folder_name); 54 | if (!project.folder_name) { 55 | console.error('No project folder name available'); 56 | alert('Error: No project folder name available'); 57 | return; 58 | } 59 | 60 | try { 61 | const title = 'Untitled Post'; 62 | console.log('Creating post with title:', title, 'for project:', project.folder_name); 63 | const newPost = await invoke ('create_post', { 64 | projectName: project.folder_name, 65 | title, 66 | }); 67 | console.log('Post created successfully:', newPost); 68 | setPosts((prev) => [...prev, newPost]); 69 | setOpenPost(newPost); 70 | } catch (error) { 71 | console.error('Failed to create post:', error); 72 | alert(`Failed to create post: ${error}`); 73 | } 74 | }; 75 | 76 | const savePost = async (content: string) => { 77 | if (!openPost || !project.folder_name) return; 78 | 79 | try { 80 | await invoke('update_post', { 81 | projectName: project.folder_name, 82 | slug: openPost.slug, 83 | content, 84 | }); 85 | 86 | // Extract title from the updated content 87 | const updatedTitle = extractTitleFromContent(content); 88 | 89 | // Update both the post content and title in state 90 | setPosts((prev) => 91 | prev.map((post) => (post.slug === openPost.slug ? { ...post, content, title: updatedTitle } : post)) 92 | ); 93 | setOpenPost((prev) => (prev ? { ...prev, content, title: updatedTitle } : null)); 94 | } catch (error) { 95 | console.error('Failed to save post:', error); 96 | throw error; 97 | } 98 | }; 99 | 100 | const extractTitleFromContent = (content: string): string => { 101 | // Check if content has frontmatter 102 | if (content.startsWith('---\n')) { 103 | const frontmatterEnd = content.indexOf('\n---\n', 4); 104 | if (frontmatterEnd !== -1) { 105 | const frontmatter = content.substring(4, frontmatterEnd); 106 | const titleMatch = frontmatter.match(/^title:\s*"?([^"]*)"?$/m); 107 | if (titleMatch) { 108 | return titleMatch[1]; 109 | } 110 | } 111 | } 112 | 113 | // Fallback to first heading or default 114 | const headingMatch = content.match(/^#\s+(.+)$/m); 115 | if (headingMatch) { 116 | return headingMatch[1]; 117 | } 118 | 119 | return 'Untitled Post'; 120 | }; 121 | 122 | const deletePost = async () => { 123 | console.log('deletePost called, openPost:', openPost, 'project folder_name:', project.folder_name); 124 | if (!openPost || !project.folder_name) { 125 | console.error('Missing openPost or project folder_name'); 126 | return; 127 | } 128 | 129 | try { 130 | console.log('Deleting post:', openPost.slug, 'from project:', project.folder_name); 131 | await invoke('delete_post', { 132 | projectName: project.folder_name, 133 | slug: openPost.slug, 134 | }); 135 | console.log('Post deleted successfully'); 136 | 137 | setPosts((prev) => prev.filter((post) => post.slug !== openPost.slug)); 138 | setOpenPost(null); 139 | } catch (error) { 140 | console.error('Failed to delete post:', error); 141 | throw error; 142 | } 143 | }; 144 | 145 | const selectPost = async (post: Post) => { 146 | if (!project.folder_name) return; 147 | 148 | try { 149 | const fullPost = await invoke ('read_post', { 150 | projectName: project.folder_name, 151 | slug: post.slug, 152 | }); 153 | setOpenPost(fullPost); 154 | } catch (error) { 155 | console.error('Failed to read post:', error); 156 | } 157 | }; 158 | 159 | const startEditingTitle = () => { 160 | setIsEditingTitle(true); 161 | setEditedTitle(project.label); 162 | }; 163 | 164 | const cancelEditingTitle = () => { 165 | setIsEditingTitle(false); 166 | setEditedTitle(project.label); 167 | }; 168 | 169 | const saveTitle = async () => { 170 | if (!currentProjectName || editedTitle.trim() === '') return; 171 | 172 | const newDisplayName = editedTitle.trim(); 173 | 174 | try { 175 | // Call backend to rename the project (passing folder_name and new display name) 176 | const updatedProjectFromBackend = await invoke<{ name: string; folder_name: string; path: string }>( 177 | 'rename_project', 178 | { 179 | oldFolderName: project.folder_name, 180 | newDisplayName: newDisplayName, 181 | } 182 | ); 183 | 184 | // Update the project locally with the complete information from backend 185 | const updatedProject = { 186 | ...project, 187 | label: updatedProjectFromBackend.name, 188 | name: updatedProjectFromBackend.name, 189 | folder_name: updatedProjectFromBackend.folder_name, 190 | path: updatedProjectFromBackend.path, 191 | }; 192 | 193 | // Update local state first 194 | setCurrentProjectName(updatedProjectFromBackend.name); 195 | 196 | // Notify parent component of the change 197 | if (onUpdateProject) { 198 | onUpdateProject(updatedProject); 199 | } 200 | 201 | // Reload posts with the updated project information 202 | await loadPosts(); 203 | 204 | setIsEditingTitle(false); 205 | console.log(`✅ Project renamed to: ${newDisplayName} (folder: ${updatedProjectFromBackend.folder_name})`); 206 | } catch (error) { 207 | console.error('Failed to rename project:', error); 208 | alert(`❌ Failed to rename project: ${error}`); 209 | // Revert on error 210 | setEditedTitle(project.label); 211 | } 212 | }; 213 | 214 | return ( 215 | 230 | {/* Left Column - Project Posts */} 231 | 334 | ); 335 | } 336 | -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::fs; 3 | use std::io::Write; 4 | use tauri::{AppHandle, Manager}; 5 | 6 | #[derive(Debug, Serialize, Deserialize)] 7 | struct Project { 8 | name: String, // Display name (what user sees) 9 | folder_name: String, // Actual folder name (sanitized) 10 | path: String, 11 | } 12 | 13 | #[derive(Debug, Serialize, Deserialize)] 14 | struct Post { 15 | filename: String, 16 | title: String, 17 | slug: String, 18 | content: String, 19 | } 20 | 21 | #[derive(Debug, Serialize, Deserialize)] 22 | struct StudioInitResult { 23 | success: bool, 24 | message: String, 25 | is_first_time: bool, 26 | } 27 | 28 | #[tauri::command] 29 | async fn initialize_studio(app: AppHandle) -> Result232 |313 | 314 | {/* Right Column - Post Editor */} 315 |233 | 254 | 255 |291 | 292 | {loading ? ( 293 |256 | {isEditingTitle ? ( 257 | setEditedTitle(e.target.value)} 261 | className="text-xl text-center font-semibold bg-neutral-800 text-white border border-neutral-600 rounded px-2 py-1 focus:border-blue-500 focus:outline-none" 262 | autoFocus 263 | onKeyDown={(e) => { 264 | if (e.key === 'Enter') saveTitle(); 265 | if (e.key === 'Escape') cancelEditingTitle(); 266 | }} 267 | onBlur={cancelEditingTitle} 268 | placeholder="Enter project name" 269 | /> 270 | ) : ( 271 |280 | 281 | 290 |276 | {project.label} 277 |
278 | )} 279 |Loading posts...294 | ) : ( 295 |296 | {posts.map((post) => ( 297 |311 | )} 312 |selectPost(post)} 306 | > 307 | {post.title} 308 | 309 | ))} 310 |316 | {openPost ? ( 317 |333 |setOpenPost(null)} onSave={savePost} onDelete={deletePost} /> 318 | ) : ( 319 | 320 |331 | )} 332 |321 |330 |📝322 |No posts yet
323 | 329 |{ 30 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 31 | let studio_path = documents_dir.join("studio"); 32 | 33 | // Check if studio directory already exists 34 | let is_first_time = !studio_path.exists(); 35 | 36 | if is_first_time { 37 | // Create the studio directory 38 | if let Err(e) = fs::create_dir_all(&studio_path) { 39 | return Err(format!("Failed to create studio directory: {}", e)); 40 | } 41 | 42 | // Create starter site 43 | create_starter_site_internal(&app, &studio_path)?; 44 | 45 | Ok(StudioInitResult { 46 | success: true, 47 | message: "🎉 Welcome to Studio Builder Desktop!\n\nYour new Astro workspace has been created at:\n~/Documents/studio/\n\nTo get started:\n1. Open a terminal in that folder\n2. Run: npm install && npm run dev\n3. Open http://localhost:4321 in your browser".to_string(), 48 | is_first_time: true, 49 | }) 50 | } else { 51 | Ok(StudioInitResult { 52 | success: true, 53 | message: "✅ Studio workspace found!\n\nLocation: ~/Documents/studio/\n\nYour existing setup has been preserved.".to_string(), 54 | is_first_time: false, 55 | }) 56 | } 57 | } 58 | 59 | fn create_starter_site_internal( 60 | app: &AppHandle, 61 | studio_path: &std::path::Path, 62 | ) -> Result<(), String> { 63 | // Get the starter site template from resources 64 | let template_path = app 65 | .path() 66 | .resolve( 67 | "resources/templates/starter-site", 68 | tauri::path::BaseDirectory::Resource, 69 | ) 70 | .map_err(|e| format!("Failed to resolve template path: {}", e))?; 71 | 72 | // Recursively copy all template files 73 | copy_dir_all(&template_path, studio_path)?; 74 | 75 | Ok(()) 76 | } 77 | 78 | fn copy_dir_all(source_dir: &std::path::Path, dest_path: &std::path::Path) -> Result<(), String> { 79 | // Create the destination directory if it doesn't exist 80 | if let Err(e) = fs::create_dir_all(dest_path) { 81 | return Err(format!( 82 | "Failed to create directory {}: {}", 83 | dest_path.display(), 84 | e 85 | )); 86 | } 87 | 88 | // Read the source directory 89 | let entries = fs::read_dir(source_dir) 90 | .map_err(|e| format!("Failed to read directory {}: {}", source_dir.display(), e))?; 91 | 92 | for entry in entries { 93 | let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?; 94 | let source_path = entry.path(); 95 | let dest_entry_path = dest_path.join(entry.file_name()); 96 | 97 | if source_path.is_dir() { 98 | // Recursively copy subdirectories 99 | copy_dir_all(&source_path, &dest_entry_path)?; 100 | } else { 101 | // Copy files 102 | if let Err(e) = fs::copy(&source_path, &dest_entry_path) { 103 | return Err(format!( 104 | "Failed to copy file {} to {}: {}", 105 | source_path.display(), 106 | dest_entry_path.display(), 107 | e 108 | )); 109 | } 110 | } 111 | } 112 | 113 | Ok(()) 114 | } 115 | 116 | #[tauri::command] 117 | async fn create_starter_site(app: AppHandle) -> Result<(), String> { 118 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 119 | let studio_path = documents_dir.join("studio"); 120 | 121 | create_starter_site_internal(&app, &studio_path) 122 | } 123 | 124 | #[tauri::command] 125 | async fn list_projects() -> Result , String> { 126 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 127 | 128 | let pages_path = documents_dir.join("studio").join("src").join("pages"); 129 | 130 | if !pages_path.exists() { 131 | return Ok(vec![]); 132 | } 133 | 134 | let mut projects = Vec::new(); 135 | 136 | match fs::read_dir(&pages_path) { 137 | Ok(entries) => { 138 | for entry in entries { 139 | if let Ok(entry) = entry { 140 | let path = entry.path(); 141 | if path.is_dir() { 142 | if let Some(folder_name) = path.file_name().and_then(|n| n.to_str()) { 143 | // Try to read display name from index.astro frontmatter 144 | let display_name = read_project_display_name(&path) 145 | .unwrap_or_else(|| folder_name.to_string()); 146 | 147 | projects.push(Project { 148 | name: display_name, 149 | folder_name: folder_name.to_string(), 150 | path: path.to_string_lossy().to_string(), 151 | }); 152 | } 153 | } 154 | } 155 | } 156 | } 157 | Err(e) => return Err(format!("Failed to read pages directory: {}", e)), 158 | } 159 | 160 | projects.sort_by(|a, b| a.name.cmp(&b.name)); 161 | Ok(projects) 162 | } 163 | 164 | #[tauri::command] 165 | async fn create_project(name: String) -> Result { 166 | // Sanitize the project name 167 | let sanitized_name = sanitize_project_name(&name); 168 | 169 | if sanitized_name.is_empty() { 170 | return Err("Project name cannot be empty after sanitization".to_string()); 171 | } 172 | 173 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 174 | 175 | let pages_path = documents_dir.join("studio").join("src").join("pages"); 176 | let project_path = pages_path.join(&sanitized_name); 177 | 178 | // Check if project already exists 179 | if project_path.exists() { 180 | return Err(format!("Project '{}' already exists", sanitized_name)); 181 | } 182 | 183 | // Create the project directory 184 | if let Err(e) = fs::create_dir_all(&project_path) { 185 | return Err(format!("Failed to create project directory: {}", e)); 186 | } 187 | 188 | // Create a project-specific layout that uses the main PostLayout 189 | let layout_content = format!( 190 | r#"--- 191 | import PostLayout from '../../layouts/PostLayout.astro'; 192 | 193 | export interface Props {{ 194 | title: string; 195 | }} 196 | 197 | const {{ title }} = Astro.props; 198 | const projectName = '{}'; 199 | --- 200 | 201 | 202 | 204 | "#, 205 | sanitized_name 206 | ); 207 | 208 | let layout_path = project_path.join("_layout.astro"); 209 | if let Err(e) = fs::write(&layout_path, layout_content) { 210 | return Err(format!("Failed to create layout file: {}", e)); 211 | } 212 | 213 | // Create an index.astro file for the project listing 214 | let index_content = format!( 215 | r#"--- 216 | title: "{}" 217 | displayName: "{}" 218 | import ProjectLayout from '../../layouts/ProjectLayout.astro'; 219 | import {{ readdir }} from 'node:fs/promises'; 220 | import path from 'node:path'; 221 | 222 | const projectName = '{}'; 223 | const displayName = '{}'; 224 | const folderName = '{}'; 225 | const projectDir = path.join(process.cwd(), 'src/pages', folderName); 226 | 227 | // Get all markdown posts 228 | let posts = []; 229 | try {{ 230 | const entries = await readdir(projectDir); 231 | posts = entries 232 | .filter(file => file.endsWith('.md')) 233 | .map(file => {{ 234 | const slug = file.replace('.md', ''); 235 | return {{ 236 | slug, 237 | title: slug.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()), 238 | href: `/${{folderName}}/${{slug}}` 239 | }}; 240 | }}); 241 | }} catch (error) {{ 242 | console.log('No posts found yet'); 243 | }} 244 | --- 245 | 246 |203 | 247 | 264 | "#, 265 | name, // original display name for title 266 | name, // original display name for displayName 267 | name, // original display name for projectName (what user sees) 268 | name, // original display name for displayName variable 269 | sanitized_name // sanitized folder name for file system operations 270 | ); 271 | 272 | let index_path = project_path.join("index.astro"); 273 | if let Err(e) = fs::write(&index_path, index_content) { 274 | return Err(format!("Failed to create index file: {}", e)); 275 | } 276 | 277 | Ok(Project { 278 | name: name.clone(), // Original display name 279 | folder_name: sanitized_name.clone(), 280 | path: project_path.to_string_lossy().to_string(), 281 | }) 282 | } 283 | 284 | // Post CRUD operations 285 | #[tauri::command] 286 | async fn list_posts(project_name: String) -> ResultPosts
248 | 249 | {{posts.length > 0 ? ( 250 |251 | {{posts.map((post) => ( 252 | 253 | {{post.title}} 254 | 255 | ))}} 256 |257 | ) : ( 258 |259 |262 | )}} 263 |No posts yet
260 |Create your first post using Studio Builder Desktop!
261 |, String> { 287 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 288 | 289 | let project_path = documents_dir 290 | .join("studio") 291 | .join("src") 292 | .join("pages") 293 | .join(&project_name); 294 | 295 | if !project_path.exists() { 296 | return Err(format!("Project '{}' does not exist", project_name)); 297 | } 298 | 299 | let mut posts = Vec::new(); 300 | 301 | match fs::read_dir(&project_path) { 302 | Ok(entries) => { 303 | for entry in entries { 304 | if let Ok(entry) = entry { 305 | let path = entry.path(); 306 | if path.is_file() { 307 | if let Some(extension) = path.extension() { 308 | if extension == "md" { 309 | if let Some(filename) = path.file_name().and_then(|n| n.to_str()) { 310 | let slug = filename 311 | .strip_suffix(".md") 312 | .unwrap_or(filename) 313 | .to_string(); 314 | 315 | // Read the file to extract the title 316 | let content = fs::read_to_string(&path).unwrap_or_default(); 317 | let title = extract_title_from_markdown(&content, &slug); 318 | 319 | posts.push(Post { 320 | filename: filename.to_string(), 321 | title, 322 | slug, 323 | content: String::new(), // Don't load full content for listing 324 | }); 325 | } 326 | } 327 | } 328 | } 329 | } 330 | } 331 | } 332 | Err(e) => return Err(format!("Failed to read project directory: {}", e)), 333 | } 334 | 335 | posts.sort_by(|a, b| a.filename.cmp(&b.filename)); 336 | Ok(posts) 337 | } 338 | 339 | #[tauri::command] 340 | async fn create_post(project_name: String, title: String) -> Result { 341 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 342 | 343 | let project_path = documents_dir 344 | .join("studio") 345 | .join("src") 346 | .join("pages") 347 | .join(&project_name); 348 | 349 | if !project_path.exists() { 350 | return Err(format!("Project '{}' does not exist", project_name)); 351 | } 352 | 353 | // Generate a unique slug 354 | let base_slug = sanitize_slug(&title); 355 | let mut slug = base_slug.clone(); 356 | let mut counter = 1; 357 | 358 | // Ensure unique filename 359 | while project_path.join(format!("{}.md", slug)).exists() { 360 | slug = format!("{}-{}", base_slug, counter); 361 | counter += 1; 362 | } 363 | 364 | let filename = format!("{}.md", slug); 365 | let file_path = project_path.join(&filename); 366 | 367 | // Create markdown content with frontmatter 368 | let content = format!( 369 | r#"--- 370 | title: "{}" 371 | date: {} 372 | --- 373 | 374 | # {} 375 | 376 | Write your content here... 377 | "#, 378 | title, 379 | chrono::Utc::now().format("%Y-%m-%d"), 380 | title 381 | ); 382 | 383 | // Write the file 384 | if let Err(e) = fs::write(&file_path, &content) { 385 | return Err(format!("Failed to create post file: {}", e)); 386 | } 387 | 388 | Ok(Post { 389 | filename, 390 | title, 391 | slug, 392 | content, 393 | }) 394 | } 395 | 396 | #[tauri::command] 397 | async fn read_post(project_name: String, slug: String) -> Result { 398 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 399 | 400 | let project_path = documents_dir 401 | .join("studio") 402 | .join("src") 403 | .join("pages") 404 | .join(&project_name); 405 | let filename = format!("{}.md", slug); 406 | let file_path = project_path.join(&filename); 407 | 408 | if !file_path.exists() { 409 | return Err(format!("Post '{}' does not exist", slug)); 410 | } 411 | 412 | let content = 413 | fs::read_to_string(&file_path).map_err(|e| format!("Failed to read post file: {}", e))?; 414 | 415 | let title = extract_title_from_markdown(&content, &slug); 416 | 417 | Ok(Post { 418 | filename, 419 | title, 420 | slug, 421 | content, 422 | }) 423 | } 424 | 425 | #[tauri::command] 426 | async fn update_post(project_name: String, slug: String, content: String) -> Result<(), String> { 427 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 428 | 429 | let project_path = documents_dir 430 | .join("studio") 431 | .join("src") 432 | .join("pages") 433 | .join(&project_name); 434 | let filename = format!("{}.md", slug); 435 | let file_path = project_path.join(&filename); 436 | 437 | if !file_path.exists() { 438 | return Err(format!("Post '{}' does not exist", slug)); 439 | } 440 | 441 | // Atomic write: write to temp file first, then rename 442 | let temp_path = project_path.join(format!(".{}.tmp", filename)); 443 | 444 | // Write to temp file 445 | { 446 | let mut temp_file = fs::File::create(&temp_path) 447 | .map_err(|e| format!("Failed to create temp file: {}", e))?; 448 | temp_file 449 | .write_all(content.as_bytes()) 450 | .map_err(|e| format!("Failed to write to temp file: {}", e))?; 451 | temp_file 452 | .sync_all() 453 | .map_err(|e| format!("Failed to sync temp file: {}", e))?; 454 | } 455 | 456 | // Atomically replace the original file 457 | fs::rename(&temp_path, &file_path) 458 | .map_err(|e| format!("Failed to replace original file: {}", e))?; 459 | 460 | Ok(()) 461 | } 462 | 463 | #[tauri::command] 464 | async fn delete_post(project_name: String, slug: String) -> Result<(), String> { 465 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 466 | 467 | let project_path = documents_dir 468 | .join("studio") 469 | .join("src") 470 | .join("pages") 471 | .join(&project_name); 472 | let filename = format!("{}.md", slug); 473 | let file_path = project_path.join(&filename); 474 | 475 | if !file_path.exists() { 476 | return Err(format!("Post '{}' does not exist", slug)); 477 | } 478 | 479 | fs::remove_file(&file_path).map_err(|e| format!("Failed to delete post file: {}", e))?; 480 | 481 | Ok(()) 482 | } 483 | 484 | #[tauri::command] 485 | async fn rename_project(old_folder_name: String, new_display_name: String) -> Result { 486 | // Sanitize the new project name for folder 487 | let sanitized_new_name = sanitize_project_name(&new_display_name); 488 | 489 | if sanitized_new_name.is_empty() { 490 | return Err("Project name cannot be empty after sanitization".to_string()); 491 | } 492 | 493 | let documents_dir = dirs::document_dir().ok_or("Could not find documents directory")?; 494 | let pages_path = documents_dir.join("studio").join("src").join("pages"); 495 | 496 | let old_project_path = pages_path.join(&old_folder_name); 497 | let new_project_path = pages_path.join(&sanitized_new_name); 498 | 499 | // Check if old project exists 500 | if !old_project_path.exists() { 501 | return Err(format!("Project folder '{}' does not exist", old_folder_name)); 502 | } 503 | 504 | // Check if new project name already exists (only if folder name is changing) 505 | if old_folder_name != sanitized_new_name && new_project_path.exists() { 506 | return Err(format!("Project folder '{}' already exists", sanitized_new_name)); 507 | } 508 | 509 | // Update the index.astro file with new display name 510 | let index_file_path = old_project_path.join("index.astro"); 511 | if index_file_path.exists() { 512 | update_project_display_name(&index_file_path, &new_display_name, &sanitized_new_name)?; 513 | } 514 | 515 | // Rename the directory if the folder name changed 516 | if old_folder_name != sanitized_new_name { 517 | fs::rename(&old_project_path, &new_project_path) 518 | .map_err(|e| format!("Failed to rename project directory: {}", e))?; 519 | 520 | // Update the layout file to reference the new folder name 521 | let layout_file_path = new_project_path.join("_layout.astro"); 522 | if layout_file_path.exists() { 523 | let layout_content = fs::read_to_string(&layout_file_path) 524 | .map_err(|e| format!("Failed to read layout file: {}", e))?; 525 | 526 | let updated_content = layout_content.replace( 527 | &format!("const projectName = '{}';", old_folder_name), 528 | &format!("const projectName = '{}';", sanitized_new_name), 529 | ); 530 | 531 | fs::write(&layout_file_path, updated_content) 532 | .map_err(|e| format!("Failed to update layout file: {}", e))?; 533 | } 534 | } 535 | 536 | // Return the updated project information 537 | let final_folder_name = sanitized_new_name; 538 | let final_project_path = pages_path.join(&final_folder_name); 539 | 540 | Ok(Project { 541 | name: new_display_name, 542 | folder_name: final_folder_name, 543 | path: final_project_path.to_string_lossy().to_string(), 544 | }) 545 | } 546 | 547 | // Helper functions 548 | fn update_project_display_name( 549 | index_path: &std::path::Path, 550 | new_display_name: &str, 551 | new_folder_name: &str, 552 | ) -> Result<(), String> { 553 | let content = fs::read_to_string(index_path) 554 | .map_err(|e| format!("Failed to read index file: {}", e))?; 555 | 556 | let mut lines: Vec = content.lines().map(|s| s.to_string()).collect(); 557 | let mut in_frontmatter = false; 558 | let mut frontmatter_end = 0; 559 | 560 | // Find frontmatter boundaries 561 | for (i, line) in lines.iter().enumerate() { 562 | if i == 0 && line.trim() == "---" { 563 | in_frontmatter = true; 564 | continue; 565 | } 566 | if in_frontmatter && line.trim() == "---" { 567 | frontmatter_end = i; 568 | break; 569 | } 570 | } 571 | 572 | if frontmatter_end == 0 { 573 | return Err("No frontmatter found in index.astro".to_string()); 574 | } 575 | 576 | // Update frontmatter fields 577 | let mut found_title = false; 578 | let mut found_display_name = false; 579 | 580 | for i in 1..frontmatter_end { 581 | let line = &lines[i]; 582 | if line.trim().starts_with("title:") { 583 | lines[i] = format!("title: \"{}\"", new_display_name); 584 | found_title = true; 585 | } else if line.trim().starts_with("displayName:") { 586 | lines[i] = format!("displayName: \"{}\"", new_display_name); 587 | found_display_name = true; 588 | } else if line.trim().starts_with("const projectName =") { 589 | lines[i] = format!("const projectName = '{}';", new_display_name); 590 | } else if line.trim().starts_with("const displayName =") { 591 | lines[i] = format!("const displayName = '{}';", new_display_name); 592 | } else if line.trim().starts_with("const folderName =") { 593 | lines[i] = format!("const folderName = '{}';", new_folder_name); 594 | } 595 | } 596 | 597 | // Add missing frontmatter fields if needed 598 | if !found_title { 599 | lines.insert(1, format!("title: \"{}\"", new_display_name)); 600 | frontmatter_end += 1; 601 | } 602 | if !found_display_name { 603 | let insert_pos = if found_title { 2 } else { 1 }; 604 | lines.insert(insert_pos, format!("displayName: \"{}\"", new_display_name)); 605 | } 606 | 607 | let updated_content = lines.join("\n"); 608 | fs::write(index_path, updated_content) 609 | .map_err(|e| format!("Failed to update index file: {}", e))?; 610 | 611 | Ok(()) 612 | } 613 | 614 | fn read_project_display_name(project_path: &std::path::Path) -> Option { 615 | let index_path = project_path.join("index.astro"); 616 | if !index_path.exists() { 617 | return None; 618 | } 619 | 620 | if let Ok(content) = fs::read_to_string(&index_path) { 621 | // Extract displayName from frontmatter 622 | if content.starts_with("---") { 623 | let lines: Vec<&str> = content.lines().collect(); 624 | let mut in_frontmatter = false; 625 | let mut frontmatter_end = 0; 626 | 627 | for (i, line) in lines.iter().enumerate() { 628 | if i == 0 && line.trim() == "---" { 629 | in_frontmatter = true; 630 | continue; 631 | } 632 | if in_frontmatter && line.trim() == "---" { 633 | frontmatter_end = i; 634 | break; 635 | } 636 | } 637 | 638 | if frontmatter_end > 0 { 639 | for i in 1..frontmatter_end { 640 | let line = lines[i].trim(); 641 | if line.starts_with("displayName:") { 642 | let display_name_part = &line[12..].trim(); 643 | // Remove quotes if present 644 | let display_name = if (display_name_part.starts_with('"') && display_name_part.ends_with('"')) 645 | || (display_name_part.starts_with('\'') && display_name_part.ends_with('\'')) 646 | { 647 | &display_name_part[1..display_name_part.len() - 1] 648 | } else { 649 | display_name_part 650 | }; 651 | return Some(display_name.to_string()); 652 | } 653 | } 654 | } 655 | } 656 | } 657 | 658 | None 659 | } 660 | 661 | fn extract_title_from_markdown(content: &str, fallback_slug: &str) -> String { 662 | // First try to extract title from frontmatter 663 | if let Some(frontmatter_title) = extract_frontmatter_title(content) { 664 | return frontmatter_title; 665 | } 666 | 667 | // Then try to find the first heading 668 | let lines = content.lines(); 669 | for line in lines { 670 | let trimmed = line.trim(); 671 | if trimmed.starts_with("# ") { 672 | return trimmed[2..].trim().to_string(); 673 | } 674 | } 675 | 676 | // Fallback to slug formatted as title 677 | fallback_slug 678 | .replace('-', " ") 679 | .replace('_', " ") 680 | .split_whitespace() 681 | .map(|word| { 682 | let mut chars = word.chars(); 683 | match chars.next() { 684 | None => String::new(), 685 | Some(first) => first.to_uppercase().collect:: () + chars.as_str(), 686 | } 687 | }) 688 | .collect:: >() 689 | .join(" ") 690 | } 691 | 692 | fn extract_frontmatter_title(content: &str) -> Option { 693 | if !content.starts_with("---") { 694 | return None; 695 | } 696 | 697 | let lines: Vec<&str> = content.lines().collect(); 698 | let mut in_frontmatter = false; 699 | let mut frontmatter_end = 0; 700 | 701 | for (i, line) in lines.iter().enumerate() { 702 | if i == 0 && line.trim() == "---" { 703 | in_frontmatter = true; 704 | continue; 705 | } 706 | if in_frontmatter && line.trim() == "---" { 707 | frontmatter_end = i; 708 | break; 709 | } 710 | } 711 | 712 | if frontmatter_end > 0 { 713 | for i in 1..frontmatter_end { 714 | let line = lines[i].trim(); 715 | if line.starts_with("title:") { 716 | let title_part = &line[6..].trim(); 717 | // Remove quotes if present 718 | let title = if (title_part.starts_with('"') && title_part.ends_with('"')) 719 | || (title_part.starts_with('\'') && title_part.ends_with('\'')) 720 | { 721 | &title_part[1..title_part.len() - 1] 722 | } else { 723 | title_part 724 | }; 725 | return Some(title.to_string()); 726 | } 727 | } 728 | } 729 | 730 | None 731 | } 732 | 733 | fn sanitize_slug(title: &str) -> String { 734 | title 735 | .to_lowercase() 736 | .chars() 737 | .map(|c| match c { 738 | ' ' | '_' => '-', 739 | c if c.is_alphanumeric() || c == '-' => c, 740 | _ => '-', 741 | }) 742 | .collect:: () 743 | .split('-') 744 | .filter(|s| !s.is_empty()) 745 | .collect:: >() 746 | .join("-") 747 | } 748 | 749 | fn sanitize_project_name(name: &str) -> String { 750 | name.chars() 751 | .map(|c| match c { 752 | ' ' => '-', 753 | c if c.is_alphanumeric() || c == '-' || c == '_' => c, 754 | _ => '-', 755 | }) 756 | .collect:: () 757 | .split('-') 758 | .filter(|s| !s.is_empty()) 759 | .collect:: >() 760 | .join("-") 761 | .to_lowercase() 762 | } 763 | 764 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 765 | pub fn run() { 766 | tauri::Builder::default() 767 | .plugin(tauri_plugin_opener::init()) 768 | .plugin(tauri_plugin_fs::init()) 769 | .invoke_handler(tauri::generate_handler![ 770 | initialize_studio, 771 | create_starter_site, 772 | list_projects, 773 | create_project, 774 | list_posts, 775 | create_post, 776 | read_post, 777 | update_post, 778 | delete_post, 779 | rename_project 780 | ]) 781 | .run(tauri::generate_context!()) 782 | .expect("error while running tauri application"); 783 | } 784 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "studio-builder-desktop", 6 | "dependencies": { 7 | "@tauri-apps/api": "^2", 8 | "@tauri-apps/plugin-fs": "~2", 9 | "@tauri-apps/plugin-opener": "^2", 10 | "framer-motion": "^12.16.0", 11 | "lucide-react": "^0.513.0", 12 | "react": "^18.3.1", 13 | "react-dom": "^18.3.1", 14 | }, 15 | "devDependencies": { 16 | "@tailwindcss/postcss": "^4.1.8", 17 | "@tauri-apps/cli": "^2", 18 | "@types/react": "^18.3.1", 19 | "@types/react-dom": "^18.3.1", 20 | "@vitejs/plugin-react": "^4.3.4", 21 | "autoprefixer": "^10.4.21", 22 | "postcss": "^8.5.4", 23 | "tailwindcss": "^3.4.4", 24 | "typescript": "~5.6.2", 25 | "vite": "^6.0.3", 26 | }, 27 | }, 28 | }, 29 | "packages": { 30 | "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], 31 | 32 | "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], 33 | 34 | "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], 35 | 36 | "@babel/compat-data": ["@babel/compat-data@7.27.5", "", {}, "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg=="], 37 | 38 | "@babel/core": ["@babel/core@7.27.4", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.4", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.4", "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g=="], 39 | 40 | "@babel/generator": ["@babel/generator@7.27.5", "", { "dependencies": { "@babel/parser": "^7.27.5", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw=="], 41 | 42 | "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], 43 | 44 | "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], 45 | 46 | "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.27.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.27.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg=="], 47 | 48 | "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], 49 | 50 | "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], 51 | 52 | "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], 53 | 54 | "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], 55 | 56 | "@babel/helpers": ["@babel/helpers@7.27.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.3" } }, "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ=="], 57 | 58 | "@babel/parser": ["@babel/parser@7.27.5", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg=="], 59 | 60 | "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], 61 | 62 | "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], 63 | 64 | "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], 65 | 66 | "@babel/traverse": ["@babel/traverse@7.27.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA=="], 67 | 68 | "@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="], 69 | 70 | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], 71 | 72 | "@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], 73 | 74 | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], 75 | 76 | "@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], 77 | 78 | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], 79 | 80 | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], 81 | 82 | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], 83 | 84 | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], 85 | 86 | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], 87 | 88 | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], 89 | 90 | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], 91 | 92 | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], 93 | 94 | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], 95 | 96 | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], 97 | 98 | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], 99 | 100 | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], 101 | 102 | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], 103 | 104 | "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], 105 | 106 | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], 107 | 108 | "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], 109 | 110 | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], 111 | 112 | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], 113 | 114 | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], 115 | 116 | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], 117 | 118 | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], 119 | 120 | "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], 121 | 122 | "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], 123 | 124 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], 125 | 126 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 127 | 128 | "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 129 | 130 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 131 | 132 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 133 | 134 | "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 135 | 136 | "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 137 | 138 | "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 139 | 140 | "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], 141 | 142 | "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9", "", {}, "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w=="], 143 | 144 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="], 145 | 146 | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.1", "", { "os": "android", "cpu": "arm64" }, "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA=="], 147 | 148 | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.41.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w=="], 149 | 150 | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.41.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg=="], 151 | 152 | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.41.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg=="], 153 | 154 | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.41.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA=="], 155 | 156 | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg=="], 157 | 158 | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA=="], 159 | 160 | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA=="], 161 | 162 | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="], 163 | 164 | "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="], 165 | 166 | "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="], 167 | 168 | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="], 169 | 170 | "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="], 171 | 172 | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.41.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g=="], 173 | 174 | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="], 175 | 176 | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="], 177 | 178 | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="], 179 | 180 | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="], 181 | 182 | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="], 183 | 184 | "@tailwindcss/node": ["@tailwindcss/node@4.1.8", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.8" } }, "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q=="], 185 | 186 | "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.8", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.8", "@tailwindcss/oxide-darwin-arm64": "4.1.8", "@tailwindcss/oxide-darwin-x64": "4.1.8", "@tailwindcss/oxide-freebsd-x64": "4.1.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", "@tailwindcss/oxide-linux-x64-musl": "4.1.8", "@tailwindcss/oxide-wasm32-wasi": "4.1.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A=="], 187 | 188 | "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg=="], 189 | 190 | "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A=="], 191 | 192 | "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw=="], 193 | 194 | "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg=="], 195 | 196 | "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8", "", { "os": "linux", "cpu": "arm" }, "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ=="], 197 | 198 | "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q=="], 199 | 200 | "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ=="], 201 | 202 | "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g=="], 203 | 204 | "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.8", "", { "os": "linux", "cpu": "x64" }, "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg=="], 205 | 206 | "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.8", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg=="], 207 | 208 | "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA=="], 209 | 210 | "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.8", "", { "os": "win32", "cpu": "x64" }, "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ=="], 211 | 212 | "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.8", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.8", "@tailwindcss/oxide": "4.1.8", "postcss": "^8.4.41", "tailwindcss": "4.1.8" } }, "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw=="], 213 | 214 | "@tauri-apps/api": ["@tauri-apps/api@2.5.0", "", {}, "sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA=="], 215 | 216 | "@tauri-apps/cli": ["@tauri-apps/cli@2.5.0", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.5.0", "@tauri-apps/cli-darwin-x64": "2.5.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", "@tauri-apps/cli-linux-arm64-musl": "2.5.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", "@tauri-apps/cli-linux-x64-gnu": "2.5.0", "@tauri-apps/cli-linux-x64-musl": "2.5.0", "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", "@tauri-apps/cli-win32-x64-msvc": "2.5.0" }, "bin": { "tauri": "tauri.js" } }, "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg=="], 217 | 218 | "@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ=="], 219 | 220 | "@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw=="], 221 | 222 | "@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.5.0", "", { "os": "linux", "cpu": "arm" }, "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg=="], 223 | 224 | "@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ=="], 225 | 226 | "@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q=="], 227 | 228 | "@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.5.0", "", { "os": "linux", "cpu": "none" }, "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA=="], 229 | 230 | "@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w=="], 231 | 232 | "@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ=="], 233 | 234 | "@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.5.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q=="], 235 | 236 | "@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.5.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg=="], 237 | 238 | "@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.5.0", "", { "os": "win32", "cpu": "x64" }, "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ=="], 239 | 240 | "@tauri-apps/plugin-fs": ["@tauri-apps/plugin-fs@2.3.0", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-G9gEyYVUaaxhdRJBgQTTLmzAe0vtHYxYyN1oTQzU3zwvb8T+tVLcAqCdFMWHq0qGeGbmynI5whvYpcXo5LvZ1w=="], 241 | 242 | "@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.2.7", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-uduEyvOdjpPOEeDRrhwlCspG/f9EQalHumWBtLBnp3fRp++fKGLqDOyUhSIn7PzX45b/rKep//ZQSAQoIxobLA=="], 243 | 244 | "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], 245 | 246 | "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], 247 | 248 | "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], 249 | 250 | "@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="], 251 | 252 | "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], 253 | 254 | "@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="], 255 | 256 | "@types/react": ["@types/react@18.3.23", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w=="], 257 | 258 | "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], 259 | 260 | "@vitejs/plugin-react": ["@vitejs/plugin-react@4.5.1", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@rolldown/pluginutils": "1.0.0-beta.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A=="], 261 | 262 | "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], 263 | 264 | "ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], 265 | 266 | "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], 267 | 268 | "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], 269 | 270 | "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], 271 | 272 | "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], 273 | 274 | "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 275 | 276 | "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], 277 | 278 | "brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], 279 | 280 | "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 281 | 282 | "browserslist": ["browserslist@4.25.0", "", { "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA=="], 283 | 284 | "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], 285 | 286 | "caniuse-lite": ["caniuse-lite@1.0.30001721", "", {}, "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ=="], 287 | 288 | "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], 289 | 290 | "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], 291 | 292 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 293 | 294 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 295 | 296 | "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], 297 | 298 | "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], 299 | 300 | "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 301 | 302 | "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], 303 | 304 | "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], 305 | 306 | "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], 307 | 308 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], 309 | 310 | "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], 311 | 312 | "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], 313 | 314 | "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], 315 | 316 | "electron-to-chromium": ["electron-to-chromium@1.5.165", "", {}, "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw=="], 317 | 318 | "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], 319 | 320 | "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], 321 | 322 | "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], 323 | 324 | "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 325 | 326 | "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 327 | 328 | "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], 329 | 330 | "fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="], 331 | 332 | "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 333 | 334 | "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], 335 | 336 | "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], 337 | 338 | "framer-motion": ["framer-motion@12.16.0", "", { "dependencies": { "motion-dom": "^12.16.0", "motion-utils": "^12.12.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-xryrmD4jSBQrS2IkMdcTmiS4aSKckbS7kLDCuhUn9110SQKG1w3zlq1RTqCblewg+ZYe+m3sdtzQA6cRwo5g8Q=="], 339 | 340 | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 341 | 342 | "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 343 | 344 | "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], 345 | 346 | "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], 347 | 348 | "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 349 | 350 | "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], 351 | 352 | "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 353 | 354 | "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 355 | 356 | "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], 357 | 358 | "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 359 | 360 | "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 361 | 362 | "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], 363 | 364 | "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 365 | 366 | "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 367 | 368 | "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], 369 | 370 | "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], 371 | 372 | "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], 373 | 374 | "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 375 | 376 | "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], 377 | 378 | "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], 379 | 380 | "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], 381 | 382 | "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], 383 | 384 | "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], 385 | 386 | "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], 387 | 388 | "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], 389 | 390 | "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], 391 | 392 | "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], 393 | 394 | "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], 395 | 396 | "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], 397 | 398 | "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], 399 | 400 | "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], 401 | 402 | "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], 403 | 404 | "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], 405 | 406 | "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], 407 | 408 | "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 409 | 410 | "lucide-react": ["lucide-react@0.513.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-CJZKq2g8Y8yN4Aq002GahSXbG2JpFv9kXwyiOAMvUBv7pxeOFHUWKB0mO7MiY4ZVFCV4aNjv2BJFq/z3DgKPQg=="], 411 | 412 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 413 | 414 | "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], 415 | 416 | "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 417 | 418 | "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 419 | 420 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 421 | 422 | "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], 423 | 424 | "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], 425 | 426 | "motion-dom": ["motion-dom@12.16.0", "", { "dependencies": { "motion-utils": "^12.12.1" } }, "sha512-Z2nGwWrrdH4egLEtgYMCEN4V2qQt1qxlKy/uV7w691ztyA41Q5Rbn0KNGbsNVDZr9E8PD2IOQ3hSccRnB6xWzw=="], 427 | 428 | "motion-utils": ["motion-utils@12.12.1", "", {}, "sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w=="], 429 | 430 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 431 | 432 | "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], 433 | 434 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 435 | 436 | "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], 437 | 438 | "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], 439 | 440 | "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], 441 | 442 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 443 | 444 | "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], 445 | 446 | "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], 447 | 448 | "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 449 | 450 | "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 451 | 452 | "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], 453 | 454 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 455 | 456 | "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 457 | 458 | "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], 459 | 460 | "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], 461 | 462 | "postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="], 463 | 464 | "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], 465 | 466 | "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="], 467 | 468 | "postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], 469 | 470 | "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], 471 | 472 | "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], 473 | 474 | "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], 475 | 476 | "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 477 | 478 | "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], 479 | 480 | "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], 481 | 482 | "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], 483 | 484 | "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], 485 | 486 | "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], 487 | 488 | "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], 489 | 490 | "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], 491 | 492 | "rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="], 493 | 494 | "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 495 | 496 | "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], 497 | 498 | "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 499 | 500 | "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], 501 | 502 | "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 503 | 504 | "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], 505 | 506 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 507 | 508 | "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], 509 | 510 | "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 511 | 512 | "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], 513 | 514 | "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 515 | 516 | "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], 517 | 518 | "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 519 | 520 | "tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], 521 | 522 | "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], 523 | 524 | "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], 525 | 526 | "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], 527 | 528 | "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], 529 | 530 | "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], 531 | 532 | "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 533 | 534 | "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], 535 | 536 | "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 537 | 538 | "typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], 539 | 540 | "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], 541 | 542 | "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 543 | 544 | "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], 545 | 546 | "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], 547 | 548 | "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], 549 | 550 | "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], 551 | 552 | "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], 553 | 554 | "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], 555 | 556 | "@tailwindcss/node/jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], 557 | 558 | "@tailwindcss/node/tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="], 559 | 560 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], 561 | 562 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], 563 | 564 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], 565 | 566 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ=="], 567 | 568 | "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], 569 | 570 | "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 571 | 572 | "@tailwindcss/postcss/tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="], 573 | 574 | "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 575 | 576 | "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 577 | 578 | "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 579 | 580 | "lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 581 | 582 | "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 583 | 584 | "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 585 | 586 | "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 587 | 588 | "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 589 | 590 | "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 591 | 592 | "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 593 | 594 | "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 595 | 596 | "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 597 | 598 | "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 599 | 600 | "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 601 | 602 | "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 603 | 604 | "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 605 | } 606 | } 607 | --------------------------------------------------------------------------------