Initialized repository for chat Terminal multiplexor website
Co-authored-by: Jeff Emmett <46964190+Jeff-Emmett@users.noreply.github.com>
This commit is contained in:
commit
01f7de52c5
|
|
@ -0,0 +1,38 @@
|
||||||
|
# sv
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a new project in the current directory
|
||||||
|
npx sv create
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npx sv create my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To create a production version of your app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "base-sveltekit-app",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"lint": "prettier --check ."
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"src": "latest",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
"vite": "^6.2.6",
|
||||||
|
"prettier": "^3.4.2",
|
||||||
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"tailwindcss": "^4.0.0",
|
||||||
|
"typescript": "5.9.3"
|
||||||
|
},
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,87 @@
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--font-mono: "JetBrains Mono", "Fira Code", "Courier New", monospace;
|
||||||
|
--font-sans: var(--font-mono); /* Force mono everywhere for the nerdy vibe */
|
||||||
|
|
||||||
|
--color-background: #0a0a0a;
|
||||||
|
--color-foreground: #00ff00; /* Terminal Green */
|
||||||
|
|
||||||
|
--color-card: #111111;
|
||||||
|
--color-card-foreground: #e5e5e5;
|
||||||
|
|
||||||
|
--color-popover: #111111;
|
||||||
|
--color-popover-foreground: #e5e5e5;
|
||||||
|
|
||||||
|
--color-primary: #00ff00;
|
||||||
|
--color-primary-foreground: #000000;
|
||||||
|
|
||||||
|
--color-secondary: #333333;
|
||||||
|
--color-secondary-foreground: #ffffff;
|
||||||
|
|
||||||
|
--color-muted: #222222;
|
||||||
|
--color-muted-foreground: #888888;
|
||||||
|
|
||||||
|
--color-accent: #00ff00;
|
||||||
|
--color-accent-foreground: #000000;
|
||||||
|
|
||||||
|
--color-destructive: #ff0000;
|
||||||
|
--color-destructive-foreground: #ffffff;
|
||||||
|
|
||||||
|
--color-border: #333333;
|
||||||
|
--color-input: #222222;
|
||||||
|
--color-ring: #00ff00;
|
||||||
|
|
||||||
|
--radius-sm: 0px;
|
||||||
|
--radius-md: 0px;
|
||||||
|
--radius-lg: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground font-mono antialiased selection:bg-primary selection:text-primary-foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Scrollbar for that retro feel */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #0a0a0a;
|
||||||
|
border-left: 1px solid #333;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #333;
|
||||||
|
border: 1px solid #0a0a0a;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #00ff00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanline {
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 0),
|
||||||
|
rgba(255, 255, 255, 0) 50%,
|
||||||
|
rgba(0, 0, 0, 0.2) 50%,
|
||||||
|
rgba(0, 0, 0, 0.2)
|
||||||
|
);
|
||||||
|
background-size: 100% 4px;
|
||||||
|
position: fixed;
|
||||||
|
pointer-events: none;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glow-text {
|
||||||
|
text-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
layout = { type: 'pane', id: 0 },
|
||||||
|
activePaneId = 0
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let canvas: HTMLCanvasElement;
|
||||||
|
let ctx: CanvasRenderingContext2D | null = null;
|
||||||
|
let containerWidth = $state(0);
|
||||||
|
let containerHeight = $state(0);
|
||||||
|
let animationFrameId: number;
|
||||||
|
let lastTime = 0;
|
||||||
|
let cursorBlink = true;
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
const BG_COLOR = '#0a0a0a';
|
||||||
|
const BORDER_COLOR = '#333333';
|
||||||
|
const ACTIVE_BORDER_COLOR = '#00ff00';
|
||||||
|
const TEXT_COLOR = '#00ff00';
|
||||||
|
const MUTED_TEXT = '#444444';
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (canvas && layout) {
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
if (!canvas) return;
|
||||||
|
const parent = canvas.parentElement;
|
||||||
|
if (parent) {
|
||||||
|
// Handle high DPI displays
|
||||||
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
const rect = parent.getBoundingClientRect();
|
||||||
|
|
||||||
|
canvas.width = rect.width * dpr;
|
||||||
|
canvas.height = rect.height * dpr;
|
||||||
|
|
||||||
|
// Scale context to match
|
||||||
|
ctx?.scale(dpr, dpr);
|
||||||
|
|
||||||
|
containerWidth.set(rect.width);
|
||||||
|
containerHeight.set(rect.height);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate(time: number) {
|
||||||
|
if (time - lastTime > 500) { // Blink every 500ms
|
||||||
|
cursorBlink = !cursorBlink;
|
||||||
|
lastTime = time;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
window.addEventListener('resize', resize);
|
||||||
|
resize();
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', resize);
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
ctx.fillStyle = BG_COLOR;
|
||||||
|
ctx.fillRect(0, 0, containerWidth.get(), containerHeight.get());
|
||||||
|
|
||||||
|
// Draw Layout
|
||||||
|
drawNode(layout, 0, 0, containerWidth.get(), containerHeight.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawNode(node: any, x: number, y: number, w: number, h: number) {
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
// Add a small gap for the border
|
||||||
|
const gap = 2;
|
||||||
|
|
||||||
|
if (node.type === 'pane') {
|
||||||
|
// Draw Pane Background
|
||||||
|
ctx.fillStyle = '#050505';
|
||||||
|
ctx.fillRect(x + gap, y + gap, w - gap*2, h - gap*2);
|
||||||
|
|
||||||
|
// Draw Border
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = node.id === activePaneId ? ACTIVE_BORDER_COLOR : BORDER_COLOR;
|
||||||
|
|
||||||
|
// If active, make border slightly thicker/glowy
|
||||||
|
if (node.id === activePaneId) {
|
||||||
|
ctx.shadowColor = ACTIVE_BORDER_COLOR;
|
||||||
|
ctx.shadowBlur = 4;
|
||||||
|
} else {
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.strokeRect(x + gap, y + gap, w - gap*2, h - gap*2);
|
||||||
|
ctx.shadowBlur = 0; // Reset
|
||||||
|
|
||||||
|
// Draw Pane ID/Status
|
||||||
|
ctx.font = '12px "JetBrains Mono", monospace';
|
||||||
|
ctx.fillStyle = node.id === activePaneId ? TEXT_COLOR : MUTED_TEXT;
|
||||||
|
ctx.fillText(`[${node.id}] zsh`, x + 15, y + 25);
|
||||||
|
|
||||||
|
// Draw "Content" simulation
|
||||||
|
if (h > 60) {
|
||||||
|
ctx.fillStyle = MUTED_TEXT;
|
||||||
|
const lines = Math.floor((h - 50) / 16);
|
||||||
|
for (let i = 0; i < Math.min(lines, 8); i++) {
|
||||||
|
// Randomize line length to look like code
|
||||||
|
const width = (Math.sin(i * 132 + node.id) * 0.5 + 0.5) * (w - 60) + 20;
|
||||||
|
ctx.fillRect(x + 15, y + 45 + (i * 16), width, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cursor
|
||||||
|
if (node.id === activePaneId && cursorBlink) {
|
||||||
|
ctx.fillStyle = TEXT_COLOR;
|
||||||
|
const lastLineY = y + 45 + (Math.min(lines, 8) * 16);
|
||||||
|
ctx.fillRect(x + 15, lastLineY, 8, 14);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (node.type === 'split-v') {
|
||||||
|
// Vertical Split (Top/Bottom)
|
||||||
|
const h1 = h * (node.ratio || 0.5);
|
||||||
|
const h2 = h - h1;
|
||||||
|
drawNode(node.children[0], x, y, w, h1);
|
||||||
|
drawNode(node.children[1], x, y + h1, w, h2);
|
||||||
|
} else if (node.type === 'split-h') {
|
||||||
|
// Horizontal Split (Left/Right)
|
||||||
|
const w1 = w * (node.ratio || 0.5);
|
||||||
|
const w2 = w - w1;
|
||||||
|
drawNode(node.children[0], x, y, w1, h);
|
||||||
|
drawNode(node.children[1], x + w1, y, w2, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-full h-full min-h-[400px] bg-black border border-border relative group overflow-hidden">
|
||||||
|
<canvas bind:this={canvas} class="block w-full h-full"></canvas>
|
||||||
|
|
||||||
|
<!-- Scanline overlay for the canvas specifically -->
|
||||||
|
<div class="absolute inset-0 pointer-events-none opacity-10 bg-[linear-gradient(rgba(18,16,16,0)_50%,rgba(0,0,0,0.25)_50%),linear-gradient(90deg,rgba(255,0,0,0.06),rgba(0,255,0,0.02),rgba(0,0,255,0.06))] z-10 bg-[length:100%_2px,3px_100%]"></div>
|
||||||
|
|
||||||
|
<div class="absolute bottom-2 right-2 text-[10px] text-muted-foreground opacity-50 group-hover:opacity-100 transition-opacity z-20 font-mono">
|
||||||
|
RENDERER: CANVAS_2D // {containerWidth.get()}x{containerHeight.get()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import '../app.css';
|
||||||
|
let { children } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="min-h-screen flex flex-col relative overflow-hidden">
|
||||||
|
<!-- Retro CRT Scanline Effect -->
|
||||||
|
<div class="scanline"></div>
|
||||||
|
|
||||||
|
<header class="border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50">
|
||||||
|
<div class="container flex h-14 max-w-screen-2xl items-center px-4">
|
||||||
|
<div class="mr-4 flex">
|
||||||
|
<a class="mr-6 flex items-center space-x-2" href="/">
|
||||||
|
<span class="font-bold text-primary text-xl tracking-tighter glow-text">~/mytmux.life</span>
|
||||||
|
<span class="animate-pulse inline-block w-2 h-4 bg-primary"></span>
|
||||||
|
</a>
|
||||||
|
<nav class="flex items-center space-x-6 text-sm font-medium text-muted-foreground">
|
||||||
|
<a class="transition-colors hover:text-primary hover:underline decoration-primary underline-offset-4" href="#learn">/docs</a>
|
||||||
|
<a class="transition-colors hover:text-primary hover:underline decoration-primary underline-offset-4" href="#configurator">/config</a>
|
||||||
|
<a class="transition-colors hover:text-primary hover:underline decoration-primary underline-offset-4" href="#about">/about</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-1 items-center justify-end space-x-2">
|
||||||
|
<div class="text-xs text-muted-foreground border border-border px-2 py-1">
|
||||||
|
v1.0.0-alpha
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="flex-1 relative z-10">
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="border-t border-border py-6 md:px-8 md:py-0">
|
||||||
|
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
|
||||||
|
<p class="text-center text-sm leading-loose text-muted-foreground md:text-left">
|
||||||
|
Built by <span class="text-primary">v0</span>. The source code is available on <a href="https://github.com" class="font-medium underline underline-offset-4">GitHub</a>.
|
||||||
|
</p>
|
||||||
|
<div class="text-xs text-muted-foreground">
|
||||||
|
$ echo "Happy Hacking"
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import TerminalVisualizer from '$lib/components/terminal-visualizer.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
// State for the layout configurator
|
||||||
|
let layout = $state({ type: 'pane', id: 0 });
|
||||||
|
let nextId = 1;
|
||||||
|
let activePaneId = $state(0);
|
||||||
|
let generatedConfig = $state('');
|
||||||
|
|
||||||
|
function splitPane(direction: 'h' | 'v') {
|
||||||
|
// Find the active pane in the tree and replace it with a split
|
||||||
|
const newLayout = JSON.parse(JSON.stringify(layout));
|
||||||
|
|
||||||
|
function findAndSplit(node: any) {
|
||||||
|
if (node.type === 'pane') {
|
||||||
|
if (node.id === activePaneId) {
|
||||||
|
const oldId = node.id;
|
||||||
|
const newId = nextId++;
|
||||||
|
|
||||||
|
// Create new split node
|
||||||
|
node.type = direction === 'h' ? 'split-h' : 'split-v';
|
||||||
|
node.ratio = 0.5;
|
||||||
|
delete node.id;
|
||||||
|
node.children = [
|
||||||
|
{ type: 'pane', id: oldId },
|
||||||
|
{ type: 'pane', id: newId }
|
||||||
|
];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return findAndSplit(node.children[0]) || findAndSplit(node.children[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findAndSplit(newLayout);
|
||||||
|
layout = newLayout;
|
||||||
|
generateTmuxConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetLayout() {
|
||||||
|
layout = { type: 'pane', id: 0 };
|
||||||
|
nextId = 1;
|
||||||
|
activePaneId = 0;
|
||||||
|
generateTmuxConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPane(id: number) {
|
||||||
|
activePaneId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple recursive function to generate tmux commands
|
||||||
|
function generateTmuxConfig() {
|
||||||
|
let config = `# ~/.tmux.conf setup\n`;
|
||||||
|
config += `# Generated by mytmux.life\n\n`;
|
||||||
|
config += `new-session -s development -n editor\n`;
|
||||||
|
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (node.type === 'split-h') {
|
||||||
|
config += `split-window -h\n`;
|
||||||
|
traverse(node.children[1]); // Right child
|
||||||
|
config += `select-pane -L\n`; // Go back left
|
||||||
|
traverse(node.children[0]); // Left child
|
||||||
|
} else if (node.type === 'split-v') {
|
||||||
|
config += `split-window -v\n`;
|
||||||
|
traverse(node.children[1]); // Bottom child
|
||||||
|
config += `select-pane -U\n`; // Go back up
|
||||||
|
traverse(node.children[0]); // Top child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(layout);
|
||||||
|
|
||||||
|
config += `\n# Select the initially active pane\n`;
|
||||||
|
config += `select-pane -t ${activePaneId}\n`;
|
||||||
|
|
||||||
|
generatedConfig = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
generateTmuxConfig();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-16 pb-20">
|
||||||
|
|
||||||
|
<!-- Hero Section -->
|
||||||
|
<section class="container px-4 pt-20 md:pt-32">
|
||||||
|
<div class="grid gap-8 md:grid-cols-2 items-center">
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div class="inline-flex items-center rounded-full border border-primary/50 bg-primary/10 px-3 py-1 text-xs font-medium text-primary">
|
||||||
|
<span class="mr-2 h-2 w-2 rounded-full bg-primary animate-pulse"></span>
|
||||||
|
SYSTEM ONLINE
|
||||||
|
</div>
|
||||||
|
<h1 class="text-4xl font-extrabold tracking-tight lg:text-6xl glow-text">
|
||||||
|
Master Your <br/>
|
||||||
|
<span class="text-primary"><Terminal /></span>
|
||||||
|
</h1>
|
||||||
|
<p class="text-xl text-muted-foreground max-w-[600px]">
|
||||||
|
Stop wasting time switching windows.
|
||||||
|
<span class="text-foreground font-bold">mytmux.life</span> helps you architect the perfect terminal development environment.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4 pt-4">
|
||||||
|
<a href="#configurator" class="inline-flex h-12 items-center justify-center bg-primary text-primary-foreground px-8 text-sm font-medium transition-colors hover:bg-primary/90 hover:shadow-[0_0_20px_rgba(0,255,0,0.5)]">
|
||||||
|
INITIALIZE_CONFIG
|
||||||
|
</a>
|
||||||
|
<a href="#learn" class="inline-flex h-12 items-center justify-center border border-input bg-background px-8 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground">
|
||||||
|
READ_MAN_PAGE
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ASCII Art / Decorative Element -->
|
||||||
|
<div class="hidden md:block p-6 border border-border bg-card/50 font-mono text-xs leading-none text-muted-foreground select-none overflow-hidden">
|
||||||
|
<pre>
|
||||||
|
_________________________________________
|
||||||
|
/ \
|
||||||
|
| root@mytmux:~ $ tmux new -s dev |
|
||||||
|
| [0] nvim ---------------- [1] server -- |
|
||||||
|
| | | | |
|
||||||
|
| | import { life } | npm run | |
|
||||||
|
| | from 'tmux'; | dev | |
|
||||||
|
| | | | |
|
||||||
|
| | // TODO: Sleep | | |
|
||||||
|
| |________________________|____________| |
|
||||||
|
| [2] logs ------------------------------ |
|
||||||
|
| | > ready in 200ms | |
|
||||||
|
| | > watching files... | |
|
||||||
|
| |_____________________________________| |
|
||||||
|
\_________________________________________/
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Configurator Section -->
|
||||||
|
<section id="configurator" class="container px-4 py-12">
|
||||||
|
<div class="flex flex-col space-y-4 mb-8">
|
||||||
|
<h2 class="text-3xl font-bold tracking-tight border-l-4 border-primary pl-4">
|
||||||
|
ENVIRONMENT_CONFIGURATOR
|
||||||
|
</h2>
|
||||||
|
<p class="text-muted-foreground">
|
||||||
|
Visually design your session layout. Click actions to split the active pane.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid lg:grid-cols-3 gap-8 h-[600px]">
|
||||||
|
<!-- Controls & Output -->
|
||||||
|
<div class="flex flex-col gap-6 h-full">
|
||||||
|
<div class="p-6 border border-border bg-card space-y-6">
|
||||||
|
<h3 class="text-lg font-bold flex items-center gap-2">
|
||||||
|
<span class="text-primary">></span> ACTIONS
|
||||||
|
</h3>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<button
|
||||||
|
onclick={() => splitPane('h')}
|
||||||
|
class="h-20 border border-dashed border-muted-foreground hover:border-primary hover:text-primary hover:bg-primary/5 transition-all flex flex-col items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<div class="flex gap-1 h-6 w-8 border border-current p-0.5">
|
||||||
|
<div class="w-1/2 h-full bg-current/50"></div>
|
||||||
|
<div class="w-1/2 h-full border border-current"></div>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">SPLIT_VERTICAL (%)</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={() => splitPane('v')}
|
||||||
|
class="h-20 border border-dashed border-muted-foreground hover:border-primary hover:text-primary hover:bg-primary/5 transition-all flex flex-col items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-1 h-8 w-6 border border-current p-0.5">
|
||||||
|
<div class="h-1/2 w-full bg-current/50"></div>
|
||||||
|
<div class="h-1/2 w-full border border-current"></div>
|
||||||
|
</div>
|
||||||
|
<span class="text-xs">SPLIT_HORIZONTAL (")</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-4 border-t border-border">
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<span class="text-xs text-muted-foreground">ACTIVE_PANE_ID:</span>
|
||||||
|
<span class="font-mono text-primary">{activePaneId}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
onclick={resetLayout}
|
||||||
|
class="flex-1 py-2 text-xs bg-destructive/10 text-destructive hover:bg-destructive/20 border border-destructive/20"
|
||||||
|
>
|
||||||
|
RESET_LAYOUT
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 p-6 border border-border bg-card flex flex-col min-h-0">
|
||||||
|
<h3 class="text-lg font-bold flex items-center gap-2 mb-4">
|
||||||
|
<span class="text-primary">></span> GENERATED_CONFIG
|
||||||
|
</h3>
|
||||||
|
<div class="flex-1 bg-black p-4 font-mono text-xs text-muted-foreground overflow-auto border border-border">
|
||||||
|
<pre>{generatedConfig}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Visualizer Canvas -->
|
||||||
|
<div class="lg:col-span-2 h-full border border-border bg-card p-1 relative">
|
||||||
|
<div class="absolute top-0 left-0 bg-primary text-black text-[10px] font-bold px-2 py-0.5 z-10">
|
||||||
|
CANVAS_RENDER_TARGET
|
||||||
|
</div>
|
||||||
|
<TerminalVisualizer {layout} {activePaneId} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Documentation / Info Section -->
|
||||||
|
<section id="learn" class="container px-4 py-12">
|
||||||
|
<div class="grid md:grid-cols-3 gap-12">
|
||||||
|
<div class="md:col-span-1 space-y-2">
|
||||||
|
<h3 class="text-xl font-bold text-primary">01. MULTIPLEXING</h3>
|
||||||
|
<p class="text-sm text-muted-foreground leading-relaxed">
|
||||||
|
Run multiple terminal sessions inside one single window. Detach them and leave them running in the background, then reattach later.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-1 space-y-2">
|
||||||
|
<h3 class="text-xl font-bold text-primary">02. WINDOWS & PANES</h3>
|
||||||
|
<p class="text-sm text-muted-foreground leading-relaxed">
|
||||||
|
Organize your workspace into windows (tabs) and panes (splits). Keep your editor, server logs, and git commands visible at once.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-1 space-y-2">
|
||||||
|
<h3 class="text-xl font-bold text-primary">03. CONFIGURATION</h3>
|
||||||
|
<p class="text-sm text-muted-foreground leading-relaxed">
|
||||||
|
Tmux is highly scriptable. Bind keys, change status bar colors, and create custom layouts to fit your specific workflow needs.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Cheat Sheet -->
|
||||||
|
<section class="container px-4 py-12 border-t border-border">
|
||||||
|
<h2 class="text-2xl font-bold mb-8">QUICK_REFERENCE_CARD</h2>
|
||||||
|
<div class="grid md:grid-cols-2 gap-4">
|
||||||
|
{#each [
|
||||||
|
{ cmd: 'Ctrl+b %', desc: 'Split pane vertically' },
|
||||||
|
{ cmd: 'Ctrl+b "', desc: 'Split pane horizontally' },
|
||||||
|
{ cmd: 'Ctrl+b o', desc: 'Swap to next pane' },
|
||||||
|
{ cmd: 'Ctrl+b c', desc: 'Create new window' },
|
||||||
|
{ cmd: 'Ctrl+b n', desc: 'Next window' },
|
||||||
|
{ cmd: 'Ctrl+b d', desc: 'Detach session' },
|
||||||
|
] as item}
|
||||||
|
<div class="flex items-center justify-between p-4 border border-border bg-card/50 hover:bg-card transition-colors group">
|
||||||
|
<span class="text-muted-foreground group-hover:text-foreground transition-colors">{item.desc}</span>
|
||||||
|
<code class="bg-secondary px-2 py-1 text-primary text-xs">{item.cmd}</code>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,18 @@
|
||||||
|
import adapter from '@sveltejs/adapter-auto';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
// Consult https://svelte.dev/docs/kit/integrations
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
|
||||||
|
kit: {
|
||||||
|
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||||
|
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||||
|
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||||
|
adapter: adapter()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||||
|
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||||
|
//
|
||||||
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [tailwindcss(), sveltekit()]
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue