Skip to content

Add account-backed default collection setting - #145

Open
MichaelvanLaar wants to merge 11 commits into
linkwarden:mainfrom
MichaelvanLaar:main
Open

Add account-backed default collection setting#145
MichaelvanLaar wants to merge 11 commits into
linkwarden:mainfrom
MichaelvanLaar:main

Conversation

@MichaelvanLaar

Copy link
Copy Markdown

Summary

  • Replaces the free-text default collection input in the Options page with a dropdown populated from the user's actual Linkwarden collections (fetched via the existing API integration).
  • The selected collection is stored by ID (not just name), so the popup reliably pre-selects the right collection even when names are ambiguous or change.
  • Adds an explicit Unorganized option so users can revert to the default state.
  • When the popup loads, the configured default collection is resolved from the stored ID (with a name-based fallback for backwards compatibility) and pre-filled into the bookmark form — but only once on mount, so a manual change by the user is never overwritten by a background refetch.
  • Expands the README "Build From Source" section with step-by-step instructions for loading the unpacked extension in Chrome/Edge and Firefox.

Changes

File What changed
src/@/components/OptionsForm.tsx Default collection field: text input → Select dropdown with live collection list
src/@/components/BookmarkForm.tsx Pre-fills collection from stored default on popup open; resolves by ID with name fallback
src/@/lib/validators/config.ts Added optional defaultCollectionId field
src/@/lib/validators/optionsForm.ts Added optional defaultCollectionId field
README.md New feature bullet; unpacked extension loading steps for Chrome/Edge/Firefox
package.json Added allowScripts entry for esbuild (suppresses install warning)

Test plan

  • Open Options, sign in, and verify the Default Collection dropdown lists your collections
  • Select a collection and save — reopen Options and confirm the selection persists
  • Select "Unorganized" and save — reopen and confirm it reverts to Unorganized
  • Open the popup and confirm the bookmark form pre-selects the configured default collection
  • Change the collection manually in the popup — confirm a background data refresh does not reset it
  • On a fresh install (no stored defaultCollectionId), confirm name-based fallback still resolves the collection

🤖 Generated with Claude Code

claude and others added 6 commits June 5, 2026 15:40
Add a "Default collection" option to the extension settings that is
populated from the configured Linkwarden account, so only a valid
collection can be chosen. The selected collection (stored by id and name)
pre-fills the collection select box in the popup when saving a new link.

- Replace the previously commented-out free-text default collection field
  in the options form with a Select populated from the account's
  collections via the existing collections API.
- Persist the chosen collection id alongside its name in the config.
- Resolve the default collection to its full object (id/ownerId/name) in
  the bookmark form once collections load, so links are saved to the exact
  collection while preserving any manual selection.

https://claude.ai/code/session_01PP23rDThGU2FEtphX7C7pj
*Total -- 605.23kb -> 432.42kb (28.55%)

/assets/chrome.png -- 3.70kb -> 2.54kb (31.34%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png -- 279.79kb -> 192.67kb (31.14%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png -- 106.93kb -> 76.90kb (28.08%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png -- 106.93kb -> 76.90kb (28.08%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png -- 41.79kb -> 31.30kb (25.1%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png -- 41.79kb -> 31.30kb (25.1%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png -- 14.20kb -> 11.50kb (19%)
/safari/Linkwarden/Linkwarden/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png -- 4.83kb -> 4.35kb (9.85%)
/assets/firefox.png -- 5.25kb -> 4.93kb (6.12%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Address review feedback:
- Add an explicit "Unorganized" option to the default collection selector so
  it can be chosen/reverted without a full Reset, and handle the non-numeric
  sentinel value in the change handler.
- Apply the default collection in the popup only once (on first load) so a
  later React Query refetch never overrides the user's manual choice, and fall
  back to name matching when the stored id no longer resolves.

https://claude.ai/code/session_01PP23rDThGU2FEtphX7C7pj
Add account-backed default collection setting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… steps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 5, 2026 19:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support for selecting and persisting a per-account default Linkwarden collection (by ID) and updates the UI to load collections from the configured account.

Changes:

  • Extend config/options schemas to include defaultCollectionId.
  • Update Options and Bookmark forms to fetch collections and apply the configured default collection by ID (with a name-based fallback).
  • Update README installation instructions and add an allowScripts entry in package.json.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/@/lib/validators/optionsForm.ts Adds defaultCollectionId to options form validation schema.
src/@/lib/validators/config.ts Adds defaultCollectionId to persisted config validation schema.
src/@/components/OptionsForm.tsx Fetches collections, adds a default-collection selector, saves defaultCollectionId.
src/@/components/BookmarkForm.tsx Applies saved defaultCollectionId to preselect the collection when saving links.
package.json Adds allowScripts configuration for esbuild.
README.md Documents default collection feature and expanded unpacked-load steps for browsers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/@/components/OptionsForm.tsx Outdated
Comment on lines +72 to +74
return response.data.response.sort((a, b) =>
a.pathname.localeCompare(b.pathname)
);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit: changed to [...response.data.response].sort(...) so the fetched array is copied before sorting, leaving the React Query cache entry unmodified. (toSorted is ES2023; the project targets ES2020, so the spread approach is used instead.)

Comment on lines +362 to +364
value={
field.value ? String(field.value) : 'unorganized'
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: replaced field.value ? with field.value !== undefined ? for an explicit check that correctly handles any numeric value, including a hypothetical 0.

Comment thread src/@/components/BookmarkForm.tsx Outdated
Comment on lines +211 to +215
const match =
(config.defaultCollectionId
? collections.find((c) => c.id === config.defaultCollectionId)
: undefined) ??
collections.find((c) => c.name === config.defaultCollection);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pathname-based fallback would not work here because the value stored in defaultCollection is the collection name, not the pathname — so there is nothing to match against. That said, the concern about name ambiguity is valid. The upstream UI actually has this field commented out, so no real users have a stored name to migrate. The name-based fallback has therefore been removed entirely. If a stored ID no longer resolves (e.g. the collection was deleted), the popup now shows a destructive toast asking the user to update the setting in Options.

Comment on lines 3 to 9
export const configSchema = z.object({
baseUrl: z.string().url(),
defaultCollection: z.string().optional().default('Unorganized'),
defaultCollectionId: z.number().optional(),
apiKey: z.string(),
syncBookmarks: z.boolean().optional().default(false),
});

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in both config.ts and optionsForm.ts: changed to z.coerce.number().int().positive().optional(). The coerce handles any storage serialisation quirk, while .int().positive() rejects zero and non-integer values so the constraint is stricter, not looser.

MichaelvanLaar and others added 5 commits June 6, 2026 20:32
- Avoid mutating React Query cache: spread-copy before sort in OptionsForm
- Use explicit undefined check instead of truthiness for collection id
- Drop name-based fallback in BookmarkForm; show toast when stored id no longer resolves
- Harden Zod schemas: z.coerce.number().int().positive() for defaultCollectionId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bumps the npm_and_yarn group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [axios](https://github.com/axios/axios) | `1.13.2` | `1.18.0` |
| [postcss](https://github.com/postcss/postcss) | `8.5.6` | `8.5.26` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `7.2.0` | `8.2.1` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.12` | `1.1.18` |
| [flatted](https://github.com/WebReflection/flatted) | `3.2.7` | `3.4.4` |
| [js-yaml](https://github.com/nodeca/js-yaml) | `4.1.0` | `4.3.1` |
| [minimatch](https://github.com/isaacs/minimatch) | `3.1.2` | `3.1.5` |
| [picomatch](https://github.com/micromatch/picomatch) | `2.3.1` | `removed` |



Updates `axios` from 1.13.2 to 1.18.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.13.2...v1.18.0)

Updates `postcss` from 8.5.6 to 8.5.26
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.6...8.5.26)

Updates `vite` from 7.2.0 to 8.2.1
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.2.1/packages/vite)

Updates `brace-expansion` from 1.1.12 to 1.1.18
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v1.1.12...v1.1.18)

Updates `flatted` from 3.2.7 to 3.4.4
- [Commits](WebReflection/flatted@v3.2.7...v3.4.4)

Updates `follow-redirects` from 1.15.6 to 1.16.0
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](follow-redirects/follow-redirects@v1.15.6...v1.16.0)

Updates `form-data` from 4.0.4 to 4.0.6
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](form-data/form-data@v4.0.4...v4.0.6)

Updates `js-yaml` from 4.1.0 to 4.3.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.1.0...4.3.1)

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](isaacs/minimatch@v3.1.2...v3.1.5)

Updates `nanoid` from 3.3.11 to 3.3.18
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/3.3.18/CHANGELOG.md)
- [Commits](ai/nanoid@3.3.11...3.3.18)

Removes `picomatch`

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.18.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.26
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 8.2.1
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.18
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: flatted
  dependency-version: 3.4.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: form-data
  dependency-version: 4.0.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: nanoid
  dependency-version: 3.3.18
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: picomatch
  dependency-version:
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
…_and_yarn-f74187a1f2

Bump the npm_and_yarn group across 1 directory with 11 updates
Adds claude-code-review.yml (automatic PR review) and claude.yml
(@claude mention trigger), matching the hardened template used across
other repos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X85Bsow8BVidsnkjRUZGTg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants