{"id":50494846,"url":"https://github.com/libraz/formulon-cell","last_synced_at":"2026-06-02T06:03:28.312Z","repository":{"id":357077314,"uuid":"1235256519","full_name":"libraz/formulon-cell","owner":"libraz","description":"Spreadsheet UI library for the formulon WASM calc engine — extension-based, i18n-aware, canvas-rendered grid","archived":false,"fork":false,"pushed_at":"2026-05-11T08:05:29.000Z","size":2254,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T09:12:07.840Z","etag":null,"topics":["canvas","datagrid","formula","formulon","headless-ui","i18n","react","spreadsheet","typescript","vue","wasm","workbook","xlsx"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/libraz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T06:42:12.000Z","updated_at":"2026-05-11T08:05:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/libraz/formulon-cell","commit_stats":null,"previous_names":["libraz/formulon-cell"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/libraz/formulon-cell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libraz%2Fformulon-cell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libraz%2Fformulon-cell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libraz%2Fformulon-cell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libraz%2Fformulon-cell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/libraz","download_url":"https://codeload.github.com/libraz/formulon-cell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/libraz%2Fformulon-cell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33808702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["canvas","datagrid","formula","formulon","headless-ui","i18n","react","spreadsheet","typescript","vue","wasm","workbook","xlsx"],"created_at":"2026-06-02T06:03:27.570Z","updated_at":"2026-06-02T06:03:28.300Z","avatar_url":"https://github.com/libraz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# formulon-cell\n\n[![CI](https://img.shields.io/github/actions/workflow/status/libraz/formulon-cell/ci.yml?branch=main\u0026label=CI)](https://github.com/libraz/formulon-cell/actions)\n[![codecov](https://codecov.io/gh/libraz/formulon-cell/branch/main/graph/badge.svg)](https://codecov.io/gh/libraz/formulon-cell)\n[![npm](https://img.shields.io/npm/v/@libraz/formulon-cell?label=%40libraz%2Fformulon-cell)](https://www.npmjs.com/package/@libraz/formulon-cell)\n[![npm — react](https://img.shields.io/npm/v/@libraz/formulon-cell-react?label=react)](https://www.npmjs.com/package/@libraz/formulon-cell-react)\n[![npm — vue](https://img.shields.io/npm/v/@libraz/formulon-cell-vue?label=vue)](https://www.npmjs.com/package/@libraz/formulon-cell-vue)\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/libraz/formulon-cell/blob/main/LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-6-blue?logo=typescript)](https://www.typescriptlang.org/)\n\nSpreadsheet UI library for the [formulon](https://github.com/libraz/formulon)\nWASM calc engine. Desktop-spreadsheet-style chrome, canvas-rendered grid,\nextension-based feature composition, runtime i18n.\n\n\u003e **β (beta).** `formulon-cell` is built primarily as a demonstration host\n\u003e for [**formulon**](https://github.com/libraz/formulon) — a headless,\n\u003e Excel-compatible calculation engine in C++17 that ships a single WASM /\n\u003e Python / CLI core. Engine docs live at\n\u003e [formulon.libraz.net](https://formulon.libraz.net). The UI surface is\n\u003e still evolving; pin a version range you can upgrade on purpose.\n\n## Packages\n\n| package | npm | what it is |\n|---------|-----|------------|\n| [`@libraz/formulon-cell`](./packages/formulon-cell)             | [![npm](https://img.shields.io/npm/v/@libraz/formulon-cell?label=)](https://www.npmjs.com/package/@libraz/formulon-cell)             | Vanilla TS / DOM core |\n| [`@libraz/formulon-cell-react`](./packages/formulon-cell-react) | [![npm](https://img.shields.io/npm/v/@libraz/formulon-cell-react?label=)](https://www.npmjs.com/package/@libraz/formulon-cell-react) | React 18+ component, hooks, and ribbon toolbar |\n| [`@libraz/formulon-cell-vue`](./packages/formulon-cell-vue)     | [![npm](https://img.shields.io/npm/v/@libraz/formulon-cell-vue?label=)](https://www.npmjs.com/package/@libraz/formulon-cell-vue)     | Vue 3 component, composables, and ribbon toolbar |\n\n## Install\n\n```sh\nnpm install @libraz/formulon-cell zustand\n# or yarn / pnpm\n```\n\n`zustand` is a peer dependency — exposed because consumers can read from\nthe same store the chrome subscribes to.\n\nThe WASM engine ships pthread-enabled and requires a\n[crossOriginIsolated context](https://developer.mozilla.org/docs/Web/API/crossOriginIsolated)\n(`Cross-Origin-Opener-Policy: same-origin` + `Cross-Origin-Embedder-Policy:\nrequire-corp`). Without it, `WorkbookHandle.createDefault()` rejects before\nmounting so a host configuration issue cannot masquerade as a working\nspreadsheet. The in-memory stub engine is opt-in via `preferStub: true` for\ntests and explicit demos.\n\n## Quick Start\n\n```ts\nimport { Spreadsheet, WorkbookHandle, presets } from '@libraz/formulon-cell';\nimport '@libraz/formulon-cell/styles.css';\n\nconst host = document.getElementById('sheet')!;\nconst wb = await WorkbookHandle.createDefault();\nconst sheet = await Spreadsheet.mount(host, {\n  workbook: wb,\n  features: presets.full(),\n  locale: 'en',\n});\n\nsheet.i18n.setLocale('ja');     // runtime locale swap\nsheet.setTheme('ink');           // dark mode\n```\n\n## Bundler integration\n\nformulon-cell re-uses `@libraz/formulon`'s pthread-enabled WASM module, so\nthe bundler hygiene rules from the engine package apply here too. Four\nthings matter:\n\n**1. Workers must ship as ES modules.** The recalc scheduler runs on Web\nWorkers spawned by Emscripten with\n`new Worker(new URL(...), { type: 'module' })`. Bundlers default to\nclassic (IIFE) workers and must be told otherwise:\n\n```ts\n// vite.config.ts\nexport default defineConfig({\n  worker: { format: 'es' },\n});\n```\n\nwebpack 5 picks up `{ type: 'module' }` automatically when\n`output.module: true`. esbuild needs `--format=esm` for the worker chunk.\n\n**2. Top-level await + dynamic node imports need an es2022 target.** The\nengine factory uses TLA and conditional `await import('node:...')`. Lift\nboth the main and worker target:\n\n```ts\n// vite.config.ts\nexport default defineConfig({\n  build: { target: 'es2022' },\n});\n```\n\n**3. Keep the engine out of dependency pre-bundling.** formulon-cell imports\n`@libraz/formulon`, whose Emscripten wrapper owns the worker/WASM asset\nresolution. Keep both packages out of dependency pre-bundling so those\nassets stay under the app bundler's control:\n\n```ts\n// vite.config.ts\nexport default defineConfig({\n  optimizeDeps: { exclude: ['@libraz/formulon-cell', '@libraz/formulon'] },\n});\n```\n\n**4. SharedArrayBuffer requires cross-origin isolation.** Serve your page\nwith `Cross-Origin-Opener-Policy: same-origin` + `Cross-Origin-Embedder-Policy:\nrequire-corp`. Without these headers, `SharedArrayBuffer` is undefined and\n`WorkbookHandle.createDefault()` rejects instead of silently falling back to\nthe in-memory **stub engine**. The stub is reserved for tests and explicit\ndemos via `preferStub: true`, because formula evaluation, recalc, and xlsx\nround-trip are intentionally incomplete there.\n\n```ts\nimport { WorkbookHandle, isUsingStub } from '@libraz/formulon-cell';\n\nconst wb = await WorkbookHandle.createDefault();\nif (isUsingStub()) {\n  console.warn('formulon-cell: explicit stub engine selected');\n}\n```\n\n## Features\n\n- **Desktop-spreadsheet-style** chrome out of the box (formula bar, status bar,\n  context menu, sheet tabs, View toolbar).\n- **Canvas-rendered** grid with theme tokens — `paper` (light) and `ink`\n  (dark) ship in the box; bring your own with the documented CSS variables.\n- **Extension-based** API: built-ins are controlled with feature flags, and\n  replaceable pieces (find/replace, format dialog, paste-special, hyperlink\n  dialog, hover comments, View toolbar, Quick Analysis, PivotTable creation,\n  …) are available as extension factories you can compose into the mount call.\n- **Runtime i18n** — swap locales without re-mounting; `ja` and `en` ship\n  by default, register more at runtime.\n- **Headless option** — keep just the canvas + store and provide your own\n  chrome.\n\n### Presets\n\n| preset | what's in it |\n|--------|--------------|\n| `presets.minimal()`  | formula bar, status bar, basic keymap |\n| `presets.standard()` | + View toolbar, Quick Analysis, session chart overlays, workbook object inspector, context menu, find/replace, clipboard, format painter, wheel scroll |\n| `presets.full()`     | + format dialog, paste-special, conditional formatting, iterative calculation settings, Go To Special, page setup, named ranges, hyperlink dialog, PivotTable creation, validation, autocomplete, hover comments, spreadsheet keymap |\n\n### i18n\n\n```ts\nimport { Spreadsheet } from '@libraz/formulon-cell';\n\nconst sheet = await Spreadsheet.mount(host, { locale: 'en' });\n\n// Swap locale at runtime — every label updates in place.\nsheet.i18n.setLocale('ja');\n\n// Override a few strings without forking the dictionary.\nsheet.i18n.extend('ja', { contextMenu: { copy: 'コピーする' } });\n\n// Register a brand new locale.\nimport fr from './fr.js';\nsheet.i18n.register('fr', fr);\nsheet.i18n.setLocale('fr');\n```\n\n## Demo apps\n\n| app | run | what it shows |\n|-----|-----|---------------|\n| `apps/playground`  | `yarn dev`        | Vanilla DOM playground (spreadsheet keymap) |\n| `apps/react-demo`  | `yarn dev:react`  | Same surface as `\u003cSpreadsheet\u003e` React component |\n| `apps/vue-demo`    | `yarn dev:vue`    | Same surface as `\u003cSpreadsheet\u003e` Vue component |\n\n## Framework Ribbon Toolbars\n\nThe React and Vue packages publish the demo ribbon as reusable framework\nchrome. Both implementations expose the same ribbon tab model and command\nsurface; import the matching toolbar CSS alongside the component.\n\n```tsx\nimport { SpreadsheetToolbar, type RibbonTab } from '@libraz/formulon-cell-react';\nimport '@libraz/formulon-cell-react/toolbar.css';\n```\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { type RibbonTab } from '@libraz/formulon-cell-vue';\nimport SpreadsheetToolbar from '@libraz/formulon-cell-vue/toolbar.vue';\nimport '@libraz/formulon-cell-vue/toolbar.css';\n\u003c/script\u003e\n```\n\n## Releasing\n\nSee [`docs/releasing.md`](./docs/releasing.md) for the manual tag-based\nrelease flow.\n\n## License\n\n[Apache-2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flibraz%2Fformulon-cell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flibraz%2Fformulon-cell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flibraz%2Fformulon-cell/lists"}