
# Getting started

This page mounts a workbench, mounts a standalone editor, covers the React and Vue wrappers, adds
one extension, and renders either on a server. Read [Introduction](/guide) first if you haven't —
it covers installing codelet and what each package entry gives you.

## Mount a workbench

A workbench is a file tree, tabs, a command palette and a panel, backed by an in-memory
`FileSystem`.

```ts
import { FileSystem, Workbench } from "codelet/workbench";

const workbench = new Workbench({
  parent: document.getElementById("app")!,
  fs: new FileSystem({ "/README.md": "# Hello\n\nEdit me." }),
});
```

::codelet-playground{mode="workbench" active="README.md" height="360"}

```md [README.md]
# Hello

Edit me.
```

::

:read-more{to="/guide/workbench"}

## Mount an editor

`Editor` is a single code field. Construct one with the element to mount into; everything else
is optional.

```ts
import { Editor } from "codelet";

const editor = new Editor({
  parent: document.getElementById("editor")!,
  doc: "console.log('hello')\n",
  lang: "ts",
});
```

::codelet-playground{height="200"}

```ts [hello.ts]
console.log("hello");
```

::

`editor.value` reads the current document. Call `editor.destroy()` when the element is removed,
to tear the view down.

:read-more{to="/guide/workbench#the-standalone-editor"}

## React and Vue

::tabs
::tab{label="React"}

```tsx
import { CodeEditor } from "codelet/react";

function Example() {
  return (
    <CodeEditor
      initialValue={"console.log('hello')\n"}
      lang="ts"
      onChange={(value) => console.log(value)}
      style={{ height: 300 }}
    />
  );
}
```

`initialValue` mounts uncontrolled: codelet owns the document from then on, and edits are
reported through `onChange`. Pass `value` instead for a controlled editor, kept in sync with
state on every render. `onCreate` hands you the underlying `Editor` once it exists, and
`className`/`style` reach the wrapping `<div>` (`style` is merged over the wrapper's own
positioning).
::

::tab{label="Vue"}

```vue
<script setup lang="ts">
import { CodeEditor } from "codelet/vue";
</script>

<template>
  <CodeEditor
    initial-value="console.log('hello')\n"
    lang="ts"
    style="height: 300px"
    @change="(value) => console.log(value)"
  />
</template>
```

The same `initialValue`/`value` split applies: `initialValue` for an uncontrolled editor,
`value` (or `v-model:value`) for one kept in sync with state. `class` and `style` fall through
to the wrapping element automatically. In place of `onChange` and `onCreate` props, the
component emits `change` (the value and the CodeMirror update) and `create` (the `Editor`
instance).
::
::

Both wrappers also accept `lang`, `theme` and `readOnly`, and update the live editor whenever
those props change.

:read-more{to="/guide/workbench#the-standalone-editor"}

## Adding an extension

A workbench with no `extensions` has no Search icon, no terminal, nothing beyond the built-in
explorer, tabs and command palette. Pass extensions to add them:

```ts
import { search } from "codelet/extensions/search";
import { FileSystem, Workbench } from "codelet/workbench";

const workbench = new Workbench({
  parent: document.getElementById("app")!,
  fs: new FileSystem({ "/README.md": "# Hello" }),
  extensions: [search],
});
```

That adds a Search icon to the activity bar, backed by `codelet/extensions/search`. Every
built-in extension is its own package entry, so a workbench only pays for the ones it imports.

:read-more{to="/guide/extensions"}
:read-more{to="/extensions"}

## Server rendering

`renderEditorHTML` (from `codelet`) and `renderWorkbench` (from `codelet/workbench/server`)
return static markup — a stand-in editor or a full shell — rendered without CodeMirror ever
running. The client mounts `Editor` or `Workbench` over the same element and takes over from
there:

::code-group

```ts [server.ts]
import { FileSystem } from "codelet/workbench";
import { renderWorkbench } from "codelet/workbench/server";

export default {
  fetch() {
    const shell = renderWorkbench({
      fs: new FileSystem({ "/README.md": "# Hello\n\nEdit me." }),
      open: "/README.md",
    });
    return new Response(
      `<!doctype html><div id="app">${shell}</div><script type="module" src="/client.js"></script>`,
      { headers: { "content-type": "text/html;charset=utf-8" } },
    );
  },
};
```

```ts [client.ts]
import { FileSystem, Workbench } from "codelet/workbench";

new Workbench({
  parent: document.getElementById("app")!,
  fs: new FileSystem({ "/README.md": "# Hello\n\nEdit me." }),
  open: "/README.md",
});
```

::

`Workbench` hydrates automatically when `parent` already has children, and mounts fresh
otherwise, so the same client code works for a server-rendered page or a client-only one.

::warning
`open`, `active`, `view`, `panel`, `secondary`, `theme`, `extensions` and `themes` are **markup**.
Pass the exact same values to `renderWorkbench()` on the server and to `Workbench` on the
client, or hydration has nothing to match and the shell rebuilds instead of taking over.
::

`renderEditorHTML` is the same idea for a standalone editor:

```ts
import { renderEditorHTML } from "codelet";

const html = renderEditorHTML({ doc: "console.log('hello')\n", lang: "ts" });
```

:read-more{to="/guide/workbench"}
:read-more{to="/guide/workbench#the-standalone-editor"}

## No CSS to import

codelet ships no CSS file. Every rule is inline, so mounting the editor or the workbench is one
import, with nothing to add to a stylesheet or a build pipeline.

:read-more{to="/guide/files"}
:read-more{to="/guide/themes"}
:read-more{to="/guide/server"}
