* fix(ui): use utf-8 encoding for inline SVG icons to support non-latin characters
* chore(test-item): re-add data-sortable-handle to TestItem box
* docs: update changelog
- Remove hardcoded body font-family from index.scss, use MUI theme dt.font_family instead
- Extract monospace font stack to shared MONACO_FONT_FAMILY constant in utils/font-family.ts
- Add platform check for twemoji mozilla in ip-info-card.tsx
- Consolidate 5 Monaco Editor font-family declarations into single constant
* refactor: migrate to @dnd-kit/react and simplify sortable components
* chore: enable css formatting in biome.json
* refactor(profile): simplify icon rendering logic and add alt text
* style(profiles): add overflow hidden to profile grid container
* refactor(profile): add radix for parseInt and improve type safety in profiles
* docs: update changelog with drag-and-drop styling improvements
* refactor(profile): move hasValidName function out of ProxiesEditorViewer
* refactor(proxy): refactor proxy chain into sortable components
* feat(proxy): add drop state styling and arrow animations to proxy chain
* style(profiles): reduce horizontal padding in profile grid
* feat(profile): add vertical margin to editor viewer items
* chore: refresh pnpm lockfile
* fix: preserve drag and drop behavior
---------
Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
Each auto-update reloads the core, which drops every connection running through a
`proxy-providers` entry. Existing profiles are corrected once at startup; the form
warns about shorter intervals but still saves them.
When a proxy node without a name (name: null) exists in the prepend/append
sequence, ProxiesEditorViewer passed a null id into @dnd-kit's SortableContext,
which runs `'id' in item` on it and throws
"Cannot use 'in' operator to search for 'id' in null", crashing the page.
Filter out proxies without a valid (non-empty string) name from the rendered
and sortable lists so the visualization editor no longer crashes. The raw YAML
data is preserved and can still be fixed in the advanced (text) mode.
Fixes#7580
- Replace boolean/string validation results with structured ValidationOutcome values across config validation, profile switching, DNS validation, and script validation. Callers can now distinguish valid, invalid, skipped, and busy states, with invalid results carrying an error kind and message.
- Route core config updates through structured outcomes and checked forced updates. Generation and validation failures now discard runtime drafts and surface validation failures instead of relying on empty success messages.
- Preserve existing frontend ergonomics by keeping boolean service wrappers where callers only need success, while direct DNS and script validation consumers receive ValidationOutcome. Failed profile editor saves reload the backend-restored file content and synchronize Monaco models so reopened editors show the restored state.
- Dispose Monaco editor instances on dialog close to prevent cycle leak
- Replace gcTime: Infinity with finite TTLs and evict orphaned subscription queryKeys
- Add missing useEffect cleanup for timers, move setTimeout out of useMemo
Replace swr with @tanstack/react-query v5 across all hooks, providers,
and components. Introduce singleton QueryClient, WS subscription pattern
via useQuery+useEffect, and enforce component-layer cache access contract.
The profile-update-completed event handler was missing a mutate('getProfiles')
call, causing the "X time ago" display to show stale timestamps after
backend timer auto-updates.