diff --git a/package-lock.json b/package-lock.json index d9957c1..42aeab0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "nanostores": "^1.0.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-resizable-panels": "^3.0.6", "tailwindcss": "^4.1.16" }, "devDependencies": { @@ -5803,6 +5804,16 @@ "node": ">=0.10.0" } }, + "node_modules/react-resizable-panels": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.6.tgz", + "integrity": "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==", + "license": "MIT", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/readdirp": { "version": "4.1.2", "license": "MIT", diff --git a/package.json b/package.json index b41ddeb..7a46a8a 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,10 @@ "nanostores": "^1.0.1", "react": "^19.2.0", "react-dom": "^19.2.0", - "tailwindcss": "^4.1.16" + "react-resizable-panels": "^3.0.6", + "sharp": "^0.34.4", + "tailwindcss": "^4.1.16", + "zustand": "^5.0.8" }, "devDependencies": { "@biomejs/biome": "2.3.2", diff --git a/src/components/shared/editor/CodeEditor.tsx b/src/components/shared/editor/CodeEditor.tsx deleted file mode 100644 index 7a4105e..0000000 --- a/src/components/shared/editor/CodeEditor.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import CodeMirror from "@uiw/react-codemirror" -import { useCallback, useState } from "react" -import { rust } from "~/helpers/config" -import { gruvbox } from "~/helpers/theme" - -export function CodeEditor() { - const [value, setValue] = useState('fn main() {\n println!("Hola, mundo!");\n}\n') - - const onChange = useCallback((val: string) => { - setValue(val) - }, []) - - return ( - - ) -} diff --git a/src/components/shared/sections/Section.astro b/src/components/shared/sections/Section.astro deleted file mode 100644 index 9e04bb3..0000000 --- a/src/components/shared/sections/Section.astro +++ /dev/null @@ -1,13 +0,0 @@ ---- -interface Props { - id?: string - className?: string - style?: string -} - -const { id, className, style } = Astro.props as Props ---- - -
- -
diff --git a/src/content.config.ts b/src/content.config.ts deleted file mode 100644 index 7e8e7d1..0000000 --- a/src/content.config.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { defineCollection, getCollection, z } from "astro:content" -import { glob } from "astro/loaders" - -const content = defineCollection({ - loader: glob({ pattern: "*.{md,mdx}", base: "./src/content" }), - schema: z.object({ - title: z.string().optional(), - description: z.string().optional(), - order: z.number(), - nextPath: z.string().optional(), - previousPath: z.string().optional(), - editor: z.boolean().optional(), - }), -}) - -/** - * Obtiene todo el contenido de la colección "content", lo ordena por el campo 'order' - * @returns id (número inicial) y slug (nombre) de cada entrada. - */ -export const allContent = await getCollection("content").then((entries) => { - return entries - .sort((a, b) => a.data.order - b.data.order) - .map((entry) => { - const [, id, slug] = entry.id.match(/^(\d+)(.+)$/) || [] - return { ...entry, id, slug } - }) -}) -/** - * Exporta las colecciones definidas para Astro. - * - * ! ADVERTENCIA: Esta es una variable interna de Astro y no debe ser utilizada directamente en el código de la aplicación. - */ -export const collections = { content } diff --git a/src/features/content/components/NavButtons.astro b/src/features/content/components/NavButtons.astro deleted file mode 100644 index d6379a9..0000000 --- a/src/features/content/components/NavButtons.astro +++ /dev/null @@ -1,53 +0,0 @@ ---- -import { IconArrowBigLeftLinesFilled, IconArrowBigRightLinesFilled } from "@tabler/icons-react" - -const { ...pagination } = Astro.props ---- - - -
- { - ( - - - - -
-

- Anterior -

- {pagination.previousPath} -
-
- ) - } - { - ( - -
-

- Siguiente -

- {pagination.nextPath} -
- - - -
- ) - } -
diff --git a/src/features/content/enums/OutputVariant.enum.ts b/src/features/content/enums/OutputVariant.enum.ts new file mode 100644 index 0000000..ad7714b --- /dev/null +++ b/src/features/content/enums/OutputVariant.enum.ts @@ -0,0 +1,4 @@ +export enum OutputVariant { + Stdout = "stdout", + Stderr = "stderr", +} diff --git a/src/features/content/enums/PanelVariant.enum.ts b/src/features/content/enums/PanelVariant.enum.ts new file mode 100644 index 0000000..94fe5f3 --- /dev/null +++ b/src/features/content/enums/PanelVariant.enum.ts @@ -0,0 +1,4 @@ +export enum PanelVariant { + ContentToEditor = "content-playground", + EditorToTerminal = "editor-terminal", +} diff --git a/src/features/content/hooks/usePanelContainerBreakpoints.ts b/src/features/content/hooks/usePanelContainerBreakpoints.ts new file mode 100644 index 0000000..1030817 --- /dev/null +++ b/src/features/content/hooks/usePanelContainerBreakpoints.ts @@ -0,0 +1,14 @@ +import { useMemo } from "react" + +export default function usePanelContainerBreakpoints(width: number) { + const size = useMemo(() => { + if (width >= 1440) return 4 + if (width >= 1024) return 5 + if (width >= 768) return 8 + if (width >= 640) return 10 + if (width >= 480) return 14 + return 18 + }, [width]) + + return { size } +} diff --git a/src/features/content/react/ReactCodeEditor.tsx b/src/features/content/react/ReactCodeEditor.tsx new file mode 100644 index 0000000..f7885da --- /dev/null +++ b/src/features/content/react/ReactCodeEditor.tsx @@ -0,0 +1,29 @@ +import CodeMirror from "@uiw/react-codemirror" +import { useCallback } from "react" +import { useRustCompilerStore } from "~/features/content/stores/useRustCompilerStore.ts" +import { rust } from "~/helpers/config" +import { gruvbox } from "~/helpers/theme" + +export default function ReactCodeEditor() { + const code = useRustCompilerStore((state) => state.code) + const setCode = useRustCompilerStore((state) => state.setCode) + + const onChange = useCallback( + (val: string) => { + setCode(val) + }, + [setCode], + ) + + return ( + + ) +} diff --git a/src/features/content/react/ReactCollapsiblePanel.tsx b/src/features/content/react/ReactCollapsiblePanel.tsx new file mode 100644 index 0000000..b37cf3a --- /dev/null +++ b/src/features/content/react/ReactCollapsiblePanel.tsx @@ -0,0 +1,31 @@ +import type { ReactNode } from "react" + +interface CollapsiblePanelProps { + icon: ReactNode + title: string + children: ReactNode + isCollapsed?: boolean +} + +export default function ReactCollapsiblePanel({ icon, title, children, isCollapsed = false }: CollapsiblePanelProps) { + if (isCollapsed) { + return ( +
+
+
{icon}
+ {title} +
+
+ ) + } + + return ( +
+
+ {icon} + {title} +
+ {children} +
+ ) +} diff --git a/src/features/content/react/ReactExecuteRust.tsx b/src/features/content/react/ReactExecuteRust.tsx new file mode 100644 index 0000000..f815f56 --- /dev/null +++ b/src/features/content/react/ReactExecuteRust.tsx @@ -0,0 +1,16 @@ +import { IconPlayerPlay } from "@tabler/icons-react" +import { useRustCompilerStore } from "~/features/content/stores/useRustCompilerStore.ts" + +export default function ReactExecuteRust() { + const execute = useRustCompilerStore((state) => state.execute) + return ( + + ) +} diff --git a/src/features/content/react/ReactMarkdownContainer.tsx b/src/features/content/react/ReactMarkdownContainer.tsx new file mode 100644 index 0000000..8c11f54 --- /dev/null +++ b/src/features/content/react/ReactMarkdownContainer.tsx @@ -0,0 +1,21 @@ +import { IconLicense } from "@tabler/icons-react" +import type { ReactNode } from "react" +import { PanelVariant } from "~/features/content/enums/PanelVariant.enum.ts" +import ReactCollapsiblePanel from "~/features/content/react/ReactCollapsiblePanel.tsx" +import { usePanelStore } from "~/features/content/stores/Panel.store.ts" + +interface ReactMarkdownContainerProps { + children: ReactNode +} + +export default function ReactMarkdownContainer({ children }: ReactMarkdownContainerProps) { + const isCollapsed = usePanelStore((state) => state.isCollapsed(`${PanelVariant.ContentToEditor}-primary`)) + + return ( + } title="Contenido"> +
+
{children}
+
+
+ ) +} diff --git a/src/features/content/react/ReactPanelContainer.tsx b/src/features/content/react/ReactPanelContainer.tsx new file mode 100644 index 0000000..ef32413 --- /dev/null +++ b/src/features/content/react/ReactPanelContainer.tsx @@ -0,0 +1,81 @@ +import { IconGripVertical } from "@tabler/icons-react" +import { type ReactNode, useEffect, useMemo, useRef } from "react" +import { type ImperativePanelHandle, Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels" +import { PanelVariant } from "~/features/content/enums/PanelVariant.enum.ts" +import usePanelContainerBreakpoints from "~/features/content/hooks/usePanelContainerBreakpoints.ts" +import { usePanelStore } from "~/features/content/stores/Panel.store.ts" +import useWindowSize from "~/features/shared/hooks/useWindowSize.ts" + +interface PanelContainerProps { + direction: "horizontal" | "vertical" + defaultLayout: [number, number] + first?: ReactNode + second?: ReactNode + variant: PanelVariant +} + +export default function ReactPanelContainer({ + first, + second, + direction = "horizontal", + defaultLayout = [50, 50], + variant, +}: PanelContainerProps) { + const { width } = useWindowSize() + const { size } = usePanelContainerBreakpoints(width) + const offset = useMemo(() => 4, []) + + const registerPanel = usePanelStore((state) => state.registerPanel) + const unregisterPanel = usePanelStore((state) => state.unregisterPanel) + const setCollapsed = usePanelStore((state) => state.setCollapsed) + + const primaryId = `${variant}-primary` + const secondaryId = `${variant}-secondary` + + const primaryPanelRef = useRef(null) + const secondaryPanelRef = useRef(null) + + useEffect(() => { + if (primaryPanelRef.current) registerPanel(primaryId, primaryPanelRef.current) + if (secondaryPanelRef.current) registerPanel(secondaryId, secondaryPanelRef.current) + + return () => { + unregisterPanel(primaryId) + unregisterPanel(secondaryId) + } + }, [registerPanel, unregisterPanel, primaryId, secondaryId]) + + return ( + + setCollapsed(primaryId, true)} + onExpand={() => setCollapsed(primaryId, false)} + > + {first} + + +
+ +
+
+ setCollapsed(secondaryId, true)} + onExpand={() => setCollapsed(secondaryId, false)} + > + {second} + +
+ ) +} diff --git a/src/features/content/react/ReactPanelHeader.tsx b/src/features/content/react/ReactPanelHeader.tsx new file mode 100644 index 0000000..570db9a --- /dev/null +++ b/src/features/content/react/ReactPanelHeader.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react" + +interface ReactPanelHeaderProps { + icon: ReactNode + title: string +} + +export default function ReactPanelHeader({ icon, title }: ReactPanelHeaderProps) { + return ( +
+ {icon} + {title} +
+ ) +} diff --git a/src/features/content/react/ReactPlayground.tsx b/src/features/content/react/ReactPlayground.tsx new file mode 100644 index 0000000..df0b971 --- /dev/null +++ b/src/features/content/react/ReactPlayground.tsx @@ -0,0 +1,26 @@ +import { IconCode } from "@tabler/icons-react" +import { PanelVariant } from "~/features/content/enums/PanelVariant.enum.ts" +import ReactCodeEditor from "~/features/content/react/ReactCodeEditor.tsx" +import ReactCollapsiblePanel from "~/features/content/react/ReactCollapsiblePanel.tsx" +import ReactPanelContainer from "~/features/content/react/ReactPanelContainer.tsx" +import ReactTerminal from "~/features/content/react/ReactTerminal.tsx" +import { usePanelStore } from "~/features/content/stores/Panel.store.ts" + +export default function ReactPlayground() { + const isCollapsed = usePanelStore((state) => state.isCollapsed(`${PanelVariant.ContentToEditor}-secondary`)) + return ( + } title="Editor" isCollapsed={isCollapsed}> + } + first={ +
+ +
+ } + /> +
+ ) +} diff --git a/src/features/content/react/ReactTerminal.tsx b/src/features/content/react/ReactTerminal.tsx new file mode 100644 index 0000000..065a9d4 --- /dev/null +++ b/src/features/content/react/ReactTerminal.tsx @@ -0,0 +1,16 @@ +import { useRustCompilerStore } from "~/features/content/stores/useRustCompilerStore.ts" +import TerminalHeader from "./terminal/TerminalHeader" +import TerminalOutput from "./terminal/TerminalOutput" + +export default function ReactTerminal() { + const isExecuting = useRustCompilerStore((state) => state.isExecuting) + const liveOutput = useRustCompilerStore((state) => state.liveOutput) + const result = useRustCompilerStore((state) => state.result) + + return ( +
+ + +
+ ) +} diff --git a/src/features/content/react/terminal/CompilationStatus.tsx b/src/features/content/react/terminal/CompilationStatus.tsx new file mode 100644 index 0000000..254fabc --- /dev/null +++ b/src/features/content/react/terminal/CompilationStatus.tsx @@ -0,0 +1,11 @@ +interface CompilationStatusProps { + success: boolean +} + +export default function CompilationStatus({ success }: CompilationStatusProps) { + return ( +
+ {success ? "✓ Compilado exitosamente" : "✗ Error de compilación"} +
+ ) +} diff --git a/src/features/content/react/terminal/ExecutingIndicator.tsx b/src/features/content/react/terminal/ExecutingIndicator.tsx new file mode 100644 index 0000000..63c814c --- /dev/null +++ b/src/features/content/react/terminal/ExecutingIndicator.tsx @@ -0,0 +1,9 @@ +import { IconLoader2 } from "@tabler/icons-react" + +export default function ExecutingIndicator() { + return ( +
+ $ cargo run +
+ ) +} diff --git a/src/features/content/react/terminal/OutputLine.tsx b/src/features/content/react/terminal/OutputLine.tsx new file mode 100644 index 0000000..7ed159a --- /dev/null +++ b/src/features/content/react/terminal/OutputLine.tsx @@ -0,0 +1,27 @@ +import { OutputVariant } from "~/features/content/enums/OutputVariant.enum.ts" + +interface OutputLineProps { + type: "stdout" | "stderr" + content: string +} + +export default function OutputLine({ type, content }: OutputLineProps) { + if (type === OutputVariant.Stdout) { + return ( +
+ {">>> "} + {content} +
+ ) + } + + if (type === OutputVariant.Stderr) { + return ( +
+ {content} +
+ ) + } + + return null +} diff --git a/src/features/content/react/terminal/TerminalHeader.tsx b/src/features/content/react/terminal/TerminalHeader.tsx new file mode 100644 index 0000000..2f0dd64 --- /dev/null +++ b/src/features/content/react/terminal/TerminalHeader.tsx @@ -0,0 +1,9 @@ +import TerminalTab from "./TerminalTab" + +export default function TerminalHeader() { + return ( +
+ +
+ ) +} diff --git a/src/features/content/react/terminal/TerminalOutput.tsx b/src/features/content/react/terminal/TerminalOutput.tsx new file mode 100644 index 0000000..9b971eb --- /dev/null +++ b/src/features/content/react/terminal/TerminalOutput.tsx @@ -0,0 +1,28 @@ +import CompilationStatus from "./CompilationStatus" +import ExecutingIndicator from "./ExecutingIndicator" +import OutputLine from "./OutputLine" + +interface TerminalOutputProps { + isExecuting: boolean + liveOutput: { + stdout?: string + stderr?: string + } + result?: { + success: boolean + } | null +} + +export default function TerminalOutput({ isExecuting, liveOutput, result }: TerminalOutputProps) { + return ( +
+ {isExecuting && } + + {liveOutput.stdout && } + + {liveOutput.stderr && } + + {result && !isExecuting && } +
+ ) +} diff --git a/src/features/content/react/terminal/TerminalTab.tsx b/src/features/content/react/terminal/TerminalTab.tsx new file mode 100644 index 0000000..508a3db --- /dev/null +++ b/src/features/content/react/terminal/TerminalTab.tsx @@ -0,0 +1,13 @@ +import { IconTerminal } from "@tabler/icons-react" + +export default function TerminalTab() { + return ( + + ) +} diff --git a/src/features/content/server/compiler.ts b/src/features/content/server/compiler.ts new file mode 100644 index 0000000..de3ab68 --- /dev/null +++ b/src/features/content/server/compiler.ts @@ -0,0 +1,66 @@ +const RUST_PLAYGROUND_API = "https://play.rust-lang.org/execute" + +export interface ExecuteOptions { + channel?: "stable" | "beta" | "nightly" + mode?: "debug" | "release" + edition?: "2015" | "2018" | "2021" | "2024" +} + +export interface ExecutionResult { + success: boolean + stdout: string + stderr: string +} + +type MessageHandler = (data: string) => void + +class RustPlayground { + async execute( + code: string, + options: ExecuteOptions = {}, + onStdout?: MessageHandler, + onStderr?: MessageHandler, + ): Promise { + try { + const response = await fetch(RUST_PLAYGROUND_API, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + code, + channel: options.channel || "stable", + mode: options.mode || "debug", + edition: options.edition || "2021", + crateType: "bin", + tests: false, + backtrace: false, + }), + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + const result = await response.json() + + if (onStdout && result.stdout) { + onStdout(result.stdout) + } + if (onStderr && result.stderr) { + onStderr(result.stderr) + } + + return { + success: result.success, + stdout: result.stdout || "", + stderr: result.stderr || "", + } + } catch (error) { + console.error("Error al ejecutar código:", error) + throw error + } + } +} + +export const rustPlayground = new RustPlayground() diff --git a/src/features/content/server/websocket.ts b/src/features/content/server/websocket.ts new file mode 100644 index 0000000..c46505d --- /dev/null +++ b/src/features/content/server/websocket.ts @@ -0,0 +1,106 @@ +const RUST_LANG_WS = "wss://play.rust-lang.org/websocket" + +let sequenceNumber = 0 + +export interface ExecuteOptions { + channel?: "stable" | "beta" | "nightly" + mode?: "debug" | "release" + edition?: "2015" | "2018" | "2021" | "2024" +} + +export interface ExecutionResult { + success: boolean + stdout: string + stderr: string +} + +type MessageHandler = (data: string) => void + +class RustPlayground { + private ws: WebSocket | null = null + private ready = false + private execution: { + stdout: string + stderr: string + resolve: (result: ExecutionResult) => void + onStdout?: MessageHandler + onStderr?: MessageHandler + } | null = null + + async connect(): Promise { + if (this.ready) return + + return new Promise((resolve) => { + this.ws = new WebSocket(RUST_LANG_WS) + + this.ws.onopen = () => { + this.ws!.send( + JSON.stringify({ + type: "websocket/connected", + payload: { iAcceptThisIsAnUnsupportedApi: true }, + meta: { websocket: true, sequenceNumber: ++sequenceNumber }, + }), + ) + this.ready = true + resolve() + } + + this.ws.onmessage = (event) => { + if (!this.execution) return + + const { type, payload } = JSON.parse(event.data) + + if (type === "output/execute/wsExecuteStdout") { + this.execution.stdout += payload + this.execution.onStdout?.(payload) + } else if (type === "output/execute/wsExecuteStderr") { + this.execution.stderr += payload + this.execution.onStderr?.(payload) + } else if (type === "output/execute/wsExecuteEnd") { + this.execution.resolve({ + success: payload.success, + stdout: this.execution.stdout, + stderr: this.execution.stderr, + }) + this.execution = null + } + } + + this.ws.onclose = () => { + this.ready = false + this.ws = null + } + }) + } + + async execute( + code: string, + options: ExecuteOptions, + onStdout?: MessageHandler, + onStderr?: MessageHandler, + ): Promise { + await this.connect() + + return new Promise((resolve) => { + this.execution = { stdout: "", stderr: "", resolve, onStdout, onStderr } + + this.ws!.send( + JSON.stringify({ + type: "output/execute/wsExecuteRequest", + payload: { + channel: options.channel, + mode: options.mode, + edition: options.edition, + crateType: "bin", + tests: false, + code, + backtrace: false, + }, + meta: { websocket: true, sequenceNumber: ++sequenceNumber }, + }), + ) + }) + } +} + +export const rustPlayground = new RustPlayground() diff --git a/src/features/content/stores/Panel.store.ts b/src/features/content/stores/Panel.store.ts new file mode 100644 index 0000000..539f234 --- /dev/null +++ b/src/features/content/stores/Panel.store.ts @@ -0,0 +1,46 @@ +import type { ImperativePanelHandle } from "react-resizable-panels" +import { create } from "zustand/react" + +interface PanelState { + panels: Map + collapsedPanels: Set + registerPanel: (id: string, panel: ImperativePanelHandle) => void + unregisterPanel: (id: string) => void + getPanel: (id: string) => ImperativePanelHandle | null + setCollapsed: (id: string, isCollapsed: boolean) => void + isCollapsed: (id: string) => boolean +} + +export const usePanelStore = create((set, get) => ({ + panels: new Map(), + collapsedPanels: new Set(), + + registerPanel: (id, panel) => + set((state) => { + const newPanels = new Map(state.panels) + newPanels.set(id, panel) + return { panels: newPanels } + }), + + unregisterPanel: (id) => + set((state) => { + const newPanels = new Map(state.panels) + newPanels.delete(id) + return { panels: newPanels } + }), + + getPanel: (id) => get().panels.get(id) || null, + + setCollapsed: (id, isCollapsed) => + set((state) => { + const newSet = new Set(state.collapsedPanels) + if (isCollapsed) { + newSet.add(id) + } else { + newSet.delete(id) + } + return { collapsedPanels: newSet } + }), + + isCollapsed: (id) => get().collapsedPanels.has(id), +})) diff --git a/src/features/content/stores/useRustCompilerStore.ts b/src/features/content/stores/useRustCompilerStore.ts new file mode 100644 index 0000000..080c127 --- /dev/null +++ b/src/features/content/stores/useRustCompilerStore.ts @@ -0,0 +1,64 @@ +import { create } from "zustand" +import { type ExecutionResult, rustPlayground } from "~/features/content/server/compiler.ts" + +interface RustCompilerState { + code: string + isExecuting: boolean + result: ExecutionResult | null + liveOutput: { stdout: string; stderr: string } + setCode: (code: string) => void + execute: () => Promise + reset: () => void +} + +export const useRustCompilerStore = create((set, get) => ({ + code: 'fn main() {\n println!("Hello, world!");\n}', + isExecuting: false, + result: null, + liveOutput: { stdout: "", stderr: "" }, + + setCode: (code) => set({ code }), + + execute: async () => { + set({ + isExecuting: true, + result: null, + liveOutput: { stdout: "", stderr: "" }, + }) + + try { + const executionResult = await rustPlayground.execute( + get().code, + { + channel: "stable", + mode: "debug", + edition: "2021", + }, + (data) => + set((state) => ({ + liveOutput: { ...state.liveOutput, stdout: state.liveOutput.stdout + data }, + })), + (data) => + set((state) => ({ + liveOutput: { ...state.liveOutput, stderr: state.liveOutput.stderr + data }, + })), + ) + + console.log("Resultado de la ejecución:", executionResult) + + set({ result: executionResult }) + } catch (error) { + console.error("Error ejecutando código:", error) + } finally { + set({ isExecuting: false }) + } + }, + + reset: () => + set({ + code: 'fn main() {\n println!("Hello, world!");\n}', + isExecuting: false, + result: null, + liveOutput: { stdout: "", stderr: "" }, + }), +})) diff --git a/src/features/content/ui/Main.astro b/src/features/content/ui/Main.astro new file mode 100644 index 0000000..49c3a32 --- /dev/null +++ b/src/features/content/ui/Main.astro @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/features/content/ui/NavButtons.astro b/src/features/content/ui/NavButtons.astro new file mode 100644 index 0000000..76994ad --- /dev/null +++ b/src/features/content/ui/NavButtons.astro @@ -0,0 +1,43 @@ +--- +import { IconArrowBigLeftLinesFilled, IconArrowBigRightLinesFilled } from "@tabler/icons-react" + +const { previous, next } = Astro.props +--- + + diff --git a/src/features/content/ui/PanelContent.astro b/src/features/content/ui/PanelContent.astro new file mode 100644 index 0000000..988ba0a --- /dev/null +++ b/src/features/content/ui/PanelContent.astro @@ -0,0 +1,15 @@ +--- +import { IconLicense } from "@tabler/icons-react" +--- + +
+ + +
diff --git a/src/features/content/components/ToggleSidebarButton.tsx b/src/features/content/ui/ToggleSidebarButton.tsx similarity index 100% rename from src/features/content/components/ToggleSidebarButton.tsx rename to src/features/content/ui/ToggleSidebarButton.tsx diff --git a/src/features/home/components/Community.astro b/src/features/home/ui/Community.astro similarity index 91% rename from src/features/home/components/Community.astro rename to src/features/home/ui/Community.astro index 27746c5..bd7102e 100644 --- a/src/features/home/components/Community.astro +++ b/src/features/home/ui/Community.astro @@ -1,12 +1,11 @@ --- import { Image } from "astro:assets" import discordMarkImage from "~/assets/images/webp/discord-mark-white.webp" -import Section from "~/components/shared/sections/Section.astro" --- -

Comunidad Rust

@@ -42,4 +41,4 @@ import Section from "~/components/shared/sections/Section.astro" title="Widget de Discord de la Comunidad Rust"> -
+ diff --git a/src/features/home/components/Hero.astro b/src/features/home/ui/Hero.astro similarity index 79% rename from src/features/home/components/Hero.astro rename to src/features/home/ui/Hero.astro index 8045dfb..c7d33d7 100644 --- a/src/features/home/components/Hero.astro +++ b/src/features/home/ui/Hero.astro @@ -2,15 +2,11 @@ import { Image } from "astro:assets" import { IconBrandGithub } from "@tabler/icons-react" import logoImage from "~/assets/images/webp/logo.webp" -import Section from "~/components/shared/sections/Section.astro" -import { allContent } from "~/content.config" - -const content = allContent.find((c) => c.slug) --- -
c.slug) Aprender -
+