TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A local-first, format-preserving CSV and spreadsheet editor.
Refrain Sheet is a local-first, offline CSV and spreadsheet editor that runs
directly from a local HTML file. It preserves CSV files as faithfully as
possible while supporting lightweight spreadsheet editing through RSF.
Open index.html directly in a browser; no installation, server, account,
or network connection is required.README 전문 보기 ↗A local-first, format-preserving CSV and spreadsheet editor.
Refrain Sheet is a local-first, offline CSV and spreadsheet editor that runs
directly from a local HTML file. It preserves CSV files as faithfully as
possible while supporting lightweight spreadsheet editing through RSF.
Open index.html directly in a browser; no installation, server, account,
or network connection is required.
“Refrain” means refraining from touching your original CSV unnecessarily.
Edit field values while preserving everything else as faithfully as possible:
delimiters, quoting, surrounding whitespace, line endings, encodings, byte
order marks, undecodable bytes, and malformed regions. Spreadsheet-only
features (formulas, structural edits, per-document metadata) are provided
through RSF (Refrain Sheet Format). The exact byte-preservation
guarantee and its documented exceptions are in
knowledge/domains/csv-preservation-guarantee.md.
ローカルで動く、書式保持CSV・スプレッドシートエディタです。 CSVのフィールド値だけを編集し、区切り文字、引用符、空白、改行コード、文字コード、 BOM、不正なCSV領域などを可能な限り保持します。
CHANGELOG.md tracks notable user-visible changes by version.
The version is defined in package.json and surfaced through the app
(Help > About / Keyboard Shortcuts shows it), the metadata written into
saved .rsf files, and the release artifact name.
This README is an entry point, not a reference manual. Durable, detailed
knowledge — architecture, security, performance, the RSF file format,
core spreadsheet/CSV domain behavior, and UI/interaction behavior — lives
in the knowledge/
OKF v0.2
bundle:
.rsf file
specification (Zstandard-compressed JSON).eval/dynamic code, a sandboxed formula engine, and an embedded
WebAssembly core that is never fetched.Values beginning with =, +, -, or @ may be interpreted as
formulas by spreadsheet software (Excel, LibreOffice, Google Sheets)
when the saved CSV is opened there. In keeping with the Refrain
principle, this application never silently modifies, escapes, or
prefixes your values as a mitigation — be careful when opening CSV files
from untrusted sources in spreadsheet software.
file://The build output is completely static and self-contained:
dist/ (build it yourself or download a release ZIP).dist/index.html (or index.html inside the extracted ZIP)
directly in a browser — double-click it or press Ctrl+O in the browser.There is no dev server requirement, no backend, no browser extension, no
CDN, and no network access of any kind. The bundle is a classic (non-module)
script specifically so it works under file:// in Chromium, and a
restrictive Content Security Policy (connect-src 'none', no http:/https:
source anywhere) blocks all outgoing connections.
| Capability | Chrome / Edge (Chromium) | Firefox | Safari |
|---|---|---|---|
Run from file:// | ✔ | ✔ | ✔ |
| Overwrite save (File System Access API) | ✔ (with permission prompt) | ✘ → download save | ✘ → download save |
| Writable handle from drag & drop | ✔ | ✘ | ✘ |
Requirements: Node.js ≥ 20 and npm, or Docker.
npm ci --ignore-scripts # install exact locked deps, no lifecycle scripts
npm run dev # Vite dev server (development only; the product itself needs no server)
npm run build # type-check + production build into dist/
npm run test # vitest (unit, property-based/fuzz, jsdom UI tests)
npm run test:rust # cargo test (Rust/WASM core)
npm run bench # performance benchmarks — see knowledge/operations/performance-measurements.md
npm run lint # eslint
npm run format # prettier --write
npm run format:check # prettier --check
npm run check:versions # package.json ⇄ package-lock.json consistency
npm run check:dist # assert dist/ is self-contained (embedded WASM, no network)
npm run check:knowledge # OKF frontmatter validation for knowledge/
npm run ui:check # load dist/ in headless Chromium, fail on render/console errors
npm run audit:ci # production-dependency vulnerability gate
npm run release -- patch # one-command release (see "Cutting a release")
The committed .npmrc sets ignore-scripts=true, so npm ci alone already
skips dependency lifecycle scripts; the explicit flag above documents the
intent for environments without the repo .npmrc.
npm run ui:check needs Chromium's browser binary, which --ignore-scripts
deliberately does not fetch automatically; the Docker image (below) already
has it, but a local (non-Docker) run needs a one-time
npx playwright install --with-deps chromium first.
A reproducible environment is provided via Dockerfile + compose.yaml
(dependencies live in a named volume, never in the host tree):
docker compose run --rm app npm ci --ignore-scripts
docker compose run --rm app npm run format:check
docker compose run --rm app npm run lint
docker compose run --rm app npm run test
docker compose run --rm app npm run build # writes dist/ to the host
docker compose run --rm app npm run ui:check # headless Chromium is preinstalled in the image
docker compose up dev # dev server on http://localhost:5173
The code is organized in strict layers — core/domain (src/core/, fully
DOM-independent), application (src/app/), UI (src/ui/), and
infrastructure (the WASM bridge, generated bindings, and build scripts) —
with dependencies flowing inward only, and every user command dispatching
through a single typed command layer. The full engineering map (layer
diagram, command/data flow, the WASM boundary, and the invariants every
change must preserve) is documented in
knowledge/architecture/index.md.
src/
core/ lossless CSV document model, RSF workbook/worksheet model +
file codec, the formula engine — DOM-independent, unit-tested
app/ state/ (tabs & app state), commands/ (the typed command layer)
and its UI contract (ui-port.ts), file access, settings, i18n
ui/ menu bar, grid/, formula bar, dialogs/, status bar
styles/ hand-written CSS, one file per UI module, loaded in order by
styles/index.css
locales/ en.json, ja.json (identical key sets)
generated/ generated: embedded WASM (Base64) + wasm-bindgen glue
wasm/ Rust crate compiled to WebAssembly (CSV core, compression,
stats/search primitives); toolchain pinned by rust-toolchain.toml
tests/ core/, app/, ui/ (mirroring src/), tooling/ (scripts, workflows,
repository rules), site/; fixtures/rsf/ is the frozen .rsf
compatibility corpus
bench/ reproducible performance benchmarks (npm run bench)
scripts/ build/, check/, release/, ui-check/ and shared lib/ (npm run …)
site/ the separate static marketing site, plain JS (npm run build:landing)
knowledge/ the OKF knowledge bundle (architecture, operations, formats,
agent-loop, domains, ui, references, decisions)
docs/ machine-checked ledgers (EOL register, Knip baseline) and
proposals/ (proposal and analysis records)
design-system/ Refrain Sheet Design System (v2/ is current); the single
source of brand artwork and design tokens for the app and the
landing site
Menu actions, keyboard shortcuts, context menus, and drag & drop all pass
through the single command layer in src/app/commands/index.ts.
Measured with cloc over the whole
repository, excluding node_modules, dist, build, coverage, and
vendor (only files git tracks are counted). The table between the two
markers below is regenerated by
scripts/release/code-stats.mjs as part of every release
commit (npm run release, including the Manual release recovery workflow),
and can be refreshed by hand with the
Release docs workflow, which opens a pull
request. Nothing outside the markers is ever machine-written.
| Language | Files | Blank | Comment | Code |
|---|---|---|---|---|
| TypeScript | 536 | 8,699 | 18,382 | 95,745 |
| JSON | 21 | 0 | 0 | 13,087 |
| Markdown | 93 | 2,401 | 4 | 12,943 |
| CSS | 53 | 1,236 | 1,579 | 11,150 |
| JavaScript | 31 | 406 | 1,140 | 4,764 |
| HTML | 20 | 74 | 76 | 2,438 |
| YAML | 14 | 204 | 440 | 1,313 |
| Rust | 4 | 112 | 180 | 883 |
| SVG | 11 | 0 | 52 | 98 |
| Bourne Shell | 1 | 11 | 19 | 47 |
| Dockerfile | 1 | 8 | 26 | 40 |
| TOML | 2 | 4 | 26 | 24 |
| INI | 1 | 3 | 0 | 13 |
| Total | 788 | 13,158 |
npm run test runs the full unit, property-based/fuzz, and jsdom UI test
suite under tests/: byte-identical CSV preservation, the formula engine,
the RSF codec (round-trip, WASM/JS parity), UI behavior (keyboard
routing, IME safety, selection, drag interactions), and deterministic
responsiveness-regression tests. See the individual files under tests/
for what each suite covers.
.github/workflows/ci.yml) runs on pull requests and pushes to
main: install, version-consistency, format, lint, tests, build,
check:dist, a dependency-vulnerability gate, and a clean-tree
assertion. On pull requests it also runs the changelog gate
(check:changelog), which fails a PR that changes src/ or
wasm/src/ without an entry in CHANGELOG.md (or
Changelog: not-needed in the PR description for a purely internal
change). CI is entirely read-only and needs no secrets, so fork PRs run
safely.main respectively..github/workflows/release.yml) run only
when a strict semantic-version tag v<major>.<minor>.<patch> is
pushed, publishing both a GitHub Release (ZIP + SHA-256 + CycloneDX SBOM
main, pull requests, and malformed or non-version tags never
release or deploy.The full CI permission model, Actions-pinning policy, and release security controls are documented in knowledge/operations/security-supply-chain.md.
The application version follows Semantic Versioning and is defined in
exactly one place, package.json; src/core/app-identity.ts imports it, so no
app-visible string hard-codes a number. npm run check:versions fails if
package.json and package-lock.json drift apart, and CI runs it on every
push. The internal Rust/WASM core crate (wasm/Cargo.toml) has its own
independent version and is intentionally not tied to the app version.
CHANGELOG.md records what changed in each version. A pull
request that changes user-visible behavior adds an entry under its
Unreleased section; cutting a release retitles that section to the new
version and date. The file holds only the current minor series; older series
are archived, unchanged, in docs/changelog/
(npm run changelog:archive when a new minor series starts).
npm run release)A single checked-in Node script, scripts/release/index.mjs,
performs the whole release safely and requires no globally installed tools
beyond Node + git:
npm run release -- patch # 0.2.4 -> 0.2.5
npm run release -- minor # 0.2.4 -> 0.3.0
npm run release -- major # 0.2.4 -> 1.0.0
npm run release -- v1.4.0 # an explicit target version
npm run release -- patch --yes # skip the confirmation prompt (CI/non-interactive)
npm run release -- patch --dry-run # run every check, change nothing
It validates the repository state and branch (main only), runs the
full required checks (including Rust tests and a security audit),
computes the new version, synchronizes package.json +
package-lock.json, then creates and pushes the release commit and an
annotated tag — which is what triggers the GitHub Actions release
workflow. It prints the exact plan and requires typing yes before any
mutation (unless --yes is passed), refuses to run from a detached HEAD,
a dirty tree, or a branch behind its upstream, and never force-pushes or
overwrites tags.
Once CI and the production build succeed, the release workflow's
deploy-pages job publishes the same dist/ to GitHub Pages at
https://<owner>.github.io/<repository>/, using the official
actions/configure-pages/upload-pages-artifact/deploy-pages actions
and no repository secrets. One-time repository setting: in
Settings → Pages → Build and deployment → Source, choose GitHub
Actions — without it the deployment step has no Pages site to publish
to.
All input — CSV content, filenames, search terms, regular expressions,
localStorage data — is treated as untrusted; cell content is never
interpreted as HTML or executable code (no innerHTML/eval/
new Function); .rsf files hold inert data only, validated and bounded
on load; the Rust/WebAssembly core is embedded and never fetched from a
network; and the application makes no network connections at runtime
(this covers the CSV editor itself — the separate marketing landing page
may load consent-gated analytics). The full threat model, dependency
policy, lockfile policy, CI permission model, and release security
controls are documented in
knowledge/operations/index.md.
.rsf spreadsheet
document (see Features).MIT License, Copyright (c) 2026 0x0da160 — see LICENSE. Bundled third-party software is documented in THIRD-PARTY-NOTICES.md.
| 142,545 |