TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A notebook-style markdown scratchpad. Entries behave like .ipynb cells, minus execution:
type markdown, and the moment the text box loses focus it is replaced by the rendered
output with an Edit button that brings the text box back.
Everything lives in the browser's localStorage — no accounts, no sign-in, no sync. The one
exception is Send/Receive, an opt-in hand-off that moves an entry to another machine through a
short-lived key in Upstash Redis; it is the only part of the app that needs environment variables
and a serverless function, and the app runs fine with it unconfigured.README 전문 보기 ↗A notebook-style markdown scratchpad. Entries behave like .ipynb cells, minus execution:
type markdown, and the moment the text box loses focus it is replaced by the rendered
output with an Edit button that brings the text box back.
Everything lives in the browser's localStorage — no accounts, no sign-in, no sync. The
Send/Receive hand-off between machines is switched off for now: its buttons, menu item and
s/g shortcuts are gone from the UI, while the /api/transfer routes and their client remain in
the codebase, unused, so the feature can be wired back in.
Blur to render. Leaving a text box renders it; Esc or Ctrl+Enter commits without clicking away.
+ Paste. Creates an entry, drops the clipboard into it, and focuses the text box. Rich
clipboard content (Teams, Confluence, a web page) is converted to markdown; plain text is left
alone. If the browser blocks clipboard reads, the entry is still created and focused so Ctrl+V
works — and pasting into an open entry converts rich content the same way.
Breadcrumbs paste as one line. A link trail copied from a web app — Azure DevOps'
/ Boards / Sprints, say — arrives as block elements that Turndown splits across a dozen lines,
with the link text stranded inside [ ]. Those are collapsed back into
/ [Boards](url) / [Sprints](url) on a single line. The rule is deliberately narrow: it needs at
least one link, short fragments, no block markdown and no sentence punctuation, so pasted prose
and documents keep their paragraphs.
Azure DevOps file links. A file copied from a repo arrives as
[StorageAccount.cs](url)Org > DataDownloader.SFTP — the link first, the location after it, led
by the project name. It is rewritten to read as a path: DataDownloader.SFTP > [StorageAccount.cs](url), with the account and project segments dropped. Only applies to a single
dev.azure.com or *.visualstudio.com link followed by a > trail.
Azure DevOps work-item lists. Work items copied from a board or query arrive as the link with
its state run on after it: [1898 Onboard …](…/_workitems/edit/1898)Resolved. Each line is
rewritten to lead with the state, lowercased: `resolved` [1898 Onboard …](url). A state on
the line after its link is picked up too. Applies to rich and plain-text paste, and only when every
line of the paste is a work-item link or its state.
Azure DevOps URLs pasted bare. A dev.azure.com URL pasted on its own — from the address
bar, where there is no rich clipboard flavor to convert — becomes a link labelled with what it
points at, whether pasted into an open entry or through + Paste. Edge's "friendly link" copy,
which carries the URL only in its HTML flavor under the page title, is read the same way. These
shapes are recognized, and the pull-request shape is checked first:
| Pasted URL | Becomes |
|---|---|
…/_git/{repo}/pullrequest/{n} | [{repo} PR !{n}](url) |
…/_git/{repo}?version=GB{branch}&path=/{file} | [{branch} / {file}](url) |
…/_git/{repo}?path=/{file} | [{file}](url) |
…/_wiki/wikis/{wiki}/{id}/{page} | [{page}](url), hyphens read as spaces |
…/_workitems/edit/{id} (any query) | [Devops {id}](url) |
…/_workitems/edit/{id}#{commentId} | [Devops {id} / Comment {commentId}](url) |
Branch names and file paths are URL-decoded, so GBfeature%2F1403 reads as feature/1403, and
version and path are read by name rather than position. As narrow as the breadcrumb rule:
the paste has to be nothing but the URL, and anything else — a GT tag, a GC commit, a build, a
bare project URL, a URL sitting inside a sentence — is left exactly as pasted.
Duplicate detection. Pasting a string that already exists verbatim jumps to that entry and says so instead of creating a twin; the toast offers Add anyway.
Images. Paste or drop a screenshot anywhere to store it as an image entry: a thumbnail that
opens full size on click, with its dimensions, size and paste time on the right of the header.
It can be copied back to the
clipboard as PNG, deleted, moved up and down, and marked up two ways (below); the annotator opens
on the box tool. Thumb / Fit W / 1:1 choose how it shows: a small thumbnail (the
default), the width of the entry, or its original pixels, scrolling
sideways when wider than the entry. The choice is saved with the image but is not an undo step.
In compact mode the Excalidraw button reads Xcd ↗. Images are re-encoded as PNG and
downscaled until they fit a per-image budget, because the whole notebook shares a few megabytes of
localStorage. A markdown export writes a dated caption rather than a megabyte of base64 — use
the .json backup to preserve the pixels.
Copy and export feedback. The button whose copy or export ran most recently turns green, and reverts when another one is used, so it doubles as a record of what you last copied. Every +📋 used since the last copy stays green, since together they are what the clipboard holds.
Entry toolbars lead with Edit, Download and Copy on text, table and image entries.
Enter continues lists. - alpha + Enter gives - , * [x] done gives * [ ] , 3. third
gives 4. , and Enter on an empty marker clears it.
Paste a URL over a selection to wrap it: select runbook, paste, get [runbook](url).
Split and merge. Ctrl+Shift+- splits an entry at the caret; Merge ↓ (or Shift+M) joins
an entry with the one below.
Raw view. r or the Raw button shows an entry's markdown source without opening the
editor, so you can read or copy it without risking an edit.
Live task checkboxes. Ticking a rendered * [ ] writes [x] back into the markdown source.
☑️ (Checkbox). Prefixes every non-empty line of an entry with * [ ] ; lines that are already
tasks are left alone, and existing bullets (- , 1. ) are converted rather than doubled. Click
again (or press t) to step on through 1. numbered, - bullets, `code` and plain text,
then back to tasks. Checkbox All only adds tasks, to every entry.
Timestamps on every entry, and long entries are clamped with Show more so one big paste does not bury the rest.
Templates live in their own dropdown, the 📑 button: 👥 meeting summary (dated, with Decisions and Todos sections), 🗓️ daily scratch, ▦ 2×2 and 3×3 table skeletons, 💻 code snippet, 🔗 link list. They are in the command palette too, with the same icons.
Tabular paste becomes a table. Markdown tables render on their own; what does not is text that
only looks tabular. A tab-separated grid (Excel, a query result) is converted to a markdown table
on paste, and pipe rows written without the | --- | delimiter row GFM requires have it inserted.
CSV paste becomes a table too. Comma-separated text as an export writes it — fields in
optional double quotes, "" for a quote, commas and line breaks allowed inside quotes — is
converted with the first row as the header. Empty fields stay empty cells. To keep prose with
commas as prose, it needs at least two rows of exactly equal width and either a quoted field or
three or more columns.
JSON paste becomes a table too. A JSON array of objects — az … -o json, SQL Server's
FOR JSON PATH, a REST response — is converted with the keys as the header, in the order they
first appear. An Azure DevOps { "count": n, "value": [ … ] } envelope is unwrapped, nulls become
empty cells, and nested objects are written as compact JSON. A single object or an array of
scalars is left as pasted.
Rich copy. Rich on an entry (or Shift+C) copies it formatted for Outlook, Teams, Word and
Wrike, where markdown otherwise arrives as raw syntax; Copy All as rich text in the ⋯ menu
does the same for every entry, or the filtered ones. The HTML carries inline styles, since mail
clients drop stylesheets, task boxes become ☐ / ☑, and images are included with their annotations
painted in. The plain-text flavor is the markdown, so a plain-text target still gets that. Syntax
colours are left out: they come from class names that mean nothing once pasted.
TSV for Excel. A table entry gets a TSV button that copies it tab-separated, which Excel
splits into cells on paste. Escaped pipes come back as |.
Table tools. An entry that is a markdown table and nothing else gets two controls in each
header cell: ↕ sorts by that column (click again for descending) and ✕ deletes the column. Numbers,
currency, 1,200, percentages and accounting negatives (4) sort as numbers; text sorts
naturally (item 2 before item 10); empty cells sink to the bottom. Alignment and cell content
are kept, and each sort or delete is one undo step. A table inside other text gets no controls.
Merging images. Merge ↓ on an image with another image directly below stacks the two into one image, left-aligned on a transparent canvas as wide as the wider one. Annotations on both are kept as editable shapes, the lower image's moved down with it. An image and a text entry never merge, so an image cannot be silently discarded into an entry's markdown.
Per-entry Copy, reorder, and delete.
Search runs through the command palette (Ctrl+K, /, or the box in the header). As you type,
the entries that match are listed under the commands — every term must match, as a substring —
each with its number and the line that matched; Enter or a click jumps to one. The first result,
Show only the n entries matching …, filters the notebook instead. While a filter is active
the header box shows it with a count, Copy and Export act on the filtered set and say so,
and Esc in command mode or the box's ✕ clears it. Ctrl+K and / reopen the palette on the
current filter, so it can be refined.
Undo / redo (Ctrl+Z, Ctrl+Shift+Z) covers deletes, reorders, splits, merges, imports,
checkbox conversions and Delete all — 30 steps deep. Typing collapses into one undo step per
editing session, and destructive actions put an Undo button in the toast.
Copy All concatenates entries to the clipboard; Export All (⋯ menu, or Ctrl+S) downloads them as
md-notebook_yyyyMMdd_HHmm.md; Backup as .json writes a lossless file including timestamps.
Import. Drag a .md file anywhere on the page (or use the ⋯ menu) to split it on --- into
entries; drop a .json backup to restore or append it. A .csv or .tsv file, or a .json file
holding an array of objects, becomes one table entry; a .csv file is read as CSV even with two
unquoted columns, which pasted text is not.
Jupyter notebooks. Export as .ipynb (⋯ menu or the Tools panel) writes nbformat 4.5, which
opens in VS Code, JupyterLab and GitHub. Every entry is a markdown cell; images travel as cell
attachments, so unlike the .md export nothing is replaced by a caption. Timestamps, annotations
and the image view ride in each cell's metadata.mdnb, and an annotated image is attached
flattened, so other tools show the markup, with the clean original in the metadata so the shapes
stay editable on the way back. Importing a notebook from elsewhere keeps markdown cells as they
are, fences code and raw cells (in the notebook's language), puts text output in a text fence
under its code, and turns image outputs and attachments into image entries.
Command palette, the main control: the box in the header, or in command mode, or / anywhere. Every action in one filterable list, with its shortcut beside it, so none has to be memorized, followed by the matching entries (see ). Type to filter (every word must match, in the label or its group), ↑/↓ and Enter to run. Entry and table commands act on the selected entry and are greyed out when it does not fit them.
Every button's tooltip names its shortcut. Outside a text box the notebook is in command mode:
| Key | Action |
|---|---|
j / k (or ↓ / ↑) | Move the selection |
Enter / e | Edit the selected entry |
Esc / Ctrl+Enter | Render the entry being edited |
a / b | Insert an entry above / below |
dd | Delete the selected entry |
c | Copy the selected entry |
t | Turn the selected entry's lines into tasks |
r | Toggle raw markdown view |
Shift+M | Merge the selected entry with the one below |
Ctrl+Shift+- | Split the entry being edited at the caret |
Alt+↑ / Alt+↓ | Move the entry up / down |
/ or Ctrl+K | Search: the command palette, on the current filter |
Ctrl+Shift+V | New entry from the clipboard |
Ctrl+Shift+Enter | New empty entry |
Ctrl+Z / Ctrl+Shift+Z | Undo / redo |
Ctrl+S | Export all as markdown |
p / Ctrl+Shift+P | Command palette |
Shift+C | Copy the selected entry as rich text |
Esc | Close the tools panel; with it closed, clear the search filter |
Ctrl+Alt+C | Copy all |
Two ways, for two different needs:
Edit — the built-in annotator, opened in place inside the entry so the notebook stays in
view; the button toggles it. Full screen ⤢ in its toolbar moves the work in progress, unsaved
shapes included, to a full-window editor. Either way: boxes, arrows, lines and text labels in five
colours. Tool keys are Excalidraw's own — r/2 box, a/5 arrow, l/6 line, t/8 text
— with Ctrl+Z / Ctrl+Shift+Z to step through, Ctrl+Enter to save and Esc to cancel.
With the Text tool, click to drop a label, or double-click with any tool; Esc while typing abandons the label rather than the
editor. The palette stops there on purpose: ellipse, freehand, diamond
and the rest are what Excalidraw is for. Annotations drawn by an earlier version still render.
Shapes are stored beside the pixels and drawn as an SVG overlay, so the screenshot itself is never modified, an annotation can be removed later, and the markup stays crisp at any zoom — a few hundred bytes per image rather than a re-encoded PNG on every edit. Flattening happens only on the way out, when the image is copied. Pointer events cover mouse, pen and finger.
Excalidraw ↗ — copies the image to the clipboard and opens excalidraw.com in its own window,
where Ctrl+V drops it in. Draw there, copy the result, and paste it back into the notebook as a
new entry. Point EXCALIDRAW_URL in app/ImageCell.tsx at a self-hosted instance if you have
one.
Embedding the editor was tried and removed: it was eight megabytes of lazily-loaded bundle plus a copy of its fonts, to reproduce something a second window already does well.
GitHub Flavored Markdown via remark-gfm: headings, fenced code blocks (```) with
syntax highlighting, inline code (`), tables, task lists, strikethrough, blockquotes, images,
and bare-URL autolinking.
A ```mermaid block is drawn as a diagram — flowcharts, sequence diagrams, ER diagrams,
Gantt charts — on a white card, so it reads like a pasted picture here and prints the same way.
Mermaid is about 4 MB, so it is fetched only the first time a diagram appears on screen; a notebook
without one never loads it. It runs with securityLevel: "strict", which sanitizes labels and
disables click handlers. A diagram that does not parse shows its first error line above the source. Single newlines render as line breaks (remark-breaks), which suits
pasted lists of links. Raw HTML is not rendered, so pasted content cannot inject markup.
npm install
cp .env.local.example .env.local # optional: only Send/Receive needs it
npm run dev # http://localhost:3000
npm run build # production build
npm run typecheck # tsc --noEmit
npm test # unit tests (vitest)
npm run test:e2e # end-to-end tests (Playwright, builds must be current)
npm run test:e2e starts its own production server on port 3123. Kill any stale next start first —
a server left running from an earlier build serves chunks that no longer exist and every test fails
at hydration. On a machine with a pre-installed Chromium (a sandbox, a locked-down image), set
CHROMIUM_PATH=/path/to/chrome instead of running playwright install.
CI (.github/workflows/ci.yml) runs typecheck, unit tests, build, and the end-to-end suite on every
push and pull request, and uploads the Playwright report when something fails.
The layout is fluid from a 412px cover screen up, and touch devices get their own rules: hover tooltips are suppressed (they stick after a tap), the code-block copy button is always visible rather than hover-revealed, and controls are at least 38px tall. On a OnePlus Open's cover screen, hiding the hint line and enabling compact mode takes the chrome above the first entry from 31% of the screen down to 17%; on the inner screen, from 16% to 5%.
Dropdowns are measured against the visible viewport and clamped to it, so a menu taller than the screen scrolls inside itself rather than running past the bottom edge — an overlay cannot be brought into view by scrolling the page behind it.
npm run build:static writes a self-contained site to out/ — 4.9 MB of plain files, no Node
runtime, no server component. About 1 MB loads with the page; most of the rest is Mermaid, fetched
only when a diagram is shown. Relative asset paths mean it also works opened directly from disk
over file://, so a copy in a OneDrive folder runs offline.
npm run build:static excludes the Send/Receive API routes, which a file host cannot run — the
static copy says so plainly if you try to send from it. Everything else works, offline included.
Azure Storage static website (HTTPS included, nothing to run):
.\scripts\Deploy-AzureStaticSite.ps1 -StorageAccount 'inscriptrxtools' -ResourceGroup 'rg-internal-tools'
azure-pipelines.yml does the same from Azure DevOps: typecheck, unit tests, end-to-end tests,
static build, then upload to the $web container.
IIS: copy out/ to the site folder. Serve it over HTTPS — navigator.clipboard requires a
secure context, so on plain http://hostname the paste button, every copy button and image paste
stop working. http://localhost is exempt; a real hostname is not.
Browser storage is per-origin, so moving between hosts does not carry the notebook with it. Use Backup as .json on the old URL and import it on the new one.
The app is a static-prerendered Next.js App Router project. Send/Receive is the one deviation: it
needs two environment variables and ships a single serverless function (/api/transfer, plus its
[code] child) to keep the Upstash token off the client — every other route is still static, and
without the variables the app behaves exactly as before, with Send and Receive reporting that
transfer is not set up.
| Variable | Where it comes from |
|---|---|
UPSTASH_REDIS_REST_URL | Upstash console → your database → REST API |
UPSTASH_REDIS_REST_TOKEN | same page; this is a secret and must not be prefixed NEXT_PUBLIC_ |
Set both in Vercel → Project → Settings → Environment Variables for Production, Preview and
Development. Locally they go in .env.local (gitignored); .env.local.example documents them.
Not reachable from the UI at present (see the note at the top); the routes still deploy.
| Route | Does |
|---|---|
POST /api/transfer | Body { entries: string[] }. Mints a code, writes transfer:<code> with SET … EX 86400 NX, answers { code }. |
GET /api/transfer/<code> | GETDEL transfer:<code>. Answers { entries }, or 404 { error: "not_found" } when the code expired or was already used. |
Both answer 503 not_configured when the environment variables are missing and 502 upstream
when Upstash is unreachable, so the browser can give each failure its own message. The token is
read only inside the route handlers (lib/upstash.ts) and never reaches the client bundle.
The project is already wired up at vercel.com/clem21/md-notebook and serves from md-notebook.vercel.app. Pushes to the production branch deploy automatically; other branches get preview URLs.
To set it up from scratch elsewhere:
next build, output .next.UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN, or skip them and leave
Send/Receive switched off.Or from the CLI:
npm i -g vercel
vercel # preview deployment
vercel --prod # production deployment
Apart from a pending Send — one transfer:<code> key in Redis, deleted the moment it is
received and expiring after 24h regardless — state is kept under the md-notebook:v2 key in localStorage (a v1 payload is migrated on first
load), scoped to the deployment's origin —
it does not sync between browsers or devices. Clearing site data clears the notebook, so use
Export All for anything worth keeping. If storage is full or disabled, the app keeps working
in memory for the session.
p/Ctrl+KCtrl+Shift+PTools panel (Tools… in the ⋯ menu, or tools in the palette): the occasional exports, kept out of the toolbars. For the
selected table entry: Copy TSV, Copy CSV and .csv ⬇, Copy JSON and .json ⬇,
and T-SQL. Copy INSERT writes a CREATE TABLE with NVARCHAR(MAX) columns and INSERT … VALUES in batches of 1000 rows, SQL Server's limit, into the table named in the panel (default
#Pasted, remembered). Copy VALUES writes SELECT * FROM (VALUES …) AS t (…), to join
against without creating anything. Plain numbers (42, -3.5) go in unquoted and as JSON
numbers; 007, 1,200 and $5 stay text, so an ID or a formatted amount is not altered. Empty
cells are NULL / null, strings are N'…' with quotes doubled, and column names are bracketed.
For the notebook: rich copy, Print / PDF, and .ipynb export and import.
Print / save as PDF (Tools panel, palette, or the browser's Ctrl+P): what is on screen —
the filtered entries, when searching — in black on white, with every entry at full length,
collapsed and clamped ones included, the header and toolbars gone, and each entry headed by its
timestamp. Choose Save as PDF in the print dialog for a file.
--- separators toggle controls whether exported entries are joined with a horizontal rule or
just a blank line.
New entries go to the top (⋯ menu) puts new, pasted, templated and imported entries above the
existing ones instead of below. a and b still insert relative to the selected entry.
Theme (⋯ menu, or theme in the command palette): three looks, remembered per browser.
The theme is applied before the page first paints, so a light theme does not flash dark on
load. Printing ignores it and uses the black-on-white print layout. Every colour in
app/globals.css is a variable, so a new theme in app/themes.css is a block of variables
plus whatever type and shape changes it wants.
Compact mode (⋯ menu) tightens spacing and type for small screens and long notebooks. Timestamps stay visible.
Wide mode (⋯ menu) lifts the 980px reading width so entries span the browser window.
Entry size sits beside each timestamp: the line count for a text entry, rows × columns for a table entry (hover for the spelled-out form), and pixel dimensions and file size for an image.
▾ All / ▸ All in the header folds every entry at once, or unfolds them when all are already folded.
The header is titled mdnb to leave room for search. The search-and-command box takes the free width in the header, up to 480px, and gives it up first as the window narrows — down to 120px — before any button wraps to a second line. It opens the command palette rather than taking typing itself.
The shortcut hint line can be dismissed with its ✕ and brought back from the ⋯ menu.
Collapse an entry with the ▾ button to leave two lines of it visible; the entry keeps a
marked edge, a +n lines chip, and its collapsed state across reloads. Double-click a collapsed
entry to expand it. Image entries collapse to their header.
Multi-tab safe. A second tab's writes are adopted rather than overwritten, and an entry open for editing in this tab is preserved through the merge.
Storage warning. If localStorage is full or blocked, a banner says entries are memory-only
instead of failing silently.