Release v0.1.16 - #324
Merged
Merged
Conversation
Open a folder as a collection: requests stored as YAML files
(NNN- prefix = order, dirs = folders, collection.yaml manifest),
so collections can be committed, diffed and PR-reviewed. Filesystem
is the store — mutations diff the serialized tree and write only
changed files; window focus re-reads external edits. Secrets stay
out of files ({{vault.*}} tokens pass through; environments never
serialized). Desktop only: tauri fs/dialog/persisted-scope plugins
plus a recursive fs-scope grant command for picked dirs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… loss - Serve Monaco from the app bundle (scripts/copy-monaco.mjs → public/monaco/vs, loader.config in lib/monaco-loader.ts) instead of jsdelivr; every editor was stuck on "Loading editor..." offline. Bump monaco-editor to 0.55.1 — the 0.53 AMD build throws in language/css/monaco.contribution.js. - Drop the stale Firebase-key gate from build-tauri.mjs (auth was removed). - Script runner: 5 s hard timeout with worker terminate + respawn; a top-level infinite loop no longer hangs the tab forever. - Tabs persistence: strip refetchable graphqlSchema always; on quota overflow retry with example bodies + file bytes stripped instead of wiping all tabs. - History: strip uploaded file bytes before POST; SQL ORDER BY/LIMIT instead of loading every row; clear/delete hard-delete rows (sync is gone, tombstones only accumulated). - Rust proxy: pooled reqwest clients (TLS/keep-alive reuse) and a 50 MB response body cap with an error envelope instead of unbounded buffering. - generate-code.ts: replace raw NUL byte in Go escaper regex with \0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…ed kit Phase 4 of the tool-consistency pass. Every Media & Design tool now renders through ToolShell + IOPanel / kit-surface panels instead of hand-rolled Cards, sharing one visual language with the rest of the app. Migrated (logic + visuals preserved): - color-picker, contrast-checker, css-gradient-builder, svg-optimizer, image-compressor, code-screenshot, exif-viewer, favicon-generator, keycode-inspector - css-generators hub + box-shadow / border-radius / clamp sub-tabs Verified intact: color swatches/pickers/eyedropper, WCAG contrast badges, gradient live preview + PNG export, image-compressor canvas + before/after, EXIF preview + metadata, favicon canvas render + ICO/ZIP export, code-screenshot export node, keycode live capture + history, and all CSS live previews. Also: IOPanel now omits its header row when a panel has neither label nor actions (kept from the Converters phase). 0 type errors; all 10 pages render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ors mounted
- RequestTabs + ResponsePanel are React.memo; the nine inline
`(v) => updateActiveTab({...})` setters become one memoized setter map,
onSaveExample/onMount/onChange handlers are stable.
- handleSend/handleCopyCurl go through latest-value refs so RequestPanel's memo
holds and the keydown listener stops re-registering on every edit;
handleLoadRequest/handleCancel/handleCurlPaste/example handlers memoized.
- Response JSON parsed once (useMemo) instead of three times per render for
the tree/table/geo views; those views cap at 200 children / 500 rows.
- Desktop layout keeps the request panel in one ResizablePanelGroup and adds
the response panel conditionally (id/order) — Send no longer remounts Monaco.
Body / pre-request / tests editor tabs are forceMount + hidden when inactive.
- History list row renderer memoized so VirtualHistoryList's memo holds;
TabBar's layout effect keys on tab id/kind/method/name, not the tabs array.
- sendProxyRequest() returns the Rust envelope directly instead of
stringify → fake Response → json(); typed ProxyEnvelope.
- Secret-variable scan covers params/headers/auth/body only, not the whole tab
(previous response + example bodies).
- Scripts worker caches pm.response.json() per run.
- next/dynamic for GrpcPanel (protobufjs), WebSocketPanel, ImportDialog
(js-yaml), CookieJarDialog, MetricsDialog, CollectionRunnerDialog; exporters
are imported on click.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…llections Brings the folder-of-YAML collections (file-store.ts, collection-files.ts, move-to-vault.ts, Rust fs/dialog/persisted-scope plugins, registry commands) onto the current main. Conflict resolution keeps main's auth removal — the branch's deep-link plugin, auth_server command and cookie_store deps stay deleted — and main's reqwest 0.13 / base64 0.23. The "Open folder as collection" button moves into ToolSidebarActions, where main relocated the sidebar header controls. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Postman and Insomnia comparison pages stopped conceding mock servers, collections, scripting and the runner — all shipped. They now list REST, GraphQL, WebSocket, native gRPC, the local mock server, runner, vault-backed secrets and import/export formats, and concede only team/cloud workspaces, a CLI runner and Windows/Linux builds. New /compare/mydevtools-vs-bruno and /compare/bruno-alternative pages; api-client metadata description/keywords and the landing capability blurb describe the real scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
The Postman environment importer existed but nothing called it. The import
dialog now detects `{ name, values[] }` payloads and creates the environment;
the environment manager gets a per-environment Postman export button.
i18n key ApiClient.environmentManager.exportPostman in all 27 locales.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…in palette entry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Two importers for the switcher path, both pure libs with tests:
- Insomnia (`lib/import/insomnia.ts`): v4 JSON resources and v5 YAML, nested
groups, every body/auth type we support, `{{ _.VAR }}` → `{{VAR}}`,
pre-request/after-response scripts, environments. Round-trips our own
Insomnia v4 exporter.
- Bruno (`lib/import/bruno.ts`): the legacy `.bru` language and v4
OpenCollection YAML. Folder trees with `folder.bru` defaults, `seq`
ordering, `~key` disabled rows, json/text/xml/form/multipart (`@file(...)`)
/graphql bodies, basic/bearer/apikey/oauth2 auth, scripts, and
`environments/*` (secret var names come in with empty values).
Detection covers both, and the import dialog now creates the environments an
import brings with it instead of dropping them. 22 new tests.
Still folder-only from a paste of a single file: picking a whole Bruno
directory needs a desktop folder-read pass (readCollectionFiles is .yaml-only).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
feat(media-design): migrate all 10 Media & Design tools onto the shar…
…nd CLI
Adds the Secure Files tool ("Files" in the UI): import files or folders and
store them as opaque `<32 random hex>.mydt` objects in a storage folder the
user picks. Original names, folder paths, types and contents are encrypted;
the app shows them only while the vault is unlocked.
Format (docs/MYDT_FORMAT.md, crates/mydt):
- Self-contained per file: Argon2id salt/params + per-file DEK wrapped by the
key derived from the master password + XChaCha20-Poly1305 metadata and
payload, with the header bound in as AAD. No index — listing scans the
folder and decrypts headers, so a folder is portable and self-healing.
- Extracted into the standalone `mydt` crate: library plus a `mydt` CLI
(encrypt/decrypt/info/ls) behind `--features cli`, so the desktop app and
the CLI read and write identical objects. Wired into rust.yml.
Desktop:
- New `/api/v1/secure-files/*` routes: settings, list, import, patch, replace,
export, delete, folder rename/delete; raw-bytes `secure_file_read` command
for previews.
- The webview gate hands the verified master password to Rust once per unlock
so Argon2id can derive the Secure Files key; it lives in AppState only and
is dropped on lock (idle auto-lock included).
- Listing keeps a decrypted-metadata cache reconciled by an id-set diff, and
import encrypts across threads with one directory fsync per batch. 10k
files: import 1.75s, cold list 0.30s, warm list 24ms.
UI:
- Folder tree, list and grid views (both virtualized), preview for text,
code, images and PDF, export with a plaintext warning, rename/move/replace,
dismissible warning for unreadable objects. Files up to 20 MB.
- Fixes a latent bug in the shared s3-drive FilePreviewDialog, which used
Radix Tooltip without a TooltipProvider and crashed the dialog subtree.
`pnpm i18n:sync` also backfilled unrelated missing keys across the 26
non-English locales, which is why messages/ is broad.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01No7es2PjF9SNQs22RYHcdM
Grid tiles show the actual image instead of a type icon, Drive-style. Only the tiles currently on screen are decrypted (the hook is fed the same visible window the virtualizer computes), at most three at a time, and the queue re-prioritizes on scroll. Each image is downscaled to 256px and re-encoded before it is kept, so the plaintext original is dropped right away and the blob pool stays bounded — 150 thumbnails, oldest off-screen ones revoked, everything revoked when the tool unmounts on lock. Cache keys include size and mtime so Replace invalidates a stale thumbnail. SVG skips the canvas (WKWebView cannot createImageBitmap it) and anything that fails to decode falls back to its icon without retrying. Also moves blobMime into lib/secure-files.ts so the preview and thumbnail paths share one implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01No7es2PjF9SNQs22RYHcdM
Codes were only ever issued at vault setup, so a user who burned all
eight — or set the vault up before codes existed — had no recovery path
left, even though the downloaded code file told them to regenerate from
Settings.
- New Settings card: verifies the master password, generates a fresh set
and stores it (storing replaces the whole array, which is what
invalidates the old codes), then shows them once with copy/download.
- GET /api/v1/auth/backup-codes returns {total, remaining} so the card
can show how many codes are left.
- markBackupCodeUsed now throws on a non-OK response instead of failing
silently, and the unlock gate burns the code after a successful unlock
rather than before — a failed burn leaves a retryable code instead of
spending one on an unlock that never happened.
- Code grid extracted so the setup gate and the settings card render
codes identically.
- SettingsPage.backupCodes translated across all 27 locales.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XHJKZ77EJdyDQ6gDt7WH6
Adds an Overview entry above the folder tree that swaps the main pane for storage stats: file and folder counts, content size, true on-disk size with the encryption overhead called out, a per-type breakdown with counts, sizes and proportional bars, plus largest and recently added files (each row jumps to its containing folder). Unreadable objects are surfaced here too. On-disk size is measured, not estimated. The listing cache now stores each object's size taken from the handle it already opens to read the header, so the first scan after unlock gets it for free and later listings cost nothing extra; write-through keeps it exact across import, rename and replace. Totals cover readable objects only — unreadable ones stay in `errors` rather than being folded into the numbers. Everything else is derived client-side from the file list already in memory and memoized, so the view stays instant at 100k files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01No7es2PjF9SNQs22RYHcdM
The Files strings shipped as English placeholders because i18n-sync fills missing keys by copying en.json — the audit passed on key presence, not on translation. This replaces all 84 keys per locale with real translations: navigation label, dashboard card, and the whole SecureFiles namespace. Plural rules follow each language rather than English: ru/uk/pl/cs carry few/many, ar carries the full zero/one/two/few/many/other set, and the languages without a count distinction (ja/ko/zh/tr/vi/id/ms) use a single other form. Verified by compiling all 1998 messages through intl-messageformat per locale and formatting them with real arguments, so every placeholder and plural branch is exercised — "3 файла", "5 plików", "ملفان", "2 soubory". Terminology is localised, not transliterated: vault becomes Tresor, coffre-fort, bóveda, cassaforte, 保管庫, 保险库; storage folder, masked and overhead follow suit. Quotation marks use each locale's convention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01No7es2PjF9SNQs22RYHcdM
Secure Files: encrypted .mydt file vault, shared format crate and CLI
- Params/Headers: the column header was a flex row with no `gap` while the data rows had `gap-2`, and its trailing spacer was `w-10` against a 36px delete button — so KEY/VALUE never sat over their inputs. Both now share one grid template, and the labels use the Input's own `px-3`. - Toolbar: `OfflineIndicator` renders null whenever you are online, but the divider before it rendered unconditionally, leaving a hairline and two gaps hanging off the end of the row. The indicator now brings its own separator. - Group dividers were `bg-border/50` and `mx-1`: too faint to read as dividers on a dark ground, so they looked like unexplained holes, and the extra margin broke the row's 8px rhythm. Now `bg-border`, spaced by the row's own gap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Drops the local Monaco bundling from c50bd6e per maintainer decision: removes lib/monaco-loader.ts, scripts/copy-monaco.mjs, the pre{dev,build} hooks, the build-tauri copy step and the public/monaco gitignore entry. @monaco-editor/loader falls back to its default cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs. Keeps monaco-editor at 0.55.1: it is a types-only dependency here, and 0.55.1 is the exact version the loader pulls from the CDN, so the local typings match what runs. Trade-off, recorded deliberately: with no network every Monaco-backed editor (API client body/scripts/response plus ~15 other tools) stays on "Loading editor..." — the app is not fully offline while this stands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Reported against the API client, but two of the three live in the shared ToolSidebarLayout used by 11 tools. - Collapsed, the 40px rail rendered a single toggle: no items, no identity, just an empty strip. It now shows the tool's accent icon, which peeks the full panel as a flyout (the existing Sheet, previously mobile-only) so the list stays reachable without giving up the width; the pin-open toggle sits below it. - New `isOverlay` on the panel context — true for the mobile sheet and the new desktop flyout — so picking a row dismisses a floating panel but never the pinned one. All five call sites moved off `isMobile`, which would have left the desktop flyout covering the thing you just opened. - API client: the tool had no title of its own (the panel header says "Collections"), leaving it unnamed once collapsed, and its toolbar sat flush against the top edge. Adds an `h1` from the existing `Navigation.apiClient` key — present in all 27 locales, so no new strings — and `px-4 pt-3`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Two follow-ups to the collapsed-rail work, both about showing one control where there should be one: - The rail rendered the tool icon *and* a separate expand arrow — two buttons for the same action stacked in 40px. Now a single button: the tool's accent icon at rest (so the strip still names the tool), swapping to the toggle glyph on hover/focus. The desktop flyout goes with it; clicking expands, as the glyph promises. `isOverlay` narrows back to the mobile sheet. - `PanelLeftClose` / `PanelLeftOpen` draw a panel outline *and* a chevron inside 16px, which at this size reads as two icons overlapping rather than one control — visible in the Notes header. Both replaced with plain `PanelLeft`, the same glyph shadcn uses for its sidebar trigger. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
API Client: offline Monaco, data-loss and hang fixes, render perf, folder collections, Insomnia/Bruno import
Network & API Improvements
…redicate Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…abindex Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
… the icon rail Collapsing used to unmount the panel, which discarded the body's search text, expanded tree groups and scroll position every time. It also made a rail impossible: entries are published by hooks inside the body, and an unmounted body cannot publish. Hide with display:none instead, mirroring how TabContent keeps inactive tool tabs alive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…d rail password-manager, api-keys, to-do and environment-manager gain a populated rail with no edits of their own — the facets are already declared as filter items. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…the rail notes and snippet-manager publish pinned items, api-client its collections, data-explorer and s3-drive their connections, bookmarks and secure-files their root folders. Also fixes secure-files' folder tree calling panel.close() unconditionally on select, which collapsed the whole sidebar on every folder pick on desktop — every other sidebar guards that with isOverlay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…ollapse Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
… NUL byte The "overview showing, nothing selected" sentinel was a literal NUL inside a string literal in the source. Same value, but the byte made the whole file binary to grep, ripgrep and diff, which then skip it without saying so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
…tives Shipped unused. They give the next per-tool sidebar migration somewhere to land without that migration also having to design the pieces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
The hero greeted off `useAuth().user.displayName`, which is a hardcoded null — that identity object is deliberately name-free, and the editable profile name lives in local preferences behind `useAppUser` (as top-bar and mobile-nav already read it). So the greeting never named anyone, however the profile was filled in. Reads the profile name instead, via a `greetingFirstName` helper that is unit tested. The now-unused `user` prop is dropped from DashboardHero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
The vault gate sat in page.tsx, which is this layout's children — so a locked vault swapped the editor for the placeholder while the sidebar column kept rendering a note tree, a search field and a New note button, all backed by a vault with no key loaded. Gate in the layout instead, above NotesProvider so the provider does not mount and fail to load notes it cannot decrypt. password-manager, api-keys, environment-manager, secure-files and snippet-manager already gate before the component that owns their ToolSidebarLayout; notes differed only because its layout is a separate route-level wrapper. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H3GuKxASDXLwFCLA7o3GGH
Akhil/unify tool sidebar
The settings page was one long scroll of stacked cards. Split it into four tabs (reusing existing i18n keys — no locale churn): - Profile — profile card - Appearance — theme, accent color, language - Security — auto-lock, vault icons, telemetry, backup codes - Backup & data — backup/restore, factory reset, app version No logic changes; each section's cards moved verbatim into a TabsContent. 0 type errors; page renders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
UI button color fix
Phase 6-Security
feat(settings): segregate settings into tabs
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| export function importInsomniaWithMeta(text: string): InsomniaImportResult { | ||
| const t = text.trim() | ||
| // yaml.load also parses JSON, so v4 YAML exports come for free. | ||
| const doc = (t.startsWith("{") ? JSON.parse(t) : yaml.load(t)) as |
ThreatCrush Security Scan1 finding(s) MEDIUM: 1
Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.