Added (all now linked from their live locations): - home_root/.claude/settings.json. The statusLine command now uses $HOME instead of a Linux-only absolute path. - home_root/.claude/bin/ratecheck and home_root/.claude/skills/australis-design. - home_config/zsh/claude-config-dir.zsh and home_root/.local/bin/claude-config-dir-init, the per-repo Claude Code login helper and its seeding script. - home_root/.gitconfig and home_config/git/ignore. - home_root/.zshenv, with the cargo env line guarded so boxes without rust start clean. link-dotfiles --check reports drift without changing anything: tracked files whose live copy is no longer a link (stow dry-run conflicts) and links into this repo that dangle. Exits 1 on drift. Verified against a clean baseline and two planted drifts, a detached .nanorc and a dangling link. .mailmap maps every earlier identity to Vuong Hoang: the repo-local 'Your Name' placeholder config (now removed), host-generated emails, and aider suffixes. CLAUDE.md: the symlink warning now lists every linked file and points at --check. README: documents what is and isn't tracked under .claude, the edit rule, and --check.
286 lines
12 KiB
HTML
286 lines
12 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
<title>Australis — Terminal UI Kit</title>
|
|
<link rel="stylesheet" href="../../colors_and_type.css"/>
|
|
<style>
|
|
html, body { height: 100%; margin: 0; }
|
|
body {
|
|
background: var(--bg-0);
|
|
color: var(--fg-1);
|
|
font-family: var(--font-sans);
|
|
overflow: hidden;
|
|
}
|
|
.shell {
|
|
height: 100vh;
|
|
display: flex; flex-direction: column;
|
|
position: relative;
|
|
}
|
|
.titlebar {
|
|
height: 36px; flex: 0 0 36px;
|
|
display: flex; align-items: center; gap: 14px;
|
|
padding: 0 14px;
|
|
background: #1b1e28;
|
|
border-bottom: 1px solid #373b46;
|
|
}
|
|
.dots { display: flex; gap: 7px; }
|
|
.dot { width: 11px; height: 11px; border-radius: 50%; }
|
|
.titlename {
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--fg-3);
|
|
letter-spacing: 0.05em;
|
|
flex: 1; text-align: center;
|
|
}
|
|
.pal-trigger {
|
|
display:flex; align-items:center; gap:6px;
|
|
background: #222531; border: 1px solid #414751;
|
|
color: var(--fg-2);
|
|
font-family: var(--font-sans); font-size: 11px;
|
|
padding: 4px 10px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
.pal-trigger:hover { background:#373b46; color: var(--fg-0); }
|
|
.body-row { flex: 1; display: flex; min-height: 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
|
|
<script type="text/babel" src="Icon.jsx"></script>
|
|
<script type="text/babel" src="Sidebar.jsx"></script>
|
|
<script type="text/babel" src="Editor.jsx"></script>
|
|
<script type="text/babel" src="Terminal.jsx"></script>
|
|
<script type="text/babel" src="StatusBar.jsx"></script>
|
|
<script type="text/babel" src="CommandPalette.jsx"></script>
|
|
|
|
<script type="text/babel">
|
|
const FILE_CONTENT = {
|
|
'src/aurora.ts': [
|
|
`<span style="color:#6e7882;">// Render the southern sky.</span>`,
|
|
`<span style="color:#9d78ff;">import</span> { <span style="color:#6388d8;">paint</span> } <span style="color:#9d78ff;">from</span> <span style="color:#16b866;">'./paint'</span>;`,
|
|
`<span style="color:#9d78ff;">import</span> <span style="color:#a9bcc3;">type</span> { <span style="color:#42dcd1;">Sky</span> } <span style="color:#9d78ff;">from</span> <span style="color:#16b866;">'./types'</span>;`,
|
|
``,
|
|
`<span style="color:#9d78ff;">export const</span> <span style="color:#ffe14e;">LATITUDE</span> = <span style="color:#42dcd1;">-77.85</span>;`,
|
|
`<span style="color:#9d78ff;">export const</span> <span style="color:#ffe14e;">CURTAINS</span> = <span style="color:#42dcd1;">3</span>;`,
|
|
``,
|
|
`<span style="color:#9d78ff;">export function</span> <span style="color:#6388d8;">render</span>(<span style="color:#a9bcc3;">name</span>: <span style="color:#42dcd1;">string</span>): <span style="color:#42dcd1;">Sky</span> {`,
|
|
` <span style="color:#9d78ff;">if</span> (!<span style="color:#a9bcc3;">name</span>) <span style="color:#9d78ff;">throw new</span> <span style="color:#6388d8;">Error</span>(<span style="color:#16b866;">'name required'</span>);`,
|
|
` <span style="color:#9d78ff;">return</span> <span style="color:#6388d8;">paint</span>(<span style="color:#a9bcc3;">name</span>, <span style="color:#ffe14e;">LATITUDE</span>, <span style="color:#ffe14e;">CURTAINS</span>);`,
|
|
`}`,
|
|
``,
|
|
`<span style="color:#6e7882;">// Watch the sky every 800ms.</span>`,
|
|
`<span style="color:#6388d8;">setInterval</span>(() <span style="color:#9d78ff;">=></span> <span style="color:#6388d8;">render</span>(<span style="color:#16b866;">'australis'</span>), <span style="color:#42dcd1;">800</span>);`,
|
|
],
|
|
'src/tokens.css': [
|
|
`<span style="color:#6e7882;">/* Australis tokens — abridged */</span>`,
|
|
`<span style="color:#00b1a8;">:root</span> {`,
|
|
` <span style="color:#a9bcc3;">--bg-0</span>: <span style="color:#42dcd1;">#222531</span>;`,
|
|
` <span style="color:#a9bcc3;">--bg-1</span>: <span style="color:#42dcd1;">#373b46</span>;`,
|
|
` <span style="color:#a9bcc3;">--fg-0</span>: <span style="color:#42dcd1;">#cce7ec</span>;`,
|
|
` <span style="color:#a9bcc3;">--fg-1</span>: <span style="color:#42dcd1;">#a9bcc3</span>;`,
|
|
` <span style="color:#a9bcc3;">--aus-blue</span>: <span style="color:#42dcd1;">#6388d8</span>;`,
|
|
` <span style="color:#a9bcc3;">--aus-cyan</span>: <span style="color:#42dcd1;">#00b1a8</span>;`,
|
|
` <span style="color:#a9bcc3;">--aus-green</span>: <span style="color:#42dcd1;">#16b866</span>;`,
|
|
`}`,
|
|
],
|
|
'README.md': [
|
|
`<span style="color:#9d78ff;"># australis</span>`,
|
|
``,
|
|
`<span style="color:#a9bcc3;">A cool, calm dark theme inspired by the southern lights.</span>`,
|
|
``,
|
|
`<span style="color:#9d78ff;">## install</span>`,
|
|
``,
|
|
`<span style="color:#16b866;"> npm i @australis/core</span>`,
|
|
``,
|
|
`<span style="color:#9d78ff;">## use</span>`,
|
|
``,
|
|
`<span style="color:#a9bcc3;">Import the stylesheet and you're done.</span>`,
|
|
],
|
|
};
|
|
|
|
const WORKSPACES = {
|
|
prod: {
|
|
name: 'Aurora prod',
|
|
tree: [
|
|
{ name: 'src', path: 'src', open: true, children: [
|
|
{ name: 'aurora.ts', path: 'src/aurora.ts', modified: true },
|
|
{ name: 'paint.ts', path: 'src/paint.ts' },
|
|
{ name: 'tokens.css', path: 'src/tokens.css' },
|
|
]},
|
|
{ name: 'public', path: 'public', children: [
|
|
{ name: 'logo.svg', path: 'public/logo.svg' },
|
|
]},
|
|
{ name: 'README.md', path: 'README.md' },
|
|
{ name: 'package.json', path: 'package.json' },
|
|
],
|
|
},
|
|
staging: {
|
|
name: 'Staging',
|
|
tree: [
|
|
{ name: 'src', path: 'src', open: true, children: [
|
|
{ name: 'aurora.ts', path: 'src/aurora.ts' },
|
|
{ name: 'tokens.css', path: 'src/tokens.css' },
|
|
]},
|
|
{ name: 'README.md', path: 'README.md' },
|
|
],
|
|
},
|
|
preview: {
|
|
name: 'Preview',
|
|
tree: [
|
|
{ name: 'src', path: 'src', children: [
|
|
{ name: 'aurora.ts', path: 'src/aurora.ts' },
|
|
]},
|
|
],
|
|
},
|
|
};
|
|
|
|
const WS_KEYS = Object.keys(WORKSPACES);
|
|
|
|
const INITIAL_TERM = [
|
|
`<span style="color:#42dcd1;">➜</span> <span style="color:#cce7ec;">australis</span> <span style="color:#9d78ff;">build</span> <span style="color:#a4c4ff;">--watch</span>`,
|
|
`<span style="color:#6e7882;"># compiling 24 modules…</span>`,
|
|
`<span style="color:#51e08a;">✓</span> tokens compiled <span style="color:#6e7882;">(184ms)</span>`,
|
|
`<span style="color:#51e08a;">✓</span> ui-kit bundled <span style="color:#6e7882;">(412ms)</span>`,
|
|
`<span style="color:#ffe14e;">!</span> 2 deprecated tokens — see migration.md`,
|
|
``,
|
|
];
|
|
|
|
function App() {
|
|
const [workspaceIdx, setWorkspaceIdx] = React.useState(0);
|
|
const workspace = WS_KEYS[workspaceIdx];
|
|
const [openFiles, setOpenFiles] = React.useState(['src/aurora.ts', 'src/tokens.css']);
|
|
const [activeFile, setActiveFile] = React.useState('src/aurora.ts');
|
|
const [termLines, setTermLines] = React.useState(INITIAL_TERM);
|
|
const [paletteOpen, setPaletteOpen] = React.useState(false);
|
|
|
|
const cycleWorkspace = () => setWorkspaceIdx(i => (i + 1) % WS_KEYS.length);
|
|
|
|
const openFile = (path) => {
|
|
if (!openFiles.includes(path)) setOpenFiles(f => [...f, path]);
|
|
setActiveFile(path);
|
|
};
|
|
const closeFile = (path) => {
|
|
const next = openFiles.filter(f => f !== path);
|
|
setOpenFiles(next);
|
|
if (activeFile === path) setActiveFile(next[0] || null);
|
|
};
|
|
|
|
const runCommand = (cmd) => {
|
|
const c = cmd.trim().toLowerCase();
|
|
const promptLine = `<span style="color:#42dcd1;">➜</span> <span style="color:#cce7ec;">${cmd}</span>`;
|
|
let out = [];
|
|
if (c === 'build' || c.startsWith('australis build')) {
|
|
out = [
|
|
`<span style="color:#6e7882;"># compiling…</span>`,
|
|
`<span style="color:#51e08a;">✓</span> built in <span style="color:#42dcd1;">312ms</span>`,
|
|
];
|
|
} else if (c === 'deploy') {
|
|
out = [
|
|
`<span style="color:#a4c4ff;">→</span> deploying to ${WORKSPACES[workspace].name.toLowerCase()}…`,
|
|
`<span style="color:#51e08a;">✓</span> live at <span style="color:#42dcd1;">https://australis.dev</span>`,
|
|
];
|
|
} else if (c === 'theme') {
|
|
out = [`<span style="color:#a4c4ff;">→</span> theme: <span style="color:#cce7ec;">Australis Dark</span> (active)`];
|
|
} else if (c === 'help') {
|
|
out = [
|
|
`<span style="color:#a9bcc3;">commands:</span>`,
|
|
` <span style="color:#9d78ff;">build</span> — compile the project`,
|
|
` <span style="color:#9d78ff;">deploy</span> — push to current workspace`,
|
|
` <span style="color:#9d78ff;">theme</span> — show active theme`,
|
|
` <span style="color:#9d78ff;">clear</span> — clear the terminal`,
|
|
];
|
|
} else if (c === 'clear') {
|
|
setTermLines([]); return;
|
|
} else {
|
|
out = [`<span style="color:#ff854f;">✗</span> unknown command: <span style="color:#cce7ec;">${cmd}</span> — try <span style="color:#9d78ff;">help</span>`];
|
|
}
|
|
setTermLines(prev => [...prev, promptLine, ...out, '']);
|
|
};
|
|
|
|
React.useEffect(() => {
|
|
const onKey = (e) => {
|
|
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
|
|
e.preventDefault();
|
|
setPaletteOpen(true);
|
|
}
|
|
};
|
|
window.addEventListener('keydown', onKey);
|
|
return () => window.removeEventListener('keydown', onKey);
|
|
}, []);
|
|
|
|
const handlePaletteRun = (item) => {
|
|
if (item.id === 'tokens') openFile('src/tokens.css');
|
|
else if (item.id === 'aurora') openFile('src/aurora.ts');
|
|
else runCommand(item.id);
|
|
};
|
|
|
|
const errors = activeFile === 'src/aurora.ts' ? 0 : 0;
|
|
const warnings = 2;
|
|
|
|
return (
|
|
<div className="shell">
|
|
<div className="titlebar">
|
|
<div className="dots">
|
|
<div className="dot" style={{ background:'#ff491a' }}/>
|
|
<div className="dot" style={{ background:'#e1c631' }}/>
|
|
<div className="dot" style={{ background:'#16b866' }}/>
|
|
</div>
|
|
<button className="pal-trigger" onClick={() => setPaletteOpen(true)}>
|
|
<Icon name="command" size={11}/> command palette
|
|
</button>
|
|
<div className="titlename">australis · {WORKSPACES[workspace].name.toLowerCase()}</div>
|
|
<div style={{ width: 80 }}/>
|
|
</div>
|
|
|
|
<div className="body-row">
|
|
<Sidebar
|
|
workspace={workspace}
|
|
workspaces={WORKSPACES}
|
|
onCycleWorkspace={cycleWorkspace}
|
|
openFile={openFile}
|
|
activeFile={activeFile}
|
|
onOpenFile={openFile}
|
|
/>
|
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
|
|
<Editor
|
|
openFiles={openFiles}
|
|
activeFile={activeFile}
|
|
onCloseFile={closeFile}
|
|
onSelectFile={setActiveFile}
|
|
fileContent={FILE_CONTENT}
|
|
/>
|
|
<Terminal lines={termLines} onCommand={runCommand}/>
|
|
</div>
|
|
</div>
|
|
|
|
<StatusBar
|
|
branch="main"
|
|
problems={{ errors, warnings }}
|
|
position="ln 8 · col 24"
|
|
syntax={activeFile && activeFile.endsWith('.ts') ? 'TypeScript'
|
|
: activeFile && activeFile.endsWith('.css') ? 'CSS'
|
|
: 'Markdown'}
|
|
/>
|
|
|
|
<CommandPalette
|
|
open={paletteOpen}
|
|
onClose={() => setPaletteOpen(false)}
|
|
onRun={handlePaletteRun}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
|
|
</script>
|
|
</body>
|
|
</html>
|