Files
dotfiles/home_root/.claude/skills/australis-design/ui_kits/terminal/index.html
T
vh 7b16be02bf feat: fold in the portable Claude Code, zsh and git config; add a drift check
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.
2026-09-24 09:07:16 -07:00

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> {`,
`&nbsp;&nbsp;<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>);`,
`&nbsp;&nbsp;<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;">=&gt;</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> {`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--bg-0</span>: <span style="color:#42dcd1;">#222531</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--bg-1</span>: <span style="color:#42dcd1;">#373b46</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--fg-0</span>: <span style="color:#42dcd1;">#cce7ec</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--fg-1</span>: <span style="color:#42dcd1;">#a9bcc3</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--aus-blue</span>: <span style="color:#42dcd1;">#6388d8</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--aus-cyan</span>: <span style="color:#42dcd1;">#00b1a8</span>;`,
`&nbsp;&nbsp;<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>