modified: src/components/NewProjectModal.tsx
This commit is contained in:
parent
6ee00e0299
commit
751f6ae7cd
20 changed files with 1069 additions and 712 deletions
|
|
@ -4,11 +4,10 @@ import { CodeEditor } from '@/components/CodeEditor';
|
|||
import { AIChat } from '@/components/AIChat';
|
||||
import { Toolbar } from '@/components/Toolbar';
|
||||
import { TemplatesDrawer } from '@/components/TemplatesDrawer';
|
||||
import { WelcomeDialog } from '@/components/WelcomeDialog';
|
||||
import { FileTree, FileNode } from '@/components/FileTree';
|
||||
import { FileTabs } from '@/components/FileTabs';
|
||||
import { PreviewModal } from '@/components/PreviewModal';
|
||||
import { NewProjectModal, ProjectConfig } from '@/components/NewProjectModal';
|
||||
// Removed named imports for WelcomeDialog and NewProjectModal. Use lazy-loaded versions from src/App.tsx.
|
||||
import { ConsolePanel } from '@/components/ConsolePanel';
|
||||
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable';
|
||||
import { useKV } from '@github/spark/hooks';
|
||||
|
|
|
|||
BIN
github-spark-0.44.15.tgz
Normal file
BIN
github-spark-0.44.15.tgz
Normal file
Binary file not shown.
|
|
@ -1,7 +1,13 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
|
||||
const path = require('path');
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
webpack: (config) => {
|
||||
config.resolve.alias['@'] = path.resolve(__dirname, 'src');
|
||||
return config;
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
|
|
|||
1502
package-lock.json
generated
1502
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@radix-ui/react-accordion": "^1.2.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||
"@radix-ui/react-avatar": "^1.1.2",
|
||||
|
|
@ -32,15 +33,19 @@
|
|||
"@radix-ui/react-tabs": "^1.1.2",
|
||||
"@radix-ui/react-toast": "^1.2.4",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"@sentry/browser": "^10.34.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.15.0",
|
||||
"lucide-react": "^0.462.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"next": "14.2.15",
|
||||
"next-themes": "^0.4.6",
|
||||
"posthog-js": "^1.328.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-error-boundary": "^6.1.0",
|
||||
"react-resizable-panels": "^4.4.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
|
|
@ -48,8 +53,8 @@
|
|||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^18",
|
||||
"@types/node": "22.19.7",
|
||||
"@types/react": "18.3.27",
|
||||
"@types/react-dom": "^18",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { initSentry, captureError } from './lib/sentry';
|
|||
import { LoadingSpinner } from './components/ui/loading-spinner';
|
||||
|
||||
import { PlatformId } from './lib/platforms';
|
||||
import { ProjectConfig } from './components/NewProjectModal';
|
||||
|
||||
// Lazy load heavy/modal components for code splitting and better initial load
|
||||
const TemplatesDrawer = lazy(() => import('./components/TemplatesDrawer').then(m => ({ default: m.TemplatesDrawer })));
|
||||
|
|
@ -513,7 +514,7 @@ end)`,
|
|||
) : (
|
||||
<>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanelGroup orientation="horizontal">
|
||||
<ResizablePanel defaultSize={15} minSize={10} maxSize={25}>
|
||||
<FileTree
|
||||
files={files || []}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Editor from '@monaco-editor/react';
|
||||
import { useKV } from '@github/spark/hooks';
|
||||
import { usePersistentState } from '@/lib/usePersistentState';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { LoadingSpinner } from './ui/loading-spinner';
|
||||
import { toast } from 'sonner';
|
||||
|
|
@ -19,7 +19,7 @@ export function CodeEditor({ onCodeChange, platform = 'roblox' }: CodeEditorProp
|
|||
}), []);
|
||||
|
||||
const editorLanguage = languageMap[platform];
|
||||
const [code, setCode] = useKV('aethex-current-code', `-- Welcome to AeThex Studio!
|
||||
const [code, setCode] = usePersistentState('aethex-current-code', `-- Welcome to AeThex Studio!
|
||||
-- Write your Roblox Lua code here
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useState } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog';
|
||||
import { Button } from './ui/button';
|
||||
import { Input } from './ui/input';
|
||||
import { Label } from './ui/label';
|
||||
import { Card } from './ui/card';
|
||||
import { Badge } from './ui/badge';
|
||||
import { Checkbox } from './ui/checkbox';
|
||||
import { Switch } from './ui/switch';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { GameController, Globe, DeviceMobile, FileCode, Info, Check } from '@phosphor-icons/react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
|
|
@ -205,7 +205,7 @@ export function NewProjectModal({ open, onClose, onCreateProject }: NewProjectMo
|
|||
<Checkbox
|
||||
id="platform-roblox"
|
||||
checked={platforms.roblox}
|
||||
onCheckedChange={(checked) =>
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
setPlatforms((p) => ({ ...p, roblox: checked === true }))
|
||||
}
|
||||
/>
|
||||
|
|
@ -218,7 +218,7 @@ export function NewProjectModal({ open, onClose, onCreateProject }: NewProjectMo
|
|||
<Checkbox
|
||||
id="platform-web"
|
||||
checked={platforms.web}
|
||||
onCheckedChange={(checked) =>
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
setPlatforms((p) => ({ ...p, web: checked === true }))
|
||||
}
|
||||
/>
|
||||
|
|
@ -231,7 +231,7 @@ export function NewProjectModal({ open, onClose, onCreateProject }: NewProjectMo
|
|||
<Checkbox
|
||||
id="platform-mobile"
|
||||
checked={platforms.mobile}
|
||||
onCheckedChange={(checked) =>
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
setPlatforms((p) => ({ ...p, mobile: checked === true }))
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
@ -11,10 +10,11 @@ import {
|
|||
import { Copy, FileCode, Download, Info, Play, FolderPlus, User, SignOut, List, ArrowsLeftRight } from '@phosphor-icons/react';
|
||||
import { toast } from 'sonner';
|
||||
import { useState, useEffect, useCallback, memo } from 'react';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog';
|
||||
import { ThemeSwitcher } from './ThemeSwitcher';
|
||||
import { PlatformSelector } from './PlatformSelector';
|
||||
import { PlatformId } from '@/lib/platforms';
|
||||
import { PlatformId } from '../lib/platforms';
|
||||
import { Button } from './ui/button';
|
||||
|
||||
interface ToolbarProps {
|
||||
code: string;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {
|
|||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Sparkle, Code, FileCode } from '@phosphor-icons/react';
|
||||
import { useKV } from '@github/spark/hooks';
|
||||
import { usePersistentState } from '@/lib/usePersistentState';
|
||||
|
||||
export function WelcomeDialog() {
|
||||
const [hasSeenWelcome, setHasSeenWelcome] = useKV('aethex-welcome-seen', 'false');
|
||||
const [hasSeenWelcome, setHasSeenWelcome] = usePersistentState('aethex-welcome-seen', 'false');
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { cn } from "@/lib/utils"
|
|||
function ResizablePanelGroup({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
|
||||
}: ComponentProps<typeof ResizablePrimitive.Group>) {
|
||||
return (
|
||||
<ResizablePrimitive.PanelGroup
|
||||
<ResizablePrimitive.Group
|
||||
data-slot="resizable-panel-group"
|
||||
className={cn(
|
||||
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
||||
|
|
@ -30,11 +30,11 @@ function ResizableHandle({
|
|||
withHandle,
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
||||
}: ComponentProps<typeof ResizablePrimitive.Separator> & {
|
||||
withHandle?: boolean
|
||||
}) {
|
||||
return (
|
||||
<ResizablePrimitive.PanelResizeHandle
|
||||
<ResizablePrimitive.Separator
|
||||
data-slot="resizable-handle"
|
||||
className={cn(
|
||||
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
||||
|
|
@ -47,7 +47,7 @@ function ResizableHandle({
|
|||
<GripVerticalIcon className="size-2.5" />
|
||||
</div>
|
||||
)}
|
||||
</ResizablePrimitive.PanelResizeHandle>
|
||||
</ResizablePrimitive.Separator>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,51 +3,196 @@ export const chapters = [
|
|||
{
|
||||
id: 1,
|
||||
title: 'Introduction to Roblox Scripting',
|
||||
content: `# Chapter 1: Introduction to Roblox Scripting\n\nWelcome to Roblox development!\n\n**Learning Objectives:**\n- Understand what Roblox Studio is\n- Learn why Lua is used\n- Write your first script\n\n**What is Roblox Studio?**\nRoblox Studio is the official IDE for creating Roblox games. It lets you build worlds, write scripts, and publish games.\n\n**Why Lua?**\nLua is a lightweight scripting language used for game logic in Roblox.\n\n**Your First Script:**\nPaste this in a Script object:\n\n\n```lua\nprint('Hello, Roblox!')\n```\n\nTry running your game to see the output!`
|
||||
content: `# Chapter 1: Introduction to Roblox Scripting
|
||||
|
||||
Welcome to Roblox development!
|
||||
|
||||
**Learning Objectives:**
|
||||
- Understand what Roblox Studio is
|
||||
- Learn why Lua is used
|
||||
- Write your first script
|
||||
|
||||
**What is Roblox Studio?**
|
||||
Roblox Studio is the official IDE for creating Roblox games. It lets you build worlds, write scripts, and publish games.
|
||||
|
||||
**Why Lua?**
|
||||
Lua is a lightweight scripting language used for game logic in Roblox.
|
||||
|
||||
**Your First Script:**
|
||||
Paste this in a Script object:
|
||||
|
||||
\`\`\`lua
|
||||
print('Hello, Roblox!')
|
||||
\`\`\`
|
||||
|
||||
Try running your game to see the output!`
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Variables and Data Types',
|
||||
content: `# Chapter 2: Variables and Data Types\n\n**Learning Objectives:**\n- Use variables to store data\n- Understand numbers, strings, tables\n\n**Variables:**\n```lua\nlocal score = 10\nlocal playerName = "Alex"\n```\n\n**Tables:**\n```lua\nlocal inventory = {"Sword", "Shield"}\nprint(inventory[1]) -- Sword\n```\n`
|
||||
content: `# Chapter 2: Variables and Data Types
|
||||
|
||||
**Learning Objectives:**
|
||||
- Use variables to store data
|
||||
- Understand numbers, strings, tables
|
||||
|
||||
**Variables:**
|
||||
\`\`\`lua
|
||||
local score = 10
|
||||
local playerName = "Alex"
|
||||
\`\`\`
|
||||
|
||||
**Tables:**
|
||||
\`\`\`lua
|
||||
local inventory = {"Sword", "Shield"}
|
||||
print(inventory[1]) -- Sword
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Functions and Events',
|
||||
content: `# Chapter 3: Functions and Events\n\n**Learning Objectives:**\n- Write reusable functions\n- Respond to game events\n\n**Functions:**\n```lua\nfunction greet(name)\n print("Hello, " .. name)\nend\ngreet("Alex")\n```\n\n**Events:**\n```lua\nlocal Players = game:GetService("Players")\nPlayers.PlayerAdded:Connect(function(player)\n print(player.Name .. " joined!")\nend)\n```\n`
|
||||
content: `# Chapter 3: Functions and Events
|
||||
|
||||
**Learning Objectives:**
|
||||
- Write reusable functions
|
||||
- Respond to game events
|
||||
|
||||
**Functions:**
|
||||
\`\`\`lua
|
||||
function greet(name)
|
||||
print("Hello, " .. name)
|
||||
end
|
||||
greet("Alex")
|
||||
\`\`\`
|
||||
|
||||
**Events:**
|
||||
\`\`\`lua
|
||||
local Players = game:GetService("Players")
|
||||
Players.PlayerAdded:Connect(function(player)
|
||||
print(player.Name .. " joined!")
|
||||
end)
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Game Objects and Hierarchy',
|
||||
content: `# Chapter 4: Game Objects and Hierarchy\n\n**Learning Objectives:**\n- Explore Roblox object hierarchy\n- Access and modify game objects\n\n**Example:**\n```lua\nlocal workspace = game.Workspace\nlocal part = workspace.Part\npart.BrickColor = BrickColor.new("Bright red")\n```\n`
|
||||
content: `# Chapter 4: Game Objects and Hierarchy
|
||||
|
||||
**Learning Objectives:**
|
||||
- Explore Roblox object hierarchy
|
||||
- Access and modify game objects
|
||||
|
||||
**Example:**
|
||||
\`\`\`lua
|
||||
local workspace = game.Workspace
|
||||
local part = workspace.Part
|
||||
part.BrickColor = BrickColor.new("Bright red")
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Player Interaction',
|
||||
content: `# Chapter 5: Player Interaction\n\n**Learning Objectives:**\n- Detect player actions\n- Respond to input\n\n**Example:**\n```lua\nlocal Players = game:GetService("Players")\nPlayers.PlayerAdded:Connect(function(player)\n player.Chatted:Connect(function(message)\n print(player.Name .. " said: " .. message)\n end)\nend)\n```\n`
|
||||
content: `# Chapter 5: Player Interaction
|
||||
|
||||
**Learning Objectives:**
|
||||
- Detect player actions
|
||||
- Respond to input
|
||||
|
||||
**Example:**
|
||||
\`\`\`lua
|
||||
local Players = game:GetService("Players")
|
||||
Players.PlayerAdded:Connect(function(player)
|
||||
player.Chatted:Connect(function(message)
|
||||
print(player.Name .. " said: " .. message)
|
||||
end)
|
||||
end)
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Building Game Logic',
|
||||
content: `# Chapter 6: Building Game Logic\n\n**Learning Objectives:**\n- Create rules and mechanics\n- Use conditions and loops\n\n**Example:**\n```lua\nlocal score = 0\nwhile score < 10 do\n score = score + 1\n print("Score:", score)\nend\n```\n`
|
||||
content: `# Chapter 6: Building Game Logic
|
||||
|
||||
**Learning Objectives:**
|
||||
- Create rules and mechanics
|
||||
- Use conditions and loops
|
||||
|
||||
**Example:**
|
||||
\`\`\`lua
|
||||
local score = 0
|
||||
while score < 10 do
|
||||
score = score + 1
|
||||
print("Score:", score)
|
||||
end
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: 'Saving Data',
|
||||
content: `# Chapter 7: Saving Data\n\n**Learning Objectives:**\n- Store player progress\n- Use DataStore service\n\n**Example:**\n```lua\nlocal DataStoreService = game:GetService("DataStoreService")\nlocal scoreStore = DataStoreService:GetDataStore("Scores")\n\n-- Save score\nscoreStore:SetAsync("player_123", 100)\n-- Load score\nlocal score = scoreStore:GetAsync("player_123")\nprint(score)\n```\n`
|
||||
content: `# Chapter 7: Saving Data
|
||||
|
||||
**Learning Objectives:**
|
||||
- Store player progress
|
||||
- Use DataStore service
|
||||
|
||||
**Example:**
|
||||
\`\`\`lua
|
||||
local DataStoreService = game:GetService("DataStoreService")
|
||||
local scoreStore = DataStoreService:GetDataStore("Scores")
|
||||
|
||||
-- Save score
|
||||
scoreStore:SetAsync("player_123", 100)
|
||||
-- Load score
|
||||
local score = scoreStore:GetAsync("player_123")
|
||||
print(score)
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: 'Deploying Your Game',
|
||||
content: `# Chapter 8: Deploying Your Game\n\n**Learning Objectives:**\n- Publish your game to Roblox\n- Update and manage releases\n\n**Steps:**\n1. Click "File > Publish to Roblox As..."\n2. Choose a name and description\n3. Set permissions and publish\n`
|
||||
content: `# Chapter 8: Deploying Your Game
|
||||
|
||||
**Learning Objectives:**
|
||||
- Publish your game to Roblox
|
||||
- Update and manage releases
|
||||
|
||||
**Steps:**
|
||||
1. Click "File > Publish to Roblox As..."
|
||||
2. Choose a name and description
|
||||
3. Set permissions and publish
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: 'Debugging and Testing',
|
||||
content: `# Chapter 9: Debugging and Testing\n\n**Learning Objectives:**\n- Find and fix bugs\n- Use print statements and breakpoints\n\n**Example:**\n```lua\nprint("Debug: Player joined")\n-- Use breakpoints in Studio to pause and inspect\n```\n`
|
||||
content: `# Chapter 9: Debugging and Testing
|
||||
|
||||
**Learning Objectives:**
|
||||
- Find and fix bugs
|
||||
- Use print statements and breakpoints
|
||||
|
||||
**Example:**
|
||||
\`\`\`lua
|
||||
print("Debug: Player joined")
|
||||
-- Use breakpoints in Studio to pause and inspect
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: 'Next Steps and Resources',
|
||||
content: `# Chapter 10: Next Steps and Resources\n\n**Continue Learning:**\n- Join the [Roblox Developer Forum](https://devforum.roblox.com/)\n- Explore [Roblox Education](https://education.roblox.com/)\n- Try building your own game!\n`
|
||||
content: `# Chapter 10: Next Steps and Resources
|
||||
|
||||
**Continue Learning:**
|
||||
- Join the [Roblox Developer Forum](https://devforum.roblox.com/)
|
||||
- Explore [Roblox Education](https://education.roblox.com/)
|
||||
- Try building your own game!
|
||||
`
|
||||
}
|
||||
];
|
||||
|
|
|
|||
27
src/lib/usePersistentState.ts
Normal file
27
src/lib/usePersistentState.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
|
||||
/**
|
||||
* usePersistentState is a React hook that syncs state with localStorage.
|
||||
* @param key The key to store the value under in localStorage.
|
||||
* @param initialValue The initial value to use if nothing is stored.
|
||||
*/
|
||||
export function usePersistentState<T>(key: string, initialValue: T): [T, (value: T) => void] {
|
||||
const [state, setState] = useState<T>(() => {
|
||||
if (typeof window === 'undefined') return initialValue;
|
||||
try {
|
||||
const item = window.localStorage.getItem(key);
|
||||
return item ? (JSON.parse(item) as T) : initialValue;
|
||||
} catch {
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
try {
|
||||
window.localStorage.setItem(key, JSON.stringify(state));
|
||||
} catch {}
|
||||
}, [key, state]);
|
||||
|
||||
return [state, setState];
|
||||
}
|
||||
5
src/types/lucide-react__icons__grip-vertical.d.ts
vendored
Normal file
5
src/types/lucide-react__icons__grip-vertical.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
declare module 'lucide-react/dist/esm/icons/grip-vertical' {
|
||||
import { FC, SVGProps } from 'react';
|
||||
const GripVerticalIcon: FC<SVGProps<SVGSVGElement>>;
|
||||
export default GripVerticalIcon;
|
||||
}
|
||||
1
src/types/phosphor-icons__react.d.ts
vendored
Normal file
1
src/types/phosphor-icons__react.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module '@phosphor-icons/react';
|
||||
1
src/types/sonner.d.ts
vendored
Normal file
1
src/types/sonner.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module 'sonner';
|
||||
3
src/types/window-spark.d.ts
vendored
Normal file
3
src/types/window-spark.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
interface Window {
|
||||
spark?: any;
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
|
|
|
|||
1
types/phosphor-icons__react.d.ts
vendored
Normal file
1
types/phosphor-icons__react.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module '@phosphor-icons/react';
|
||||
1
types/sonner.d.ts
vendored
Normal file
1
types/sonner.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module 'sonner';
|
||||
Loading…
Reference in a new issue