npm package: @shnea/editor
Description: Reusable Tiptap-based editor and viewer package with configurable file-service upload integration.
Latest version: 0.1.6

﻿# @shnea/editor

`@shnea/editor` is a reusable Tiptap-based editor package for Shnea services.

It provides:
- `ShneaEditor`: a rich text editor wrapper for article, comment, sms, and no-toolbar presets
- `ShneaViewer`: a sanitized HTML viewer with click-to-zoom image preview
- `createFileServiceUploadAdapter`: a file-service integration helper for image and file uploads

`ShneaEditor` itself is not tied to `file-service`.
If your app has its own upload API, pass a custom `uploadAdapter`.
Use `createFileServiceUploadAdapter` only when you want to call Shnea file-service.

## Install

```bash
npm install @shnea/editor
```

## Usage

```tsx
import { ShneaEditor, createFileServiceUploadAdapter } from '@shnea/editor';
import '@shnea/editor/editor.css';

const uploadAdapter = createFileServiceUploadAdapter({
  baseUrl: import.meta.env.VITE_FILE_SERVICE_BASE_URL,
  bearerToken: accessToken,
  retentionCategory: 'editor',
});

<ShneaEditor
  mode="article"
  value={value}
  onChange={(html) => setValue(html)}
  uploadAdapter={uploadAdapter}
/>;
```

## Modes

- `article`: full toolbar preset
- `comment`: compact writing preset
- `sms`: byte-limited preset
- `no-toolbar`: editor preset without toolbar UI

## Fonts

- `Pretendard` 400/600 is loaded by default for editor font selection.
- Other bundled font options are loaded lazily when selected in the toolbar.

## Upload adapter choice

Use one of these two patterns:

- Use `createFileServiceUploadAdapter` when you want to upload to Shnea file-service.
- Use a custom `uploadAdapter` when you want to upload to your own file API.

`@shnea/editor` does not read env vars or manage tokens by itself.
The consuming app is responsible for preparing and passing upload configuration.

Editor upload behavior:

- Raster images are converted to compressed `webp` before upload.
- Large images are resized down to a max edge of `2560px` before encoding.
- `gif` and `svg` uploads are left unchanged.
- The editor shows an in-place loading overlay while uploads are running.

### 1. Use Shnea file-service

```tsx
import { ShneaEditor, createFileServiceUploadAdapter } from '@shnea/editor';

const uploadAdapter = createFileServiceUploadAdapter({
  baseUrl: import.meta.env.VITE_FILE_SERVICE_BASE_URL,
  bearerToken: accessToken,
  retentionCategory: 'editor',
});
```

### 2. Use your own upload API

```tsx
import { ShneaEditor } from '@shnea/editor';

const uploadAdapter = async (file: File) => {
  const formData = new FormData();
  formData.append('file', file);

  const response = await fetch('/api/uploads', {
    method: 'POST',
    body: formData,
  });

  if (!response.ok) {
    throw new Error('Upload failed');
  }

  const data = await response.json();

  return {
    url: data.previewUrl ?? data.url,
    name: data.name ?? file.name,
    fileId: data.fileId,
    previewUrl: data.previewUrl ?? data.url,
    downloadUrl: data.downloadUrl ?? data.url,
    mimeType: file.type,
    size: file.size,
  };
};
```

## File service integration

- Uploads use `POST /files/upload` with Bearer JWT.
- `createFileServiceUploadAdapter` sends `retentionCategory=editor` by default.
- Override `retentionCategory` per consumer app when a different retention policy is required.
- Reads use the returned `previewUrl` or `downloadUrl`.
- Store `fileId` as the canonical attachment reference in the consuming service.
- Handle `401`, `413`, and `507` as explicit upload failures.

Detailed integration docs:
- [shnea_editor integration guide](/E:/JetBrains/IntelliJ/shnea_editor/shnea_editor_연동_지침.md)
- [file-service integration guide](/E:/JetBrains/IntelliJ/shnea_editor/파일서비스_연동_지침.md)

## Scripts

- `npm run build`: build the package
- `npm run check`: TypeScript type-check
- `npm run test`: alias of `check`

## Environment

The package itself does not read env vars directly.
If you use `createFileServiceUploadAdapter`, the consuming app should provide:

- `FILE_SERVICE_BASE_URL`
- a valid upload Bearer token