BareGit
# Design Document: SillyTavern Character Card Translator

## 1. Overview
This document describes the design and implementation of a pure front-end web application intended to translate SillyTavern character cards (JSON format) from one language to another using OpenAI-compatible LLM APIs.

The application is designed to be highly portable, requiring no build process and capable of being served from any static file server.

## 2. Technical Stack

### 2.1 Preact.js (without Build Process)
We use [Preact](https://preactjs.com/) as the UI library for its small footprint and compatibility with React. To avoid a build process (like Webpack or Vite), we utilize [htm](https://github.com/developit/htm) (Hyperscript Tagged Markup).

- **Script Loading**: Preact and htm will be loaded via ESM (ECMAScript Modules) from a CDN (e.g., [esm.sh](https://esm.sh/)).
- **Syntax**: We will use `html` tagged templates instead of JSX.
    ```javascript
    import { h, render } from 'https://esm.sh/preact';
    import htm from 'https://esm.sh/htm';
    const html = htm.bind(h);
    ```

### 2.2 Styling
No external CSS frameworks like Bootstrap or Tailwind will be used, as per the workspace mandates.
- **CSS**: Custom Vanilla CSS will be used, written in a separate `style.css` file or embedded within the HTML.
- **Design Principles**: Focus on accessibility, clean typography, and responsive layouts.

### 2.3 Storage
- **localStorage**: Used to persist configuration options (API keys, endpoints, prompts, ignored keys).

## 3. Architecture and Data Structures

### 3.1 Intermediate Data Representation
To support "revert" and "retry" functionality, we cannot simply mutate the JSON strings in place. We must maintain a mapping between the original and translated content.

We will define a class (or a structure) `TranslationNode`:
```javascript
class TranslationNode
{
    constructor(original_value)
    {
        this.original_value = original_value;
        this.translated_value = null;
        this.status = 'pending'; // 'pending', 'translating', 'done', 'error'
    }
}
```

### 3.2 Application State
The global state will manage:
1.  **Configuration**: API credentials and preferences.
2.  **Source Data**: The original JSON object.
3.  **Process Tree**: A mirrored object tree where leaf strings are replaced by `TranslationNode` instances.

## 4. Translation Logic

### 4.1 Recursive Traversal and State Mirroring
The application will transform the input JSON into a reactive state tree. This process is crucial because it converts static strings into interactive objects.

**Algorithm: `createProcessTree(input_node, ignored_patterns)`**
1.  **Input**: A JavaScript value (Object, Array, String, Number, Boolean, or Null).
2.  **Logic**:
    - If `input_node` is an **Array**:
        - Create a new reactive array (e.g., a signal-wrapped array).
        - Iterate through each element and recursively call `createProcessTree` for each.
    - If `input_node` is an **Object**:
        - Create a new reactive object.
        - Iterate through each `key-value` pair.
        - Check if `key` matches any regex in `ignored_patterns`.
        - If it matches, recursively call `createProcessTree` for the value, but pass a flag to mark all downstream nodes as `ignored`.
        - Otherwise, recursively call `createProcessTree` for the value.
    - If `input_node` is a **String**:
        - If the `ignored` flag is set, return a `TranslationNode` with `status: 'ignored'`.
        - Otherwise, return a new `TranslationNode(input_node)`.
    - For all other types: Return the value as-is.

### 4.2 OpenAI API Integration Details
The `translateText` function must handle the specific schema of OpenAI-compatible APIs (like GPT-4 or local models like Ollama/vLLM).

**Request Structure**:
```json
{
    "model": "gpt-3.5-turbo",
    "messages": [
        {"role": "system", "content": "System prompt from settings..."},
        {"role": "user", "content": "Text to translate..."}
    ],
    "temperature": 0.3
}
```

**Response Handling**:
- Success: Extract `choices[0].message.content`.
- Error: Handle `401` (Unauthorized), `429` (Rate Limit), and `500` (Server Error). Update the `TranslationNode.status` to `'error'` and store the error message for display.

**Concurrency**: To avoid browser-level hang-ups or API rate limits, we use a task queue pattern. A `TaskQueue` class will manage pending translations, ensuring only `N` (e.g., 3) requests are active at once.

**TaskQueue Implementation Details**:
1.  **Queue Storage**: A private array `this._queue` stores objects containing the `text` and a `resolve/reject` pair.
2.  **Execution Loop**: A `process()` method that:
    - Checks if the number of active requests is less than the limit.
    - If so, shifts a task from the queue.
    - Increments the `active_count`.
    - Executes the `fetch` request.
    - Upon completion (success or failure), decrements `active_count` and calls `process()` again to trigger the next task.
3.  **Usage**: `const translated = await task_queue.add(() => translateText(node.original_value))`.

## 5. UI Components

### 5.1 Main Layout
A simple container with:
- **Header**: Title and "Options" button.
- **Input Area**: A file upload or text area for the raw JSON.
- **Control Bar**: "Start Translation", "Download Result" buttons.
- **Main View**: Toggleable between the raw input and the Fine-tuning Tree View.

### 5.2 Options Dialog
A modal containing:
- **API URL**: e.g., `https://api.openai.com/v1/chat/completions`.
- **API Key**: Password field for the key.
- **System Prompt**: Multi-line text area.
- **Ignored Keys**: A list of strings, each treated as a regex.
- **Save/Close**: Persists to `localStorage`.

### 5.3 Fine-tuning Tree View Implementation
The Tree View is a recursive component: `TreeNode({ key, value, depth })`.

- **Collapsible Folders**: If `value` is an Object or Array, render a toggle icon to collapse/expand children. Use a `depth` variable to apply CSS `padding-left`.
- **String Node Rendering**:
    - If `value` is a `TranslationNode`:
        - Render the `original_value` in a dimmed style.
        - Render a text area or div containing the `translated_value` (if present) or `original_value` (if pending).
        - **Action Buttons**:
            - `Translate`: Sets `status` to `'translating'`, calls `translateText`, then updates `translated_value` and sets `status` to `'done'`.
            - `Revert`: Sets `translated_value` to `null` and `status` to `'pending'`.
            - `Retry`: Same as `Translate`, but ignores the "already translated" check.

### 5.4 Global State Management (Signals)
Since we are using Preact, we will use [@preact/signals](https://preactjs.com/guide/v10/signals/) for state management. This allows fine-grained updates; clicking "Retry" on a single leaf will only re-render that specific `TreeNode` component, not the entire tree.

- `config`: A signal holding the settings object.
- `processTree`: A signal holding the root of the mirrored JSON tree.
- `isTranslating`: A boolean signal to disable global controls during batch processing.

## 6. Detailed Implementation Steps

### Step 1: Basic Scaffolding
1.  Create `index.html`.
2.  Import Preact and htm.
3.  Create a basic "Hello World" component to verify the environment.

### Step 2: Configuration Management
1.  Implement `Settings` service to read/write to `localStorage`.
2.  Build the `OptionsDialog` component.

### Step 3: JSON Processing Engine
1.  Write a function `preprocessJson(obj, path, ignored_patterns)` that returns a tree of `TranslationNode` objects.
2.  Implement the recursive logic for `ignored_keys`.

### Step 4: Translation Service
1.  Implement `translateText(text, settings)` which calls the LLM API.
2.  Handle error states and network timeouts.

### Step 5: Tree View Component
1.  Build a recursive `TreeNode` component.
2.  Connect the `Translate`, `Revert`, and `Retry` buttons to the state.

### Step 6: Export Logic
1.  Implement a function to "flatten" the `TranslationNode` tree back into a standard JSON string for download.

## 7. Coding Standards (Per GEMINI.md)

- **Indentation**: 4 spaces.
- **Braces**: Left brace on a new line.
- **Naming**:
    - Variables: `snake_case` (e.g., `api_key`).
    - Functions: `camelCase` (e.g., `handleFileUpload`).
    - Classes: `CapCase` (e.g., `TranslationManager`).
- **Logic**: Use `unique_ptr` logic for state (ensure single source of truth).

## 8. Testing Strategy
- **Unit Tests**: Test the regex filtering logic with various SillyTavern card samples.
- **Integration Tests**: Mock the API response to test the UI's handling of translation results and errors.
- **Manual Verification**: Upload a real character card and verify the tree view renders correctly.