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.
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user