# platon-code-editor-vue2

Vue 2 code editor component built on [Monaco Editor](https://microsoft.github.io/monaco-editor/) and [Livecodes](https://livecodes.io). Supports JavaScript, TypeScript, Vue 2 SFC, Vue 3 SFC, HTML, CSS, and PostgreSQL with optional live preview.

## Features

- **7 languages** — JavaScript, TypeScript, Vue 2, Vue 3, HTML, CSS, PostgreSQL
- **Monaco Editor** — VS Code-grade editing with IntelliSense, syntax highlighting, and type hints
- **Vue SFC editor** — Template / Script / Style tabs for Vue 2 and Vue 3 components
- **Live preview** — powered by Livecodes iframe sandbox
- **v-model** — two-way content binding
- **Zero webpack config** — Monaco workers loaded from CDN automatically

---

## Installation

```bash
npm install platon-code-editor-vue2
```

> **Peer dependency:** `vue ^2.6.14` must be installed in your project.

---

## Usage

### Global registration

```js
// main.js
import Vue from 'vue'
import CodeEditor from 'platon-code-editor-vue2'
import 'platon-code-editor-vue2/dist/platon-code-editor-vue2.css'

Vue.use(CodeEditor)
```

### Local registration

```js
import { CodeEditor } from 'platon-code-editor-vue2'
import 'platon-code-editor-vue2/dist/platon-code-editor-vue2.css'

export default {
  components: { CodeEditor },
}
```

### Basic example

```html
<template>
  <div style="height: 600px;">
    <CodeEditor
      v-model="code"
      lang="javascript"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      code: "console.log('Hello!')",
    }
  },
}
</script>
```

> **Required:** the parent element must have an explicit height. The editor fills 100% of its container.

---

## Examples

### Language selector

```html
<template>
  <div style="height: 100vh; display: flex; flex-direction: column;">
    <select v-model="lang">
      <option value="javascript">JavaScript</option>
      <option value="typescript">TypeScript</option>
      <option value="vue2">Vue 2</option>
      <option value="vue3">Vue 3</option>
      <option value="html">HTML</option>
      <option value="css">CSS</option>
      <option value="postgresql">PostgreSQL</option>
    </select>

    <div style="flex: 1;">
      <CodeEditor v-model="code" :lang="lang" />
    </div>
  </div>
</template>
```

### With preview

```html
<CodeEditor
  v-model="code"
  lang="vue2"
  :preview="true"
  :live="true"
/>
```

### Reading content programmatically

```html
<template>
  <div style="height: 500px;">
    <CodeEditor ref="editor" v-model="code" lang="javascript" />
    <button @click="save">Save</button>
  </div>
</template>

<script>
export default {
  methods: {
    async save() {
      const content = await this.$refs.editor.getContent()
      console.log(content)
    },
  },
}
</script>
```

### Setting content programmatically

```html
<template>
  <div style="height: 500px;">
    <CodeEditor ref="editor" v-model="code" lang="javascript" />
    <button @click="reset">Reset</button>
  </div>
</template>

<script>
export default {
  methods: {
    async reset() {
      await this.$refs.editor.setContent("console.log('reset!')")
    },
  },
}
</script>
```

---

## API

### Props

| Prop      | Type      | Default        | Description |
|-----------|-----------|----------------|-------------|
| `value`   | `String`  | `null`         | Editor content — use with `v-model` |
| `lang`    | `String`  | `'javascript'` | Active language. See [Supported Languages](#supported-languages) |
| `preview` | `Boolean` | `false`        | Show live preview panel |
| `live`    | `Boolean` | `false`        | Auto-refresh preview on every keystroke (requires `preview: true`) |

### Events

| Event   | Payload  | Description |
|---------|----------|-------------|
| `input` | `String` | Emitted when editor content changes. Used by `v-model` |

### Methods

Access via `ref`:

| Method | Signature | Description |
|--------|-----------|-------------|
| `getContent` | `() => Promise<string>` | Returns current editor content |
| `setContent` | `(content: string) => Promise<void>` | Replaces editor content |
| `run` | `() => Promise<void>` | Refreshes the preview panel (non-Vue languages) |

---

## Supported Languages

| `lang` value  | Editor | Preview |
|---------------|--------|---------|
| `javascript`  | Monaco | ✓ |
| `typescript`  | Monaco | ✓ |
| `vue2`        | Monaco (Template / Script / Style tabs) | ✓ |
| `vue3`        | Monaco (Template / Script / Style tabs) | ✓ |
| `html`        | Livecodes | ✓ |
| `css`         | Livecodes | ✓ |
| `postgresql`  | Livecodes | ✓ |

---

## Vue SFC Editor (vue2 / vue3)

When `lang` is `vue2` or `vue3`, the editor shows three separate Monaco tabs:

- **Template** — HTML template (syntax: `html`)
- **Script** — component logic (`javascript` for Vue 2, `typescript` for Vue 3)
- **Style** — component styles (`css`)

Content is assembled into a full SFC string for `v-model`:

```
<template>
  ...
</template>

<script>
  ...
</script>

<style scoped>
  ...
</style>
```

**Vue 2 IntelliSense** — the `/** @type {import('vue').ComponentOptions} */` JSDoc annotation is automatically injected into the Script tab so Monaco can provide type hints for `data`, `methods`, `computed`, etc. without any extra configuration.

**Vue 3 IntelliSense** — built-in TypeScript type definitions for `ref`, `computed`, `reactive`, `watch`, `onMounted`, `defineComponent`, and other Composition API functions.

---

## Preview Panel

When `:preview="true"` is set:

- A **▶ Run** button appears in the editor toolbar
- Clicking Run rebuilds the preview in an isolated iframe sandbox
- Setting `:live="true"` auto-reruns the preview 800 ms after each keystroke (the Run button is disabled in this mode)

---

## Monaco Workers

Monaco workers are loaded from CDN (`jsdelivr.net`) by default — no webpack plugin required.

To use local workers instead (e.g. for offline environments), configure `window.MonacoEnvironment` **before** importing this library:

```js
window.MonacoEnvironment = {
  getWorkerUrl(moduleId, label) {
    if (label === 'typescript' || label === 'javascript')
      return '/workers/ts.worker.js'
    if (label === 'css')
      return '/workers/css.worker.js'
    if (label === 'html')
      return '/workers/html.worker.js'
    return '/workers/editor.worker.js'
  },
}

import CodeEditor from 'platon-code-editor-vue2'
```

---

## iframe postMessage Bridge

When the editor is embedded inside an `<iframe>`, it communicates with the parent page via `postMessage`.

### Parent → Editor

Send a message to the iframe with `target: 'platon-editor'`:

```js
iframe.contentWindow.postMessage(
  { target: 'platon-editor', type: 'platon:set-lang', lang: 'typescript' },
  '*'
)

iframe.contentWindow.postMessage(
  { target: 'platon-editor', type: 'platon:set-content', content: 'const x = 1' },
  '*'
)

iframe.contentWindow.postMessage(
  { target: 'platon-editor', type: 'platon:get-content' },
  '*'
)
```

### Editor → Parent

Messages sent from the editor (source: `'platon-editor'`):

| `type` | Payload | Description |
|--------|---------|-------------|
| `platon:ready` | `{ lang }` | Editor is mounted and ready |
| `platon:content-change` | `{ lang, content }` | Content changed by user |
| `platon:content` | `{ lang, content }` | Response to `platon:get-content` |

---

## License

MIT
