Code Editor
A code editor with no editor in it.
Walkthrough
1. Basic
It is a real <textarea> with a highlighted copy painted behind it, so Tab indents, Enter keeps your indentation, brackets close themselves and ⌘Z still walks back one edit at a time. Type in it.
<script lang="ts"> import { CodeEditor } from 'omaris'; let value = $state(`export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs));}`);</script><CodeEditor bind:value lang="ts" title="utils.ts" height={220} class="w-full" /> 2. Copy
copy puts a copy button in the header, or floats one when there is no header.
<script lang="ts"> import { CodeEditor } from 'omaris'; let withHeader = $state(`export function slugify(text) { return text .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, '');}`); let bare = $state(`const total = items.reduce((sum, item) => sum + item.price, 0);`);</script><div class="flex flex-col gap-5"> <CodeEditor bind:value={withHeader} lang="ts" title="slugify.ts" copy height={160} /> <!-- No title, so the button floats over the corner: quiet until the pointer arrives, and always there on a touch screen. Select a few lines first and it copies the selection instead of the file. --> <CodeEditor bind:value={bare} lang="ts" copy height={96} lineNumbers={false} /></div> 3. Vim
vim is the whole integration. Click in and you are in normal mode — the status bar says which one you are in and echoes the half-typed command. Counts, operators and motions (d2w, ci", >ip, gUiw, gcc), registers, marks, / search, ., <C-a>, and an ex line with :w, :%s/a/b/g and :set rnu. u undoes a whole command, not a keystroke.
<script lang="ts"> import { CodeEditor, Text, type VimMode } from 'omaris'; let mode = $state<VimMode>('normal'); let writes = $state(0); let value = $state(`export function total(list) { let sum = 0; for (const n of list) { sum += n; } return sum;}`);</script><div class="flex w-full flex-col gap-2"> <CodeEditor bind:value bind:vimMode={mode} lang="ts" vim title="total.ts" insertSpaces={false} height={240} onsave={() => writes++} /> <Text variant="label-sm" tone="muted"> mode {mode} · :w written {writes} {writes === 1 ? 'time' : 'times'} </Text></div> 4. Status and read only
status puts line, column and length along the bottom. readOnly keeps the colouring and the gutter but refuses edits, and startLine numbers a snippet from wherever it really begins.
<script lang="ts"> import { CodeEditor, Text } from 'omaris'; let json = $state(`{ "name": "omaris", "type": "module", "peerDependencies": { "svelte": "^5.0.0" }}`); const EXCERPT = `.dark { --surface: oklch(0.21 0.01 260); --surface-container: oklch(0.26 0.012 260);}`;</script><div class="flex w-full flex-col gap-4"> <CodeEditor bind:value={json} lang="json" title="package.json" status height="9rem" /> <div class="flex flex-col gap-1"> <Text variant="label-sm" tone="muted">read-only, numbered from 118</Text> <CodeEditor value={EXCERPT} lang="css" readOnly size="sm" startLine={118} highlightActiveLine={false} height="7rem" /> </div></div> 5. Find and diagnostics
⌘F finds and ⌘H replaces — every hit is drawn behind the text, so the count in the panel and the marks in the file agree. markers puts a rule under a span, colours its gutter number, and shows the message in the status bar while the caret is on that line. ⌃Space completes.
<script lang="ts"> import { CodeEditor } from 'omaris'; let value = $state(`const total = items.reduce((a, b) => a + b);let unused = 2;console.log(totl);`);</script><CodeEditor bind:value lang="ts" title="totals.ts" height={190} status completions={['items', 'total', 'reduce', 'console']} markers={[ { line: 2, column: 5, endColumn: 11, severity: 'warning', message: "'unused' is declared but never read" }, { line: 3, column: 13, endColumn: 17, severity: 'error', message: "Cannot find name 'totl'. Did you mean 'total'?" } ]} class="w-full"/> 6. Overridden
height lives in a custom property rather than a class, so height="auto" plus a min-h-* on classes.frame lets the editor grow with its content instead of scrolling. wrap stops the sideways scroll, and the gutter can go.
<script lang="ts"> import { CodeEditor } from 'omaris'; let value = $state( `# every line here is long enough to prove that wrapping is on rather than a horizontal scrollbar\nbun run check && bun run lint && bun run reference` );</script><CodeEditor bind:value lang="bash" wrap lineNumbers={false} tabSize={4} autoClose={false} height="auto" classes={{ root: 'rounded-shape-lg border-primary/40', frame: 'min-h-24', input: 'caret-primary' }} class="w-full"/>