Repository navigation
Import: other browsers, bookmarks files, pasted links, history (IM) - #22
Conversation
… window and onboarding's step - Netscape bookmarks HTML (Chromium- and Firefox-style exports): folders, order, titles and addresses kept; a file with no bookmarks says "No bookmarks found in this file" and imports nothing. - One pinned folder, "Imported bookmarks", at the end of the target Space's pinned section, with closed sub-folders; tabs asleep, no favicon or request until opened; one undo step. - Large imports go in store updates of 250 items (2,000 bookmarks: about 0.15 s, longest main stall about 30 ms on a quiet machine). - The import window (IM-01): Back / Next / Cancel, the bookmark tree with Select all / none and a count (IM-04), the target Space (IM-06), progress and a summary with skipped items; Done shows the folder in the sidebar (IM-14). Dropping a file on the window or the sidebar starts it. - Onboarding's import step shows the same flow inline (OB-03): its import is added on Start browsing, and Skip setup adds nothing (D44). The Done screen says what was imported (OB-09). - Browser profiles are found through one function (importPlaces); tests set fixture places, and a test run without them searches nowhere and fails. Fixtures come from scripts/import-fixtures.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Paste a list of links in the import window: one address per line, "Title — address" (a dash, bar or tab), Markdown links and bullets; one pinned folder named by the user, a pinned tab per link in the list's order, asleep. Lines that aren't links are counted in the summary. - ⌘V / Ctrl+V on the sidebar outside a text field, with two or more links on the clipboard, opens the same import with them filled in; one link or other text is left alone. - A .txt list of links from a file or a drop imports the same way (IM-13), named after the file. - Enter is Next and Escape is Back in the flow (Enter in the folder name too). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…06, IM-14) - Finds Chromium-family browsers (Local State, a Default folder, or Opera's own folder), Firefox (profiles.ini) and Safari (macOS) at the places importPlaces lists; tests set made-up fixture folders only. - Reads each profile's bookmarks from copies: Chromium's Bookmarks JSON, Firefox's places.sqlite (node:sqlite, built into Electron's Node), Safari's Bookmarks.plist (a binary property list). A file that can't be copied is locked: the types step asks to quit the browser and try again. - import.sources / import.inspect, and the browser plan: "Imported from <Browser>" with the source's groups as sub-folders; several profiles get a sub-folder each, or a matching Orbit profile and Space each. - Entry points: the macOS app menu, the command bar and Settings -> General (its runner is now bound, so Import... opens the flow). - Fixture script writes chrome/, firefox/ and safari/ fixture folders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(IM-07, IM-15) Chromium's History, Firefox's places.sqlite and Safari's History.db are read from copies a window of rows at a time and added to the chosen profile's history: titles, visit and typed counts and the newest visits. History and the command bar have them at once; no tab is made. A history larger than the 50,000 pages Orbit keeps is trimmed to its newest as it is read, and the summary counts the rest. Importing again adds only the visits a page lacked. 100,000 pages take about 2 s without a long task in the windows. The history store gains importPages and finishImport (additive), which write the import once as a snapshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (IM-16) After onboarding's import of a history, the Done step offers the six sites visited most: three as Favorites, three as pinned tabs. One click takes one and another takes it back; Dismiss all drops them. They are added, unloaded, with the import when Start browsing ends onboarding, and never with Skip setup (D44). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export Pinned Items as Bookmarks… (the File menu on macOS, the command bar, and Settings → General with Include Today tabs) writes a standard bookmarks HTML file through the save dialog: the Favorites as a folder, each Space as a folder of its pinned section with its folders nested, and Today's tabs only when chosen. Orbit's own pages and private windows' profiles are left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t (IM-09) The types step lists the source profile's extensions from its Preferences and Secure Preferences, each read from a copy: pinned ones first, the browser's own left out, a name kept only in the manifest read from it in its default language. The row is off and says they can't be imported yet; unpacked ones never come over. Installing them, pinning and the ad-blocker de-duplication wait for the extensions lane's install path on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FEATURES.md regenerated from the merged feature status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st keeps it that way (D57) The help for a browser Orbit can't read, and the pasted folder's default name, named the browser the spec describes. The clean-room rule forbids that in Orbit's UI. The heading is now "Browser not listed?", the default name "Imported links", and the routes are told in general terms, with no other product's menus or shortcuts. scripts/features.mjs writes IM-12's checklist title neutrally too. tests/unit/clean-room.test.ts fails on 30de427 (8 UI lines and the checklist title) and passes here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: ae2d47f1-9c4d-4447-b6a1-4483e473d04b) |
Conflicts: src/shared/rpc.ts keeps both sides' views; docs/feature-status.json is merged key by key (no key was changed on both sides); FEATURES.md is regenerated from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the drop offers (M3, D58) The sidebar's fallback for a dropped file with no bookmarks opened the drop's address with tabs.create. After chrome's merge that ran before onLinkDrop's D49 check, so a page's drag carrying a file and a file: address would open the local file. The fallback now applies the same check (pageMayOpen); a refused address leaves the import window to say the file holds no bookmarks. Test: import-file, a .txt with no links dropped with a file: uri-list opens no tab. Without the fix it fails: a tab opens on the file: address. The import specs now check that no page was loaded instead of counting all web contents: chrome's views (loading, link) are made as needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 829bc19a-ecd7-41db-9c14-c143b6f6898b) |
…ead in a worker (M1, D58)
- netscape.ts: a title ends at its close tag or the next <DT>, <DD>, <DL>, <H3> or <A>, never
further; the tag and title patterns use [^<>]; titles are cut to 1,000 characters.
- parseLinkList: a Markdown link is split at its last "](" (no backtracking); the sidebar's
paste looks at its first 256 KB only, until two links are found.
- A dropped or chosen file, and a links plan's text, are read in a worker thread
(reader-worker.ts, electron-vite's ?nodeWorker). A bookmarks tree comes back as JSON text,
which main passes on to the window without reading; link nodes come in parts of 5,000.
- Folders deeper than 64 are flattened in place while reading (files and other browsers'
bookmarks, iteratively); counts are iterative; a file's items past 200,000 are counted (F1).
- A file over 64 MB says it is too large on every route, and the import window checks the
size before reading (F2). A read that fails says so (F1).
- The import window starts a folder holding over 1,000 bookmarks closed, and draws each new
file's tree afresh (F5).
Tests: tests/unit/import-linear.test.ts reads 12 shapes at 64 and 256 KB in thread CPU time;
the review's 6 failed before (e.g. "<a " 2.7 s and one title of "<" 9.7 s at 64 KB). The e2e
test drops five 1 MB hostile files on the import window: main's longest stall was 7.2-7.7 s on
the first before, 30-44 ms per file now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 3b440f43-ff12-4977-aa2c-c7516cc81f72) |
…go at quit, and are cleared at startup (M2, D58) - The scratch folders are made under <user data>/import-scratch, which is cleared when Orbit starts, so what a crash leaves is removed at the next launch. - The folders being read are kept in a set. In will-quit every import is cancelled, the reader workers are ended, and each open folder's databases are closed and the folder removed synchronously, before the state's last write. - A history read that the quit cut short logs nothing. Tests: a quit at 2,000 of 100,000 pages read leaves no copy in TMPDIR or user data (without the fix, orbit-import-*/0-History stayed in TMPDIR); a folder planted in import-scratch before launch is gone after startup (without the fix, still there 10 s on). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 01e9bfac-b058-4f83-b596-36fa4a816d7a) |
…only its status (F6, F8) - F6: the folders of the bookmarks being added are tracked. A quit (will-quit, before the state's last write) or a failure part-way removes them with everything in them, in one store update. A crash part-way isn't covered: the parts saved before it stay. - F8: once an import ends, ImportJobs keeps only its status and the window it came from; its plan (a file's tree or a pasted text) and the bookmarks read are let go. Done finds its folder there. Tests: a quit with 248 of 100,000 bookmarks added, then a relaunch, shows no "Imported bookmarks" (without the fix: it held 998 tabs); a failure on the third store update leaves no tab (without the fix: 498 stayed); an ended job is no longer kept whole (without the fix: get() returned it, plan and all). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 23a00368-63e8-4c52-83a1-c75d99a88658) |
…ers' bookmarks and damaged files (F3) - Skipped bookmarks can't be chosen, so the import window always sends them, with their folders, to be counted. Main adds no folder that holds nothing but them. - A profile's file that is there but can't be read is named with its types and why: at "What to import" and in the summary, e.g. "Work: history couldn't be read (the file is damaged)". A history read that fails during the import adds the same line. Summary lines may now have no count. - Items cleanTree leaves out past 200,000 are counted (M1); deeper folders are flattened, not cut. Tests: 4 bookmarklets, 3 in one folder, give "4 bookmarks: Bookmarklet (a script, not a page)" and no empty folder (without the fix: "1 bookmark"); a garbage Default History, a 5,000-page History cut in half and a garbage places.sqlite are named (without the fix: nothing, the types step and summary were silent). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 69eaae5d-fd3e-41ef-84a4-95742773ea16) |
…6 MB isn't read, and settings must be an object (F4) - readJson checks the size of the file and of its copy: over 16 MB, it isn't read. - extensions.settings is spread only when it's a plain object; at most 1,000 extensions are looked at. Tests: settings that are a 4 MB string take 40 ms CPU and list nothing (without the fix: 8.8-11.1 s CPU per round); a 17 MB Preferences lists nothing and a 15 MB one is read (without the fix, the 17 MB one was read). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: daef14a9-7a1b-4e68-9ba7-41959df182da) |
…server uses ports 3350-3369 - F1: files nested 2,000 and 50,000 folders deep, dropped on the import window, show "2 of 2" and import both bookmarks with the folders kept to 64 levels; dropped on the sidebar, the window opens at them. A read that fails (main's handler stubbed to throw) says "Orbit couldn't read this file", from the import window and from the sidebar. - F2: a 65 MB file is said to be too large when dropped on the import window, chosen with From a file..., or dropped on the sidebar. - loggedServer starts at 3350 + worker % 20. Without the fixes (the build before M1): the 2,000-deep file never reached the bookmarks step and no message showed; the 65 MB drop said "big.html: No bookmarks found in this file". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 67294652-6b9d-4648-8da0-2750eb39ef4a) |
…nd in names; the features script spells the name (F11) - The test reads src/, tests/ (fixtures included, binary ones as latin1), scripts/, the feature status file and FEATURES.md. It compares words without case, splitting at camelCase humps and at regular-expression escapes, with an allowlist (empty) for lines where the letters mean something else. - scripts/features.mjs spells the name from letters, as the test does; FEATURES.md comes out the same. Without the fix: the extended test found 4 lines in scripts/features.mjs (57, 60, 61, 62). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 3ef2beb1-79f4-4e1f-878d-d22dac49b560) |
- IM-01: Cancel is offered until adding starts, then Hide (bookmarks are one undo step); the window isn't modal. - IM-03: cookies (IM-10) aren't offered; a file that can't be read is named with why. - IM-04: which folders start closed, and that the list isn't virtualized (F5). - IM-05: partial: no favicon is fetched when an imported row scrolls into view. - IM-13: files are read in a worker in linear time; deep, oversized and unreadable files (F1, F2). - IM-14: the summary names every item left out (F3); a quit while adding takes the bookmarks back (F6); scratch copies live under user data (D58). - OB-14: an import chosen during onboarding isn't saved, so a quit before Start browsing loses it (F7). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 0100bb89-12a8-4521-adfa-b79e008a5bf2) |
The held onboarding import has no Cancel in the progress view; the note said it had. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 8e833770-3df3-4af1-ae7e-8ef862c54802) |
Conflicts: src/shared/rpc.ts keeps both new RendererViews, 'import' and 'internal'; FEATURES.md is regenerated. docs/feature-status.json merged by itself: it parses, has no duplicate keys, and matches both sides key by key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 8af4a8e4-7ee9-4907-9259-722c25099856) |
…or failure takes back only the import's own tabs (D62, N3) The import's tabs go into the state in paced parts of 1,000 where no section shows them, then one update adds the folders and shows them at once. Nothing can be moved into or out of the folder while it is made, so a quit or a failure part-way takes back exactly the tabs it added, by id. Undo of a finished import is one step, also across Spaces. An e2e-only hook holds an import part-way so the quit test can stop it there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tes are capped; the limit counts bookmarks only (D62 (5), N1, N2, N6, N7) A list of links over about 1 MB or 10,000 lines, dropped, chosen or pasted, is never editable text: main holds its bytes, the import window shows its count and first eight lines, read-only, with Clear, and the import takes the held bytes. It is counted once, in the reader. The windows send UTF-8 bytes, which main moves to the reader and back without copying or decoding them, so a 16 MB list stalls main under 100 ms (291-589 ms before) and the import window stays under 400 MB (it took over 3 GB). A paste over 64 MB says it's too large, as a file does. The 200,000 limit counts bookmarks only, so the selection count and the summary agree; folders have a limit of their own, 20,000, past which they're flattened into the folder that holds them (N7). Tests that fail without each fix cover the long list (dropped and pasted), the paste cap, the reader worker (N1), the quit's removal of a copy still being made (N2), the limits (N7) and the clean-room test's backslash, UTF-16 and file-name checks (N6). A new test shows a crash part-way leaves nothing of the import. The IM-04, IM-05, IM-12, IM-13 and IM-14 notes say what was measured. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: a6d16e70-c490-4038-96ac-d1ac2aa8b16b) |
… one stays, Restore Data stops it (D65, N1, N3-N7) - Must-fix (D65 (1)-(2)): the command bar leaves out the tabs an import made out of sight (a shared set in import/unplaced.ts), but not a Peek's floating tab. At the attach, a tab the user reached anyway and placed stays where they put it; its folder leaves it out. Comments no longer say nothing can reach them. - N1 (D65 (3)): Restore Data, its undo and redo stop a running import first, also while it adds its starter suggestions. Its tabs are removed without going to the Archive, and the import window says why it stopped. - N3: the windows count a paste's bytes (TextEncoder.encodeInto into at most 64 MB) and send none of a list over the cap. The test checks what main receives. - N4: the quit test reads the state written at the quit, with a placed hidden tab. - N5: Clear tells main to let go of a held list. - N6: the clean-room test looks for the bare letters in binary files, with an allow-list of ordinary words checked at their place. - N7: an import is done only once its numbers are in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: 4a2dd22a-8b92-4467-a089-d058f5785570) |
Conflicts: SidebarItems.tsx and src/shared/actions.ts each keep both sides' import lines; FEATURES.md regenerated with scripts/features.mjs. docs/feature-status.json merged on its own and matches both sides leaf by leaf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SidebarItems.tsx: both imports kept (the import lane's sidebar helpers and the feel lane's motion config). The import lane's reveal finds a row by its slot, and slots stay for every row in the windowed list (FL-21), so it reaches rows not drawn yet. FEATURES.md regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The import lane: bring bookmarks, history and pinned tabs over from other browsers on the machine, from standard files, and from a pasted list of links. Built from
lanes/import.mdand the IM rows of the spec.What it builds
import.open), the app menu, the command bar's "Import", and Settings → General (ST-03).main.Data safety
tests/fixtures/import/, built byscripts/import-fixtures.ts). In e2e mode Orbit refuses to search the real places unless a test has set fixture places.tests/unit/clean-room.test.tskeeps the name out ofsrc/andFEATURES.md.Review
The review (
reviews/lane-import.md) asked for changes, with 3 must-fixes:file:address without D49's check.Fix round 1 (
2062278..9d06199) mergedmainand fixed all three:It also fixed F1–F8 and F11.
Fix check 1 asked for 2 more must-fixes:
Fix round 2 (
8a285c0..bc6723d) merged downloads (PR Downloads and DevTools (downloads lane: DL, DT) #20) and fixed both (D62):20,000 bookmarks now freeze the window for under 90 ms (it was up to 2.3 s).
Fix check 2 asked for 1 more must-fix: the command bar could find the import's hidden tabs, and a tab found and placed ended up in two places.
Fix round 3 (
ce46735) fixed it (D65):Fix check 3: APPROVE, no must-fix (
reviews/lane-import-fixcheck3.md).sources.ts.Merge gate (D42), run locally on Linux on
ce46735npm run build,npm run typecheck,npm run lintand./node_modules/.bin/vitest runall exit 0. Unit: 685 passed of 685, in 72 files.xvfb-run -a -s "-screen 0 1440x900x24" ./node_modules/.bin/playwright test --workers 1 --retries 0ran 544 tests in 26.2 min: 540 passed, 4 skipped, 0 failed, and it exited 0.test.fail()) counts as passed.ce46735itself, withmain(70b3f9d) in it.src/main/import/places.ts);🤖 Generated with Claude Code
Note
High Risk
Imports and mutates sidebar tabs, folders, and per-profile history from external browser data and large files, with restore/import interaction and filesystem copies of sensitive profile databases.
Overview
Adds the import lane: a multi-step flow to bring data from other browsers on the machine, Netscape bookmarks HTML,
.txtlink lists, and pasted URLs into Orbit as pinned folders and profile history.Main process discovers Chromium/Firefox/Safari profiles (
places.ts,browsers.ts), reads copies of their SQLite/JSON files (copy.ts, worker-based file parsing inreader-worker.ts), maps bookmarks vianetscape.ts/ browser-specific parsers, and places them withDetachedFolderso large imports build tabs off-sidebar then attach in one step (bookmarks.ts,unplaced.ts). History merges in chunks through newHistoryStore.importPages/finishImport(history.ts). Commands wireimport.open,import.start, export pinned items (export.ts), onboarding commit, and e2e hooks inimport/index.ts.Integration:
registerImportnow takesHistoryService; command-bar tab search excludes unplaced import tabs; Restore Data callsstopImportsForRestoreso a backup restore does not archive half-built import tabs (AR-11 / D65). Docs regenerate higher built/tested counts for IM-* and related OB/ST rows;scripts/import-fixtures.tsbuilds deterministic test browser trees;features.mjsneutralizes a competitor product name in checklist titles.Reviewed by Cursor Bugbot for commit ce46735. Bugbot is set up for automated code reviews on this repo. Configure here.