# SymvaCAS Web / 웹 버전

SymvaCAS's existing Python/SymPy engine runs in the browser through CPython WebAssembly (Pyodide 314.0.7, Python 3.14.2, SymPy 1.14.0). The site consists entirely of static files; a calculation server, Android SDK, Node.js build, API key, and special cross-origin isolation headers are not required.

기존 Python/SymPy 계산 엔진을 브라우저의 WebAssembly 런타임에서 실행합니다. 계산 서버 없이 정적 웹 호스팅으로 배포할 수 있습니다.

Cartesian supports both **Function / y=f(x)** and **Implicit / F(x,y)=0**. Enter `x+1`, `y=x+1`, or `y^2+x^2=1`, one curve per line, to plot and analyze them together. Tap a point on a curve with several y branches to select the branch for a derivative, tangent, integral or arc length.

카테시안에서 **Function / y=f(x)**와 **Implicit / F(x,y)=0**을 모두 지원합니다. `x+1`, `y=x+1`, `y^2+x^2=1`을 한 줄에 하나씩 입력해 함께 그리고 분석할 수 있습니다. 여러 y 가지가 있는 곡선은 그래프의 점을 눌러 미분·접선·적분·호 길이에 사용할 가지를 선택하세요.

## Build and run / 빌드 및 실행

### GitHub Pages — open index.html / GitHub에서 바로 실행

The repository includes `.github/workflows/pages.yml`. In **Settings → Pages → Build and deployment → Source**, select **GitHub Actions** once. Push the changes to `main` (or run **Actions → Deploy SymvaCAS Web → Run workflow**). The workflow builds and publishes a complete static site, including the WASM runtime and engine archive. No Python server or build command is needed by visitors.

저장소의 **Settings → Pages → Source**를 **GitHub Actions**로 한 번 설정하세요. 이후 `main`에 업로드하면 자동으로 배포됩니다. 배포가 완료되면 아래 주소에서 `index.html`이 바로 실행됩니다.

- [SymvaCAS Web](https://kirinonakar.github.io/symvacas/)
- [index.html](https://kirinonakar.github.io/symvacas/index.html)

These are deployment addresses, not confirmation that the site has already been published. GitHub's repository file viewer shows HTML source; use the Pages URL to run the application. All files are relative to `index.html`, so project Pages paths such as `/symvacas/` are supported. For forks, replace the owner/repository in the URL.

### Local development / 로컬 개발

From the repository root, with Python 3.10 or newer:

```powershell
python web/build.py
python web/serve.py
```

Open [http://localhost:8080](http://localhost:8080). The first build needs an internet connection to download the pinned runtime and wheels. Subsequent builds reuse downloaded files. `--skip-download` updates the bundled engine, function catalog, help files, and cache manifest without downloading dependencies.

저장소 루트에서 위 명령을 실행한 뒤 브라우저로 접속하세요. 최초 빌드에만 런타임 다운로드가 필요하며, 계산은 브라우저 안에서 처리합니다. `index.html`을 더블 클릭하는 `file://` 방식은 Worker/모듈 로딩 제한 때문에 지원하지 않습니다.

Upload the **complete contents of `web/`**, including generated `vendor/`, `engine.zip`, and `assets.js`, to any static HTTP(S) host. Nested paths such as `/symvacas/` work. Serve `.wasm` as `application/wasm`, `.js`/`.mjs` as JavaScript, and preserve binary archives. Do not replace missing files with an HTML SPA fallback. GitHub Pages, nginx, or a standard static file server can serve the site.

## Features / 기능

- Scientific/CAS expressions use a JavaScript port of the Kotlin Pratt parser and the **same validated AST protocol and Python engine** as Android. Exact decimal literals, fractions, complex arithmetic, calculus, solving, matrices, vectors, units, statistics, distributions, transforms, and finance catalog functions remain available. No JavaScript `eval` or SymPy `parse_expr` is used for math input.
- Korean browsers default to Korean; other browsers default to English. Choose either language in Setup.
- Light, dark, and system themes, selectable in Setup. System mode responds to operating system changes. Preferences are saved locally.
- Android keypad layout: direction pad, six-column scientific/CAS rows, five-column numeric rows, SHIFT/ALPHA, and 2nd/1st page switching. Hold a key for 500 ms to use its SHIFT function directly; holding SHIFT itself toggles SHIFT as on Android.
- DEG/RAD/GRAD, internal precision from 3–200 digits, display digits from 2–200 (limited by internal precision).
- Math input supports plain-text/LaTeX paste. Missing trailing brackets close automatically on calculation; this can be disabled in Setup.
- Define custom functions directly in the calculator: enter `f(x)=x^3-8x+7` and press `=`, then calculate `f(2)` to get `-1`. Definitions also accept `:=` and multiple parameters, appear in Functions, and are saved in this browser.
- `diff(f(x),x)=g(x)` or `g(x)=diff(f(x),x)` saves a reusable derivative. `integrate(x,x)=f(x)` or `f(x)=integrate(x,x)` saves the integral `x^2/2 + C`; `f(2)` returns `2 + C`. Either calculus operation can also store its result in Ans. `Ans=g(x)` creates a function linked to the current answer, which is deleted when Ans changes or is cleared, including after `g(1)` or `Ans(1)`. Directly saved derivatives and integrals remain permanent.
- Scroll up to browse up to ten previous calculations; tap a formula to reuse it. The separate History dialog retains up to 500 calculations. Clearing History keeps starred entries.
- The result notation button cycles OFF → ENG → SCI → OFF, as on Android. OFF shows a dimmed ENG label; ENG uses powers of three and SCI uses powers of ten. The preference is saved and applies to current results and history without changing exact values.
- CALC prompts for input variables one at a time using the regular keypad or Keyboard entry. Saved formulas and symbolic STO results expose their underlying inputs: after `C=A+B`, enter `C` and press CALC to enter A and B, even if they already have numeric values. Formula assignments retain their source; self-updates such as `B=B+1` still store the computed value. Confirm each value with `=` or CALC; a blank value recalls its stored value or defaults to zero. Values must be numeric. AC cancels and restores the formula.
- Python editor with local `.py` open/save, `symvacas_catalog`, standard library/SymPy/mpmath, bounded output, and interactive `input()` dialogs. Optional pre-entered values (one per line) are consumed first.
- Statistics, matrices, and vectors use spreadsheet tables with row/column headers and arrow-key cell navigation. Statistics columns and independent sample selectors are named x/y/z; controls unused by the selected analysis are disabled. Group/value data select group names from the data.
- Matrix and vector workspaces include `add`, `subtract`, `multiply`, and `divide`. The grid accepts up to 9 × 9 matrices or 9 vector components.
- Statistics include grouped and categorical data, custom regression, distribution queries, and scatter/histogram/box plots.
- Probability has eight editable examples for coins, dice, cards, conditional probability, and selection, plus fifteen distributions, tail/interval/quantile queries, combinations, Bayes, and repeated trials. Gamma uses shape and scale, Beta uses two positive shapes, Log-normal uses the mean and standard deviation of ln(X), and Weibull uses shape and scale. Negative binomial counts failures before the r-th success; total trials equal X + r. Random selection supports all specified items, exactly k, at least k, at most k, and at least one, using the same hypergeometric model for drawing without replacement.
- History, variables, custom functions, datasets, drafts, and settings are saved in browser local storage. Setup can export/import a full backup. No account is required.
- Setup → Graph colors offers independent HSL sliders for all six curve colors, with individual and full-palette resets. Changes immediately update curves, formula markers, trace points and integral shading, and are retained in settings and backups.
- After the calculation engine is ready, the service worker caches the complete static application for offline use on HTTPS or localhost. The cache version changes when any shipped source or engine file changes. Calculation itself does not require the internet; the optional currency download does.

## Execution and scope / 실행 및 범위

The WASM interpreter runs in a dedicated **module** Web Worker, as required by Pyodide 314. Stop or the 20-second deadline terminates the Worker and starts a new interpreter, including for scripts such as `while True: pass`. The deadline pauses during interactive Python input, preserving the remaining execution time. Cancelling the input dialog stops the script. Interactive input uses [Pyodide run_sync](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.run_sync) and requires WebAssembly Promise Integration; pre-entered input values also work without it. Stored browser state survives this restart. No SharedArrayBuffer or COOP/COEP headers are needed. Service-worker upgrades replace old offline caches without reloading the running page or restarting its ready WASM/SymPy engine. The next page load fetches the latest application shell from the network, with the offline cache as a fallback.

웹은 MathML 수학 입출력과 텍스트 편집을 사용합니다. Android의 문서 제공자 권한과 공유 창은 웹 파일 선택·다운로드·클립보드로 대체합니다. Python `input()`은 미리 입력한 값을 먼저 사용하고, 부족하면 입력창에서 값을 받아 같은 실행을 이어갑니다. 입력 대기 시간은 20초 실행 제한에서 제외합니다. 대화형 입력은 WebAssembly Promise Integration을 지원하는 브라우저가 필요하며, 미지원 브라우저에서는 값을 미리 입력할 수 있습니다. 와이어프레임·표면·표면+격자 렌더링, 표면 색상, 격자 밀도(12×12~96×96), 회전·고도·확대, 자동·수동 z 범위를 조절하고 설정을 저장할 수 있습니다.

Only the standard library, SymPy, and mpmath are bundled; arbitrary native Python packages are not installed. A runtime's first load transfers approximately 18 MB of local static assets and can take longer on mobile devices. Browser storage and offline cache availability depend on the browser. Clearing site data removes locally saved work; use full backups to keep a copy.

Engine asset requests have a 30-second deadline covering headers and the complete response body, and retry once with the HTTP cache bypassed. Startup installs the bundled pure-Python mpmath and SymPy wheels directly, in dependency order, with the pinned integrity hashes, avoiding the runtime package manager's initialization locks. Download and installation errors fail startup explicitly. Engine startup has a two-minute deadline per attempt. A stalled or failed startup restarts the Worker automatically once without reloading the page; if both attempts fail, the status shows an error and Reload engine can start a fresh attempt. Drafts and saved data survive these retries.

## Validation / 검증

### JavaScript structure / 코드 구조

`app.js` composes the controllers, switches workspaces, and manages page lifecycle. Modules communicate through explicit callbacks; each controller owns its transient state.

- `app-state.js`: saved-state defaults, field restoration, and debounced persistence.
- `app-ui.js` / `app-dialogs.js`: shared UI helpers, dialogs, catalog, and settings.
- `engine-ui.js`: engine status, busy/Stop controls, and offline registration.
- `calculator.js` / `calculator-keypad.js`: expression editing, evaluation, previews, CALC, results/tape, and keypad modifiers.
- `workspaces.js`: workspace execution routing, shared formula previews, and simple tool forms.
- `matrix-workspace.js`, `statistics-workspace.js`, `probability-workspace.js`, `python-workspace.js`, `functions-workspace.js`, and `graph-workspace.js`: each workspace's controls and state.

### Run tests / 테스트 실행

```powershell
cd web
npm ci
npm test
```

Node.js 22 or newer is needed only for development tests. jsdom is a test-only dependency; the shipped application has no npm runtime dependencies. Tests compare the web parser to 114 actual Kotlin-exported AST fixtures, run the actual WASM engine, and exercise production Worker requests and DOM workflows including language/theme switches, math, graphs, Python input, variables/custom functions, and hard cancellation/recovery. They run without computer use or browser automation. These tests do not establish pixel-level browser rendering or cross-browser compatibility.

To refresh Kotlin fixtures after parser changes, run `./gradlew.bat :math:exportCases` and then `python web/build.py`. Follow the root README for Android/desktop engine validation.

Pyodide's [self-hosting documentation](https://pyodide.org/en/stable/usage/downloading-and-deploying.html) describes its static runtime distribution. See [THIRD_PARTY.md](THIRD_PARTY.md) for license notices.