diff --git a/.impeccable/design.json b/.impeccable/design.json new file mode 100644 index 0000000..6281722 --- /dev/null +++ b/.impeccable/design.json @@ -0,0 +1,290 @@ +{ + "schemaVersion": 2, + "generatedAt": "2026-08-21T19:47:00.000Z", + "title": "Design System: Entangled VPN — Connected Shell", + "extensions": { + "colorMeta": { + "bg": { + "role": "neutral", + "displayName": "Bench Black", + "canonical": "#0a0a0a", + "tonalRamp": ["#0a0a0a", "#141414", "#1a1a1a", "#222222", "#2a2a2a", "#3a3a3a", "#5c5c5c", "#8a8a8a"] + }, + "surface": { + "role": "neutral", + "displayName": "Panel Surface", + "canonical": "#141414", + "tonalRamp": ["#0a0a0a", "#111111", "#141414", "#1a1a1a", "#222222", "#2a2a2a", "#3a3a3a", "#5c5c5c"] + }, + "surface-2": { + "role": "neutral", + "displayName": "Raised Surface", + "canonical": "#1a1a1a", + "tonalRamp": ["#0a0a0a", "#141414", "#1a1a1a", "#222222", "#262626", "#2a2a2a", "#3a3a3a", "#5c5c5c"] + }, + "border": { + "role": "neutral", + "displayName": "Structure Border", + "canonical": "#2a2a2a", + "tonalRamp": ["#0a0a0a", "#141414", "#1a1a1a", "#222222", "#262626", "#2a2a2a", "#3a3a3a", "#5c5c5c"] + }, + "hairline": { + "role": "neutral", + "displayName": "Hairline", + "canonical": "#3a3a3a", + "tonalRamp": ["#141414", "#1a1a1a", "#222222", "#262626", "#2a2a2a", "#3a3a3a", "#5c5c5c", "#8a8a8a"] + }, + "muted": { + "role": "neutral", + "displayName": "Muted Label", + "canonical": "#8a8a8a", + "tonalRamp": ["#2a2a2a", "#3a3a3a", "#5c5c5c", "#6e6e6e", "#8a8a8a", "#a3a3a3", "#c8c8c8", "#f5f5f5"] + }, + "text": { + "role": "neutral", + "displayName": "Readout Text", + "canonical": "#f5f5f5", + "tonalRamp": ["#111111", "#2a2a2a", "#5c5c5c", "#8a8a8a", "#a3a3a3", "#c8c8c8", "#e8e8e8", "#f5f5f5"] + }, + "text-dim": { + "role": "neutral", + "displayName": "Dim Trace", + "canonical": "#5c5c5c", + "tonalRamp": ["#0a0a0a", "#141414", "#1a1a1a", "#222222", "#2a2a2a", "#3a3a3a", "#5c5c5c", "#8a8a8a"] + }, + "cta-fill": { + "role": "primary", + "displayName": "Bench White", + "canonical": "#ffffff", + "tonalRamp": ["#0a0a0a", "#2a2a2a", "#5c5c5c", "#8a8a8a", "#b8b8b8", "#d4d4d4", "#eeeeee", "#ffffff"] + }, + "cta-ink": { + "role": "primary", + "displayName": "CTA Ink", + "canonical": "#0a0a0a", + "tonalRamp": ["#0a0a0a", "#111111", "#141414", "#1a1a1a", "#222222", "#2a2a2a", "#3a3a3a", "#5c5c5c"] + }, + "live": { + "role": "secondary", + "displayName": "Live Green", + "canonical": "#2fbf62", + "tonalRamp": ["#0d3319", "#145c2a", "#1a8f45", "#228f4a", "#2fbf62", "#4fd17a", "#7ee0a0", "#c8f5d8"] + }, + "fault": { + "role": "tertiary", + "displayName": "Fault Red", + "canonical": "#e23d3d", + "tonalRamp": ["#3d0a0a", "#6e1414", "#9e1e1e", "#c41e1e", "#e23d3d", "#e86a6a", "#f09999", "#f5cccc"] + }, + "graticule": { + "role": "neutral", + "displayName": "Graticule Line", + "canonical": "#262626", + "tonalRamp": ["#0a0a0a", "#111111", "#141414", "#1a1a1a", "#222222", "#262626", "#2a2a2a", "#3a3a3a"] + } + }, + "typographyMeta": { + "display": { + "displayName": "Wordmark", + "purpose": "Topbar brand cell — uppercase tracked bench label." + }, + "headline": { + "displayName": "Section Headline", + "purpose": "Channels, Chat, Settings pane titles." + }, + "title": { + "displayName": "Card Title", + "purpose": "Settings disclosure section headers." + }, + "body": { + "displayName": "Body", + "purpose": "Hints, toggles, composer textarea." + }, + "label": { + "displayName": "Tracked Label", + "purpose": "Readout captions and column headers via .label-track." + }, + "mono": { + "displayName": "Mono Readout", + "purpose": "VIP, IPs, ping, path labels, chat transcript." + } + }, + "shadows": [ + { + "name": "overlay-menu", + "value": "0 8px 24px rgba(0, 0, 0, 0.28)", + "purpose": "Sidebar overflow menus only — functional overlay elevation." + }, + { + "name": "overlay-modal", + "value": "0 12px 40px rgba(0, 0, 0, 0.28)", + "purpose": "Create/join network dialogs — functional overlay elevation." + } + ], + "motion": [ + { + "name": "ease-out", + "value": "cubic-bezier(0.16, 1, 0.3, 1)", + "purpose": "Default outgoing transitions — buttons, borders, chevrons." + }, + { + "name": "ease-inout", + "value": "cubic-bezier(0.4, 0, 0.2, 1)", + "purpose": "Balanced transitions where specified." + }, + { + "name": "dur-fast", + "value": "150ms", + "purpose": "Row hover, sidebar tick feedback." + }, + { + "name": "dur-mid", + "value": "280ms", + "purpose": "Button and border state changes." + }, + { + "name": "dur-slow", + "value": "420ms", + "purpose": "Card body collapse animation." + } + ], + "breakpoints": [ + { + "name": "peer-collapse", + "value": "720px", + "purpose": "Peer graticule hides IP, path, ping columns." + } + ] + }, + "components": [ + { + "name": "Default Button", + "kind": "button", + "refersTo": "button-default", + "description": "Standard bordered bench control for secondary actions.", + "html": "", + "css": ".ds-btn-default { border: 1px solid var(--border, #2a2a2a); background: transparent; color: var(--text, #f5f5f5); padding: 8px 14px; min-height: 36px; border-radius: 2px; cursor: pointer; font-weight: 500; font-size: 13px; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; transition: background 280ms cubic-bezier(0.16, 1, 0.3, 1), border-color 280ms cubic-bezier(0.16, 1, 0.3, 1), color 280ms cubic-bezier(0.16, 1, 0.3, 1); } .ds-btn-default:hover:not(:disabled) { border-color: var(--hairline, #3a3a3a); background: var(--surface-2, #1a1a1a); } .ds-btn-default:focus-visible { outline: 2px solid var(--text, #f5f5f5); outline-offset: 2px; } .ds-btn-default:disabled { opacity: 0.45; cursor: not-allowed; }" + }, + { + "name": "Ghost Button", + "kind": "button", + "refersTo": "button-ghost", + "description": "Muted inline action in peer row and compact contexts.", + "html": "", + "css": ".ds-btn-ghost { border: 1px solid var(--border, #2a2a2a); background: transparent; color: var(--muted, #8a8a8a); padding: 4px 10px; min-height: 28px; border-radius: 2px; cursor: pointer; font-weight: 500; font-size: 12px; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; transition: color 280ms cubic-bezier(0.16, 1, 0.3, 1); } .ds-btn-ghost:hover:not(:disabled) { color: var(--text, #f5f5f5); } .ds-btn-ghost:focus-visible { outline: 2px solid var(--text, #f5f5f5); outline-offset: 2px; }" + }, + { + "name": "CTA Button", + "kind": "button", + "refersTo": "button-cta", + "description": "Primary commit — send message, save, create/join confirm.", + "html": "", + "css": ".ds-btn-cta { background: var(--cta-fill, #ffffff); color: var(--cta-ink, #0a0a0a); border: 1px solid var(--cta-fill, #ffffff); padding: 8px 14px; min-height: 36px; border-radius: 2px; cursor: pointer; font-weight: 600; font-size: 13px; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; transition: background 280ms cubic-bezier(0.16, 1, 0.3, 1); } .ds-btn-cta:hover:not(:disabled) { background: color-mix(in srgb, var(--cta-fill, #ffffff) 88%, var(--cta-ink, #0a0a0a)); } .ds-btn-cta:focus-visible { outline: 2px solid var(--text, #f5f5f5); outline-offset: 2px; } .ds-btn-cta:disabled { opacity: 0.45; cursor: not-allowed; }" + }, + { + "name": "Bezel Chrome Button", + "kind": "button", + "refersTo": "bezel", + "description": "Uppercase chrome control for settings, disconnect, and panel actions.", + "html": "", + "css": ".ds-bezel { border: 1px solid var(--border, #2a2a2a); background: transparent; color: var(--text, #f5f5f5); min-height: 32px; padding: 4px 10px; border-radius: 2px; font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; transition: background 280ms cubic-bezier(0.16, 1, 0.3, 1), border-color 280ms cubic-bezier(0.16, 1, 0.3, 1); } .ds-bezel:hover:not(:disabled) { border-color: var(--hairline, #3a3a3a); background: var(--surface-2, #1a1a1a); } .ds-bezel.active, .ds-bezel.ds-bezel-active { background: var(--text, #f5f5f5); color: var(--bg, #0a0a0a); border-color: var(--text, #f5f5f5); } .ds-bezel:focus-visible { outline: 2px solid var(--text, #f5f5f5); outline-offset: 2px; }" + }, + { + "name": "Mono Input Field", + "kind": "input", + "refersTo": "button-default", + "description": "Settings and modal text field — mono values on bench black fill.", + "html": "", + "css": ".ds-field-label { display: block; font-size: 12px; font-weight: 600; color: var(--muted, #8a8a8a); margin-bottom: 4px; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; } .ds-input { width: 100%; background: var(--bg, #0a0a0a); border: 1px solid var(--border, #2a2a2a); border-radius: 2px; color: var(--text, #f5f5f5); padding: 8px 10px; min-height: 36px; font-size: 13px; font-family: 'IBM Plex Mono', Consolas, monospace; box-sizing: border-box; } .ds-input:focus-visible { outline: 2px solid var(--text, #f5f5f5); outline-offset: 2px; } .ds-input.ds-invalid { border-color: var(--fault, #e23d3d); }" + }, + { + "name": "Readout Label Track", + "kind": "custom", + "refersTo": "bezel", + "description": "Tracked uppercase caption paired with mono readout values in topbar cells.", + "html": "
Virtual IP10.242.0.42
", + "css": ".ds-readout-cell { display: flex; flex-direction: column; gap: 4px; padding: 10px 16px; border-right: 1px solid var(--border, #2a2a2a); } .ds-label-track { display: block; font-size: 10px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted, #8a8a8a); line-height: 1.2; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; } .ds-cell-value { font-size: 1.35rem; font-weight: 500; letter-spacing: 0.02em; color: var(--text, #f5f5f5); font-family: 'IBM Plex Mono', Consolas, monospace; line-height: 1.15; }" + }, + { + "name": "Path LED Cluster", + "kind": "custom", + "refersTo": "bezel", + "description": "Three-lamp path indicator with 7-segment quality bar — live/fault/idle semantics.", + "html": "
Path
Direct
", + "css": ".ds-path-readout { display: flex; flex-direction: column; gap: 4px; } .ds-path-row { display: flex; align-items: center; gap: 8px; } .ds-leds { display: flex; gap: 4px; } .ds-led { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--text-dim, #5c5c5c); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text, #f5f5f5) 18%, transparent); } .ds-led.ds-on.ds-led-live { background: var(--live, #2fbf62); } .ds-led.ds-on.ds-led-fault { background: var(--fault, #e23d3d); } .ds-led.ds-on.ds-led-idle { background: var(--text, #f5f5f5); } .ds-path-value { font-family: 'IBM Plex Mono', Consolas, monospace; font-size: 1.22rem; font-weight: 500; } .ds-path-value.ds-live { color: var(--live, #2fbf62); } .ds-path-value.ds-fault { color: var(--fault, #e23d3d); } .ds-seg-bar { display: flex; gap: 3px; margin-top: 2px; } .ds-seg { flex: 1; height: 3px; background: var(--text-dim, #5c5c5c); } .ds-seg.ds-on.ds-live { background: var(--live, #2fbf62); } .ds-seg.ds-on.ds-fault { background: var(--fault, #e23d3d); }" + }, + { + "name": "Settings Card", + "kind": "card", + "refersTo": "button-default", + "description": "Collapsible settings section with raised head and bordered body.", + "html": "
Connection

Changes apply on reconnect.

", + "css": ".ds-card { background: var(--surface, #141414); border: 1px solid var(--border, #2a2a2a); border-radius: 2px; overflow: hidden; } .ds-card-head { display: flex; align-items: center; min-height: 48px; padding: 10px 14px; background: var(--surface-2, #1a1a1a); } .ds-card-title { font-size: 13px; font-weight: 600; color: var(--text, #f5f5f5); font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; } .ds-card-body-pad { padding: 14px 16px 16px; border-top: 1px solid var(--border, #2a2a2a); } .ds-hint { color: var(--muted, #8a8a8a); font-size: 13px; margin: 0 0 12px; max-width: 65ch; line-height: 1.45; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; }" + }, + { + "name": "Unread Badge", + "kind": "chip", + "refersTo": "button-cta", + "description": "Fault-red unread count chip on room chat and peer rows.", + "html": "3", + "css": ".ds-badge { display: inline-block; min-width: 16px; padding: 0 5px; background: var(--fault, #e23d3d); color: #ffffff; font-size: 10px; line-height: 16px; text-align: center; border-radius: 2px; font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; font-weight: 500; }" + }, + { + "name": "Network Sidebar Row", + "kind": "nav", + "refersTo": "bezel", + "description": "Quiet rail network item with square active tick and mono meta.", + "html": "
game-night
3 peers
", + "css": ".ds-network-item { display: flex; align-items: center; padding: 10px 12px; min-height: 44px; gap: 10px; border-bottom: 1px solid var(--border, #2a2a2a); cursor: pointer; transition: background 150ms cubic-bezier(0.16, 1, 0.3, 1); } .ds-network-item:hover { background: var(--bg-hover, #222222); } .ds-network-item.ds-active { background: var(--surface-2, #1a1a1a); } .ds-tick { width: 6px; height: 6px; flex-shrink: 0; background: var(--text-dim, #5c5c5c); } .ds-tick.ds-on { background: var(--text, #f5f5f5); } .ds-network-name { font-size: 13px; font-weight: 500; color: var(--text, #f5f5f5); font-family: 'Bricolage Grotesque', 'Segoe UI', sans-serif; } .ds-network-meta { font-size: 11px; font-family: 'IBM Plex Mono', Consolas, monospace; color: var(--muted, #8a8a8a); line-height: 1.4; }" + } + ], + "narrative": { + "northStar": "The Signal Bench", + "overview": "The connected shell treats a mesh session like an oscilloscope readout: room identity, virtual IP, and path quality are the primary instruments; peers are numbered channels on a graticule; chat is a secondary readout panel. Layout is a horizontal bench — readout band on top, quiet network rail left, channel graticule center, chat readout right — not a three-pane messenger reskin.\n\nMatte black-and-white panels, 1px hairlines, and tracked uppercase labels over mono values carry the instrument metaphor. Path quality (Direct/p2p vs relay/WebSocket) is the chromatic trigger: green only for live direct paths, red only for relay, fault, unread, and error states. No ambient wash, grain, or decorative color elsewhere.\n\nThe intro/connect screen (space.css, ConnectView) is a separate visual world and out of scope for this system.", + "keyCharacteristics": [ + "Readout-band chrome: room, VIP, path LEDs + segment bar, peer counts, theme toggle, bezel actions", + "Two-chroma signal grammar: --live green and --fault red only; everything else neutral B&W", + "Tracked .label-track captions over .cell-value mono readouts", + "Graticule peer table (.scope-face) with 48×28px grid lines", + "2px corner radius, hairline borders, tonal layering — no default drop shadows on surfaces", + "Dark default + light theme via data-theme=\"light\" token swap" + ], + "rules": [ + { + "name": "The Two-Chroma Rule", + "body": "--live and --fault are the only hue accents in the connected shell. Do not introduce blues, yellows, or brand tints on connected surfaces. Neutral gray carries everything else, including caution and offline states.", + "section": "colors" + }, + { + "name": "The Path-First Rule", + "body": "Color draws the eye to path quality before chat, settings, or decorative chrome. Green means direct; red means relay, degraded, unread, or error.", + "section": "colors" + }, + { + "name": "The Label-Over-Value Rule", + "body": "Instrument captions sit above values: tracked uppercase label, then heavier mono or semibold value. Do not inline label:value pairs in the readout band.", + "section": "typography" + }, + { + "name": "The Hairline Rule", + "body": "Structure uses 1px solid var(--border). Hover promotes to var(--hairline). No thick frames, inset glows, or offset drop shadows on bench surfaces.", + "section": "elevation" + } + ], + "dos": [ + "Do scope all connected tokens and rules under html[data-app=\"connected\"] and connected.css.", + "Do lead with path quality in topbar and peer list before chat or settings affordances.", + "Do pair .label-track captions with mono or semibold values in readout surfaces.", + "Do use --live for p2p/direct and --fault for relay, ws, unread, errors only.", + "Do keep the graticule grid confined to .scope-face peer channels.", + "Do use 2px radius and 1px hairlines consistently across buttons, cards, inputs, and bezels." + ], + "donts": [ + "Don't apply connected-shell tokens to the intro/connect screen (space.css world).", + "Don't add chromatic accents beyond --live and --fault on connected surfaces.", + "Don't use drop shadows, grain, or ambient washes on bench panels — overlays only.", + "Don't reskin as a messenger three-pane layout; chat is a secondary readout, not the home surface.", + "Don't use Bungee or connect-screen display fonts inside the connected shell." + ] + } +} diff --git a/.impeccable/mocks/comp-a-readout.json b/.impeccable/mocks/comp-a-readout.json new file mode 100644 index 0000000..4dacb8e --- /dev/null +++ b/.impeccable/mocks/comp-a-readout.json @@ -0,0 +1 @@ +{"prompt":"Comp A readout + scope face for Entangled Signal bench","approved":true,"optionId":"comp-a","world":"scientific-notation-oscilloscope-signal-bench","seed":"82f5fa76"} \ No newline at end of file diff --git a/.impeccable/mocks/verify-chat.png b/.impeccable/mocks/verify-chat.png new file mode 100644 index 0000000..a395f8f Binary files /dev/null and b/.impeccable/mocks/verify-chat.png differ diff --git a/.impeccable/mocks/verify-connected.png b/.impeccable/mocks/verify-connected.png new file mode 100644 index 0000000..5d0258d Binary files /dev/null and b/.impeccable/mocks/verify-connected.png differ diff --git a/.impeccable/mocks/verify-settings.png b/.impeccable/mocks/verify-settings.png new file mode 100644 index 0000000..2bcd2e8 Binary files /dev/null and b/.impeccable/mocks/verify-settings.png differ diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..e7271f9 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,144 @@ +--- +name: Entangled VPN — Connected Shell +description: Oscilloscope signal-bench UI for the connected mesh session — matte B&W panels, path-quality chroma, graticule peer channels. +colors: + bg: "#0a0a0a" + surface: "#141414" + surface-2: "#1a1a1a" + border: "#2a2a2a" + hairline: "#3a3a3a" + muted: "#8a8a8a" + text: "#f5f5f5" + text-dim: "#5c5c5c" + cta-fill: "#ffffff" + cta-ink: "#0a0a0a" + live: "#2fbf62" + fault: "#e23d3d" + graticule: "#262626" + bg-light: "#ffffff" + surface-light: "#f7f7f7" + live-light: "#1a8f45" + fault-light: "#c41e1e" +typography: + display: + fontFamily: "'Bricolage Grotesque', 'Segoe UI', sans-serif" + fontSize: "11px" + fontWeight: 500 + lineHeight: 1.2 + letterSpacing: "0.18em" + headline: + fontFamily: "'Bricolage Grotesque', 'Segoe UI', sans-serif" + fontSize: "1.05rem" + fontWeight: 600 + lineHeight: 1.15 + letterSpacing: "-0.03em" + title: + fontFamily: "'Bricolage Grotesque', 'Segoe UI', sans-serif" + fontSize: "13px" + fontWeight: 600 + lineHeight: 1.5 + letterSpacing: "0" + body: + fontFamily: "'Bricolage Grotesque', 'Segoe UI', sans-serif" + fontSize: "14px" + fontWeight: 400 + lineHeight: 1.5 + letterSpacing: "0" + label: + fontFamily: "'Bricolage Grotesque', 'Segoe UI', sans-serif" + fontSize: "10px" + fontWeight: 500 + lineHeight: 1.2 + letterSpacing: "0.14em" + mono: + fontFamily: "'IBM Plex Mono', Consolas, monospace" + fontSize: "1rem" + fontWeight: 500 + lineHeight: 1.15 + letterSpacing: "0" +rounded: + sm: "2px" + md: "8px" + full: "999px" +spacing: + xs: "4px" + sm: "8px" + md: "12px" + lg: "16px" + xl: "20px" +components: + button-ghost: + backgroundColor: "transparent" + textColor: "{colors.text}" + rounded: "{rounded.md}" + padding: "8px 14px" + height: "36px" + button-cta: + backgroundColor: "{colors.cta-fill}" + textColor: "{colors.cta-ink}" + rounded: "{rounded.md}" + padding: "8px 14px" + height: "36px" + bezel: + backgroundColor: "transparent" + textColor: "{colors.muted}" + rounded: "{rounded.sm}" + padding: "6px 10px" + height: "32px" + label-track: + textColor: "{colors.muted}" + typography: "{typography.label}" +--- + +# Design System: Entangled VPN — Connected Shell + +## Overview + +The connected app is a signal bench, not a consumer VPN dashboard. After join, the window is a dense instrument: one readout band, a quiet networks rail, graticule peer channels, and a chat readout. Path quality is the primary signal. The intro / connect screen (`ConnectView`, `space.css`) is a separate surface and stays out of this system. + +## Colors + +Matte black-and-white neutrals own every surface. `--live` green and `--fault` red are the only chromatic tokens, and they are scoped: + +- Green: Direct / p2p path only +- Red: relay, reconnecting, unread badges, errors + +Do not introduce amber, cyan, neon wash, or decorative chroma. Light theme keeps the same roles with inverted neutrals and slightly deeper live/fault. + +## Typography + +Bricolage Grotesque carries chrome and section titles. IBM Plex Mono carries VIP, peer IPs, ping, path labels, and the chat log. Readout cells use tracked uppercase labels (`.label-track`) over semibold or mono values. Section panes use a single instrument title line — no kicker above the heading. + +## Layout + +1. Readout band (~64–72px): Entangled wordmark, room, copyable VIP, path LEDs + 7-tick segment bar, peer and relay counts, theme / settings / disconnect +2. Left rail: saved networks, create / join (resizable, ~220px default) +3. Center: `.scope-face` graticule with CH-indexed peer rows and inline path traces +4. Right: chat readout (~42%, max 480px) open by default in a room; settings replaces peer+chat when active + +No ambient wash, grain, or view-transition gimmicks. Depth is tonal (surface steps) and 1px hairlines, not drop shadows on the bench itself. Overlay shadows exist only on menus and modals. + +## Components + +- `.btn` / `.btn-ghost` / `.btn-cta` / `.btn-quiet` / `.btn.danger` +- `.bezel` for chrome actions +- `.led` and segment ticks for path aggregate +- `.scope-face` peer table on a CSS graticule +- Settings `.card` disclosures with SVG chevrons +- Theme switch pill (B&W thumb; no colored track accents) + +Icons are drawn SVG at one stroke weight. Unicode menu glyphs are not part of the system. + +## Do + +- Derive chrome path state from one `pathAggregate` (`direct` | `relay` | `reconnecting` | `disconnected`) +- Keep chat as a side readout, not a top-level app route +- Prefer density suitable for a desktop Wails window +- Preview with Vite sim `?connected=1` + +## Don't + +- Paint green or red outside path / fault / unread / error roles +- Add a telemetry footer that invents crypto, throughput, or engine claims +- Restyle the intro starfield / Entangled title as part of this system +- Reintroduce section kickers above pane titles diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..402c272 --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,53 @@ +# Product + + + +## Platform + +web + +## Users + +Primary: a small friend group on Windows who want a shared LAN for games, files, and chat without a public VPN brand. They run the client as Administrator (Wintun), join a named room, and stay in it for a session. + +## Product Purpose + +Entangled VPN creates a private virtual LAN (`10.242.0.0/24`) between friends. Success is: you are in a room, you have a virtual IP, peers are reachable (P2P or relay), and chat/DMs work. + +## Positioning + +A mesh for a handful of people, not a consumer VPN. Rooms, pipe invites (`server|room|password`), owner delete, and in-mesh chat are the product. Crypto is X25519 + HKDF-only (`hkdf-v1`) + XChaCha20-Poly1305. + +## Operating Context + +Windows desktop app (Wails v2 + Wintun). Connect screen first; after connect: saved networks, peer list, room chat / DMs, settings. Path can be P2P, UDP relay, or WebSocket. Auto-reconnect re-joins the room. Languages: English, Russian, Chinese. + +## Capabilities and Constraints + +- Connected shell: networks rail, peer list as home, chat as a side panel (not a top-level app), settings in chrome, disconnect, theme, copy virtual IP, ping, DMs. +- Intro / connect screen with the Entangled title is out of scope for this redesign and must keep its current look. +- Do not invent server hosts, tokens, or crypto claims. +- Frontend can be previewed with the Vite sim (`lib/sim.js`, DEV-only; `?connected=1` jumps into the shell). + +## Brand Commitments + +- Name: Entangled. Intro screen stays as-is. +- Connected-app palette: high-contrast black-and-white only. Subtle green only for Direct/p2p. Subtle red only for relay/fault/unread/error. No other chromatic accents. +- Connected frame: one chrome row (room + IP + path), quiet room rail, dense peer list, chat panel. No ambient wash, grain, or view-transition gimmicks. +- Overhaul the connected UI (layout and language), not a reskin of the previous chrome. + +## Evidence on Hand + +- README, CHANGELOG, `client/frontend` Svelte UI, `client/vpncore`. +- No customer quotes or usage metrics. Sim data is synthetic. + +## Product Principles + +- The room and your virtual IP are the facts that matter. +- Path quality (Direct vs relay) is the primary signal in chrome and the peer list. +- Intro and connected app can look different; connected must still feel like the same product. +- Density for a desktop window, not a marketing page. + +## Accessibility & Inclusion + +Keyboard focus, `prefers-reduced-motion`, and existing i18n strings. No new copy that is English-only without locale keys. diff --git a/client/frontend/dist/assets/bricolage-latin-400-A6LyuA6R.woff2 b/client/frontend/dist/assets/bricolage-latin-400-A6LyuA6R.woff2 new file mode 100644 index 0000000..ccc73da Binary files /dev/null and b/client/frontend/dist/assets/bricolage-latin-400-A6LyuA6R.woff2 differ diff --git a/client/frontend/dist/assets/bricolage-latin-500-DEDBoLFO.woff2 b/client/frontend/dist/assets/bricolage-latin-500-DEDBoLFO.woff2 new file mode 100644 index 0000000..ed9b2a2 Binary files /dev/null and b/client/frontend/dist/assets/bricolage-latin-500-DEDBoLFO.woff2 differ diff --git a/client/frontend/dist/assets/bricolage-latin-600-nxTgbNFE.woff2 b/client/frontend/dist/assets/bricolage-latin-600-nxTgbNFE.woff2 new file mode 100644 index 0000000..e9794de Binary files /dev/null and b/client/frontend/dist/assets/bricolage-latin-600-nxTgbNFE.woff2 differ diff --git a/client/frontend/dist/assets/ibm-plex-mono-latin-400-Dm_PoFIZ.woff2 b/client/frontend/dist/assets/ibm-plex-mono-latin-400-Dm_PoFIZ.woff2 new file mode 100644 index 0000000..a6c77d6 Binary files /dev/null and b/client/frontend/dist/assets/ibm-plex-mono-latin-400-Dm_PoFIZ.woff2 differ diff --git a/client/frontend/dist/assets/ibm-plex-mono-latin-500-C_OblDzq.woff2 b/client/frontend/dist/assets/ibm-plex-mono-latin-500-C_OblDzq.woff2 new file mode 100644 index 0000000..99c2610 Binary files /dev/null and b/client/frontend/dist/assets/ibm-plex-mono-latin-500-C_OblDzq.woff2 differ diff --git a/client/frontend/dist/assets/ibm-plex-serif-latin-400-italic-yImJBWyJ.woff2 b/client/frontend/dist/assets/ibm-plex-serif-latin-400-italic-yImJBWyJ.woff2 new file mode 100644 index 0000000..5645b4d Binary files /dev/null and b/client/frontend/dist/assets/ibm-plex-serif-latin-400-italic-yImJBWyJ.woff2 differ diff --git a/client/frontend/dist/assets/index-5j6ZJn19.js b/client/frontend/dist/assets/index-5j6ZJn19.js deleted file mode 100644 index dae65e4..0000000 --- a/client/frontend/dist/assets/index-5j6ZJn19.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const r of a.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&i(r)}).observe(document,{childList:!0,subtree:!0});function n(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(o){if(o.ep)return;o.ep=!0;const a=n(o);fetch(o.href,a)}})();function de(){}function Ti(t){return t()}function Gs(){return Object.create(null)}function Le(t){t.forEach(Ti)}function Gl(t){return typeof t=="function"}function dt(t,e){return t!=t?e==e:t!==e||t&&typeof t=="object"||typeof t=="function"}function Wi(t){return Object.keys(t).length===0}function Vl(t,...e){if(t==null){for(const i of e)i(void 0);return de}const n=t.subscribe(...e);return n.unsubscribe?()=>n.unsubscribe():n}function on(t){let e;return Vl(t,n=>e=n)(),e}function ve(t,e,n){t.$$.on_destroy.push(Vl(e,n))}function Ze(t,e,n){return t.set(n),e}function l(t,e){t.appendChild(e)}function K(t,e,n){t.insertBefore(e,n||null)}function F(t){t.parentNode&&t.parentNode.removeChild(t)}function c(t){return document.createElement(t)}function g(t){return document.createTextNode(t)}function h(){return g(" ")}function Ul(){return g("")}function W(t,e,n,i){return t.addEventListener(e,n,i),()=>t.removeEventListener(e,n,i)}function Gt(t){return function(e){return e.stopPropagation(),t.call(this,e)}}function s(t,e,n){n==null?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function Fi(t){let e;return{p(...n){e=n,e.forEach(i=>t.push(i))},r(){e.forEach(n=>t.splice(t.indexOf(n),1))}}}function Oi(t){return t===""?null:+t}function Ki(t){return Array.from(t.childNodes)}function b(t,e){e=""+e,t.data!==e&&(t.data=e)}function ie(t,e){t.value=e??""}function Vs(t,e,n,i){n==null?t.style.removeProperty(e):t.style.setProperty(e,n,"")}function xn(t,e,n){for(let i=0;ii.call(this,e))}const Kt=[],Vt=[];let Jt=[];const Bs=[],Vi=Promise.resolve();let zl=!1;function Bi(){zl||(zl=!0,Vi.then(qi))}function tl(t){Jt.push(t)}const ql=new Set;let Wt=0;function qi(){if(Wt!==0)return;const t=rn;do{try{for(;Wtt.indexOf(i)===-1?e.push(i):n.push(i)),n.forEach(i=>i()),Jt=e}const el=new Set;let vt;function Ui(){vt={r:0,c:[],p:vt}}function zi(){vt.r||Le(vt.c),vt=vt.p}function Ye(t,e){t&&t.i&&(el.delete(t),t.i(e))}function lt(t,e,n,i){if(t&&t.o){if(el.has(t))return;el.add(t),vt.c.push(()=>{el.delete(t),i&&(n&&t.d(1),i())}),t.o(e)}else i&&i()}function ft(t){return t?.length!==void 0?t:Array.from(t)}function ll(t,e){t.d(1),e.delete(t.key)}function sl(t,e,n,i,o,a,r,_,d,u,v,f){let m=t.length,j=a.length,w=m;const A={};for(;w--;)A[t[w].key]=w;const p=[],C=new Map,D=new Map,P=[];for(w=j;w--;){const I=f(o,a,w),J=n(I);let B=r.get(J);B?P.push(()=>B.p(I,e)):(B=u(J,I),B.c()),C.set(J,p[w]=B),J in A&&D.set(J,Math.abs(w-A[J]))}const k=new Set,N=new Set;function y(I){Ye(I,1),I.m(_,v),r.set(I.key,I),v=I.first,j--}for(;m&&j;){const I=p[j-1],J=t[m-1],B=I.key,S=J.key;I===J?(v=I.first,m--,j--):C.has(S)?!r.has(B)||k.has(B)?y(I):N.has(S)?m--:D.get(B)>D.get(S)?(N.add(B),y(I)):(k.add(S),m--):(d(J,r),m--)}for(;m--;){const I=t[m];C.has(I.key)||d(I,r)}for(;j;)y(p[j-1]);return Le(P),p}function Bt(t){t&&t.c()}function mt(t,e,n){const{fragment:i,after_update:o}=t.$$;i&&i.m(e,n),tl(()=>{const a=t.$$.on_mount.map(Ti).filter(Gl);t.$$.on_destroy?t.$$.on_destroy.push(...a):Le(a),t.$$.on_mount=[]}),o.forEach(tl)}function ht(t,e){const n=t.$$;n.fragment!==null&&(Hi(n.after_update),Le(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function Xi(t,e){t.$$.dirty[0]===-1&&(Kt.push(t),Bi(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{const w=j.length?j[0]:m;return u.ctx&&o(u.ctx[f],u.ctx[f]=w)&&(!u.skip_bound&&u.bound[f]&&u.bound[f](w),v&&Xi(t,f)),m}):[],u.update(),v=!0,Le(u.before_update),u.fragment=i?i(u.ctx):!1,e.target){if(e.hydrate){const f=Ki(e.target);u.fragment&&u.fragment.l(f),f.forEach(F)}else u.fragment&&u.fragment.c();e.intro&&Ye(t.$$.fragment),mt(t,e.target,e.anchor),qi()}an(d)}class wt{$$=void 0;$$set=void 0;$destroy(){ht(this,1),this.$destroy=de}$on(e,n){if(!Gl(n))return de;const i=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return i.push(n),()=>{const o=i.indexOf(n);o!==-1&&i.splice(o,1)}}$set(e){this.$$set&&!Wi(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}const Qi="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(Qi);const Ft=[];function Zi(t,e){return{subscribe:$e(t,e).subscribe}}function $e(t,e=de){let n;const i=new Set;function o(_){if(dt(t,_)&&(t=_,n)){const d=!Ft.length;for(const u of i)u[1](),Ft.push(u,t);if(d){for(let u=0;u{i.delete(u),i.size===0&&n&&(n(),n=null)}}return{set:o,update:a,subscribe:r}}function $i(t,e,n){const i=!Array.isArray(t),o=i?[t]:t;if(!o.every(Boolean))throw new Error("derived() expects stores as input, got a falsy value");const a=e.length<2;return Zi(n,(r,_)=>{let d=!1;const u=[];let v=0,f=de;const m=()=>{if(v)return;f();const w=e(i?u[0]:u,r,_);a?r(w):f=Gl(w)?w:de},j=o.map((w,A)=>Vl(w,p=>{u[A]=p,v&=~(1<{v|=1<[...i,{id:n,msg:t,type:e}]),setTimeout(()=>{Wl.update(i=>i.filter(o=>o.id!==n))},4e3),e==="error"&&no(t)}function no(t){if(!t){nl.set(null);return}nl.set({id:++to,msg:String(t)})}function un(){nl.set(null)}function Fl(t,e){return t&&t.id?"peer:"+t.id:"room:"+(e||"network")}function _n(){return Fl(on(cn),on(ze).room)}function Ql(t,e,n,i=!1,o="sent",a=!1,r=!1){const _=++eo;let d;a?d=Fl(null,on(ze).room):i?d=_n():r?d="peer:"+t:d=Fl(null,on(ze).room),Hl.update(f=>{const m=f[d]?[...f[d]]:[];return m.push({id:i&&t.startsWith("local_")?t:_,fromID:t,nickname:e,message:n,timestamp:Date.now(),isSelf:i,status:o,system:a}),{...f,[d]:m.length>Ys?m.slice(-Ys):m}});const u=on(Ce),v=_n();!i&&!(u==="chat"&&d===v)&&Xl.update(f=>({...f,[d]:(f[d]||0)+1}))}function lo(t){Ql("system","",t,!1,"sent",!0)}function Kl(t){Xl.update(e=>{const n={...e};return delete n[t],n})}function ln(t,e){const n=_n();Hl.update(i=>{const o=i[n];return o?{...i,[n]:o.map(a=>a.id===t?{...a,status:e}:a)}:i})}const so={subscribe(t){return Hl.subscribe(e=>{t(e[_n()]||[])})}},Jl={brand:"Entangled",tagline:"Private LAN for friends — anywhere.",networks:"Networks",network_count:"{n} network(s)",create_network:"Create Network",join_network:"Join Network",connecting:"Connecting...",peer_count:"{n} peer(s)",offline_click:"Offline — click to connect",saved:"Saved",no_networks:"No saved networks",no_networks_desc:"Create or join a network to get started",settings:"Settings",network_name:"Network name",password_opt:"Password (optional)",cancel:"Cancel",create:"Create",join:"Join",leave:"Leave network",remove:"Remove saved",delete_room:"Delete network",more_actions:"More actions",copy_invite:"Copy invite",paste_invite:"Have an invite?",invite_copied:"Invite copied",invite_placeholder:"server|room|password",server_addr:"Server Address",nickname:"Nickname",server_token:"Server token",server_token_desc:"optional shared secret (ENTANGLED_TOKEN)",connect:"Connect",disconnect:"Disconnect",connected:"Connected",server:"Server",virtual_ip:"Virtual IP",na:"N/A",reconnecting:"Reconnecting...",disconnected:"Disconnected",phase_dialing:"Dialing server…",phase_auth:"Authenticating…",phase_ready:"Ready",phase_error:"Error",room_chat:"Room Chat",chat:"Chat",col_nickname:"Nickname",col_ip:"Virtual IP",col_ping:"Ping",ping_peer:"Ping",ping_sent:"Pinging {name}…",error_ping:"Ping failed: {err}",col_path:"Path",col_status:"Status",status_online:"Online",status_offline:"Offline",path_p2p:"Direct",path_relay:"Relay",path_ws:"Fallback",path_unknown:"—",path_p2p_tip:"Peer-to-peer direct connection",path_relay_tip:"Traffic routed through the server relay",path_ws_tip:"WebSocket fallback path",you:"You",me:"me",no_peers:"No peers connected",no_peers_desc:"Create or join a network, then wait for friends",network:"Network",chat_with:"Chat with {name}",room_chat_title:"Room Chat — {room}",back:"Back",no_messages:"No messages yet",no_messages_desc:"Send a message to start the conversation",type_message:"Type a message...",send:"Send",send_disabled_no_room:"Join a network to chat",msg_truncated:"Message truncated to 1000 characters",retry:"Retry",today:"Today",yesterday:"Yesterday",you_self:"You",status_connected:"Connected",status_disconnected:"Disconnected",status_reconnecting:"Reconnecting…",status_relay_degraded:"Some peers on relay",status_server:"Server: {s}",status_ip:"IP: {ip}",status_network:"Network: {room}",status_peers:"Peers: {n}",version:"Entangled v{v}",settings_title:"Settings",section_connection:"CONNECTION",section_network:"ADVANCED NETWORK",section_system:"SYSTEM",section_appearance:"APPEARANCE",advanced_warn:"Only change these if you know what they do.",reconnect_apply:"Reconnect to apply",auto_connect:"Auto Connect",auto_connect_desc:"connect to last server on startup",auto_join:"Auto Join Last Room",auto_join_desc:"join last open room after connect (password rooms need a manual join after restart)",connection_mode:"Connection",connection_direct:"Direct (P2P)",connection_relay:"Relay (server)",connection_mode_desc:"Direct prefers peer-to-peer and uses relay only when needed. Relay forces all traffic through the server.",mtu:"MTU",mtu_desc:"Packet size. Wrong values can break games or cause lag.",dns_server:"DNS Server",dns_desc:"Optional DNS for the virtual network. Leave blank for system default.",socks5:"SOCKS5 Proxy",socks5_placeholder:"e.g. 127.0.0.1:10808",socks5_desc:"Proxies signaling (WebSocket) only — not peer/relay UDP. Reconnect to apply.",stun_server:"STUN Server",stun_desc:"Helps find your public address for direct links. Reconnect to apply.",start_windows:"Start with Windows",updates:"Updates",check_updates:"Check for updates",apply_update:"Update",update_checking:"Checking for updates…",update_latest:"You are up to date.",update_available:"Update available: v{v}",update_downloading:"Downloading update… the app will restart.",update_failed:"Update failed",confirm_update:"Download and install the update? The app will restart.",error_update_check:"Update check failed: ",error_update:"Update failed: ",notif_updating:"Installing update…",ui_scale:"UI Scale",ui_scale_desc:"Scales the whole interface. Use with window resize or F11 fullscreen.",theme:"Theme",theme_dark:"Dark",theme_light:"Light",language:"Language",save:"SAVE",reset:"RESET TO DEFAULTS",language_en:"English",language_ru:"Russian",language_zh:"Chinese",copied:"Copied",dismiss_error:"Dismiss",field_required:"Required",notif_created:"Network created: {name}",notif_joined:"Joined network: {name}",notif_left:"Left network",notif_removed:"Removed: {name}",notif_deleted:"Network deleted: {name}",notif_connected:"Connected to {addr}",notif_disconnected:"Disconnected",notif_settings_saved:"Settings saved",notif_reconnect_required:"Reconnect to apply proxy / STUN / token changes",notif_auto_join_skipped:'Auto-join skipped for "{name}" — enter the password to join',notif_need_config:"Set server & nickname in Connect page first",notif_room_deleted:"Network was deleted by owner",invite_no_password:"Invite copied without password (not stored). Add it before sharing.",error_create:"Failed to create network: {err}",error_join:"Failed to join: {err}",error_leave:"Failed to leave: {err}",error_remove:"Failed to remove: {err}",error_connect:"Connection failed: {err}",error_disconnect:"Error: {err}",error_send:"Failed to send: {err}",error_not_connected:"Not connected — cannot send message",error_invalid_invite:"Invalid invite: server and room are required",error_save:"Failed to save settings: {err}",confirm_disconnect:"Disconnect from server?",confirm_leave:"Leave the current network? It stays in your saved list.",confirm_leave_remove:"Leave and remove this network from your saved list?",confirm_remove:'Remove "{name}" from saved networks? (does not delete the network for others)',confirm_delete:'Permanently delete network "{name}" for everyone?',confirm_reset:"Reset all settings to defaults?"},io={brand:"Entangled",tagline:"Частная LAN для друзей — где угодно.",networks:"Сети",network_count:"{n} сет(ь/и/ей)",create_network:"Создать сеть",join_network:"Подключиться",connecting:"Подключение...",peer_count:"{n} участник(ов)",offline_click:"Не в сети — нажмите для подключения",saved:"Сохранено",no_networks:"Нет сохранённых сетей",no_networks_desc:"Создайте или войдите в сеть, чтобы начать",settings:"Настройки",network_name:"Название сети",password_opt:"Пароль (опционально)",cancel:"Отмена",create:"Создать",join:"Войти",leave:"Покинуть сеть",remove:"Убрать из списка",delete_room:"Удалить сеть",more_actions:"Ещё действия",copy_invite:"Копировать инвайт",paste_invite:"Есть инвайт?",invite_copied:"Инвайт скопирован",invite_placeholder:"сервер|комната|пароль",server_addr:"Адрес сервера",nickname:"Псевдоним",server_token:"Токен сервера",server_token_desc:"опциональный секрет (ENTANGLED_TOKEN)",connect:"Подключиться",disconnect:"Отключиться",connected:"Подключено",server:"Сервер",virtual_ip:"Виртуальный IP",na:"Н/Д",reconnecting:"Переподключение...",disconnected:"Отключено",phase_dialing:"Подключение к серверу…",phase_auth:"Аутентификация…",phase_ready:"Готово",phase_error:"Ошибка",room_chat:"Чат комнаты",chat:"Чат",col_nickname:"Псевдоним",col_ip:"Виртуальный IP",col_ping:"Пинг",ping_peer:"Пинг",ping_sent:"Пинг {name}…",error_ping:"Пинг не удался: {err}",col_path:"Путь",col_status:"Статус",status_online:"В сети",status_offline:"Не в сети",path_p2p:"Прямой",path_relay:"Релей",path_ws:"Запасной",path_unknown:"—",path_p2p_tip:"Прямое соединение peer-to-peer",path_relay_tip:"Трафик через серверный релей",path_ws_tip:"Запасной путь через WebSocket",you:"Вы",me:"я",no_peers:"Нет подключённых участников",no_peers_desc:"Создайте или войдите в сеть и дождитесь друзей",network:"Сеть",chat_with:"Чат с {name}",room_chat_title:"Чат комнаты — {room}",back:"Назад",no_messages:"Сообщений пока нет",no_messages_desc:"Отправьте сообщение, чтобы начать разговор",type_message:"Введите сообщение...",send:"Отправить",send_disabled_no_room:"Войдите в сеть, чтобы писать",msg_truncated:"Сообщение обрезано до 1000 символов",retry:"Повторить",today:"Сегодня",yesterday:"Вчера",you_self:"Вы",status_connected:"Подключено",status_disconnected:"Отключено",status_reconnecting:"Переподключение…",status_relay_degraded:"Часть пиров через релей",status_server:"Сервер: {s}",status_ip:"IP: {ip}",status_network:"Сеть: {room}",status_peers:"Участники: {n}",version:"Entangled v{v}",settings_title:"Настройки",section_connection:"ПОДКЛЮЧЕНИЕ",section_network:"ДОП. СЕТЬ",section_system:"СИСТЕМА",section_appearance:"ВНЕШНИЙ ВИД",advanced_warn:"Меняйте только если понимаете, что делаете.",reconnect_apply:"Переподключитесь, чтобы применить",auto_connect:"Автоподключение",auto_connect_desc:"подключаться к последнему серверу при запуске",auto_join:"Автовход в комнату",auto_join_desc:"входить в последнюю открытую комнату (с паролем — вручную после перезапуска)",connection_mode:"Соединение",connection_direct:"Прямое (P2P)",connection_relay:"Релей (сервер)",connection_mode_desc:"Прямое предпочитает P2P и использует релей только при необходимости. Релей гонит весь трафик через сервер.",mtu:"MTU",mtu_desc:"Размер пакета. Неверное значение ломает игры или даёт лаги.",dns_server:"DNS-сервер",dns_desc:"Опциональный DNS виртуальной сети. Пусто — системный по умолчанию.",socks5:"SOCKS5-прокси",socks5_placeholder:"напр. 127.0.0.1:10808",socks5_desc:"Только сигнальный WebSocket, не UDP пиров/релея. Переподключитесь, чтобы применить.",stun_server:"STUN-сервер",stun_desc:"Помогает найти публичный адрес. Переподключитесь, чтобы применить.",start_windows:"Запуск с Windows",updates:"Обновления",check_updates:"Проверить обновления",apply_update:"Обновить",update_checking:"Проверка обновлений…",update_latest:"У вас актуальная версия.",update_available:"Доступно обновление: v{v}",update_downloading:"Загрузка обновления… приложение перезапустится.",update_failed:"Ошибка обновления",confirm_update:"Скачать и установить обновление? Приложение перезапустится.",error_update_check:"Не удалось проверить обновления: ",error_update:"Не удалось обновить: ",notif_updating:"Установка обновления…",ui_scale:"Масштаб UI",ui_scale_desc:"Масштабирует весь интерфейс. Вместе с изменением размера окна или F11.",theme:"Тема",theme_dark:"Тёмная",theme_light:"Светлая",language:"Язык",save:"СОХРАНИТЬ",reset:"СБРОСИТЬ",language_en:"Английский",language_ru:"Русский",language_zh:"Китайский",copied:"Скопировано",dismiss_error:"Закрыть",field_required:"Обязательно",notif_created:"Сеть создана: {name}",notif_joined:"Подключились к сети: {name}",notif_left:"Покинули сеть",notif_removed:"Удалено: {name}",notif_deleted:"Сеть удалена: {name}",notif_connected:"Подключились к {addr}",notif_disconnected:"Отключились",notif_settings_saved:"Настройки сохранены",notif_reconnect_required:"Переподключитесь, чтобы применить прокси / STUN / токен",notif_auto_join_skipped:"Автовход пропущен для «{name}» — введите пароль вручную",notif_need_config:"Укажите сервер и псевдоним на странице подключения",notif_room_deleted:"Сеть удалена владельцем",invite_no_password:"Инвайт скопирован без пароля (он не хранится). Добавьте перед отправкой.",error_create:"Не удалось создать сеть: {err}",error_join:"Не удалось подключиться: {err}",error_leave:"Не удалось покинуть: {err}",error_remove:"Не удалось удалить: {err}",error_connect:"Ошибка подключения: {err}",error_disconnect:"Ошибка: {err}",error_send:"Не удалось отправить: {err}",error_not_connected:"Нет подключения — невозможно отправить сообщение",error_invalid_invite:"Неверный инвайт: нужны сервер и комната",error_save:"Не удалось сохранить настройки: {err}",confirm_disconnect:"Отключиться от сервера?",confirm_leave:"Покинуть текущую сеть? Она останется в сохранённых.",confirm_leave_remove:"Покинуть и убрать сеть из сохранённых?",confirm_remove:'Убрать "{name}" из сохранённых? (сеть для других не удалится)',confirm_delete:'Навсегда удалить сеть "{name}" для всех?',confirm_reset:"Сбросить все настройки?"},oo={brand:"Entangled",tagline:"朋友的私人局域网 — 随时随地。",networks:"网络",network_count:"{n} 个网络",create_network:"创建网络",join_network:"加入网络",connecting:"连接中...",peer_count:"{n} 个节点",offline_click:"离线 — 点击连接",saved:"已保存",no_networks:"暂无保存的网络",no_networks_desc:"创建或加入网络以开始",settings:"设置",network_name:"网络名称",password_opt:"密码(可选)",cancel:"取消",create:"创建",join:"加入",leave:"离开网络",remove:"移除已保存",delete_room:"删除网络",more_actions:"更多操作",copy_invite:"复制邀请",paste_invite:"有邀请码?",invite_copied:"邀请已复制",invite_placeholder:"服务器|房间|密码",server_addr:"服务器地址",nickname:"昵称",server_token:"服务器令牌",server_token_desc:"可选共享密钥(ENTANGLED_TOKEN)",connect:"连接",disconnect:"断开",connected:"已连接",server:"服务器",virtual_ip:"虚拟 IP",na:"无",reconnecting:"重新连接...",disconnected:"已断开",phase_dialing:"正在连接服务器…",phase_auth:"正在验证…",phase_ready:"就绪",phase_error:"错误",room_chat:"房间聊天",chat:"聊天",col_nickname:"昵称",col_ip:"虚拟 IP",col_ping:"延迟",ping_peer:"测速",ping_sent:"正在测速 {name}…",error_ping:"测速失败: {err}",col_path:"路径",col_status:"状态",status_online:"在线",status_offline:"离线",path_p2p:"直连",path_relay:"中继",path_ws:"备用",path_unknown:"—",path_p2p_tip:"点对点直连",path_relay_tip:"经服务器中继转发",path_ws_tip:"WebSocket 备用路径",you:"你",me:"我",no_peers:"暂无连接的节点",no_peers_desc:"创建或加入网络,然后等待好友",network:"网络",chat_with:"与 {name} 聊天",room_chat_title:"房间聊天 — {room}",back:"返回",no_messages:"暂无消息",no_messages_desc:"发送一条消息开始对话",type_message:"输入消息...",send:"发送",send_disabled_no_room:"加入网络后才能聊天",msg_truncated:"消息已截断至 1000 个字符",retry:"重试",today:"今天",yesterday:"昨天",you_self:"你",status_connected:"已连接",status_disconnected:"已断开",status_reconnecting:"重新连接…",status_relay_degraded:"部分节点经中继",status_server:"服务器: {s}",status_ip:"IP: {ip}",status_network:"网络: {room}",status_peers:"节点: {n}",version:"Entangled v{v}",settings_title:"设置",section_connection:"连接",section_network:"高级网络",section_system:"系统",section_appearance:"外观",advanced_warn:"仅在了解作用时更改这些选项。",reconnect_apply:"重新连接以生效",auto_connect:"自动连接",auto_connect_desc:"启动时自动连接到上次的服务器",auto_join:"自动加入房间",auto_join_desc:"连接后自动加入上次的开放房间(有密码的房间重启后需手动加入)",connection_mode:"连接方式",connection_direct:"直连 (P2P)",connection_relay:"中继 (服务器)",connection_mode_desc:"直连优先点对点,必要时才用中继。中继模式强制全部流量经服务器。",mtu:"MTU",mtu_desc:"数据包大小。错误值可能导致游戏异常或卡顿。",dns_server:"DNS 服务器",dns_desc:"虚拟网络可选 DNS。留空则使用系统默认。",socks5:"SOCKS5 代理",socks5_placeholder:"例如 127.0.0.1:10808",socks5_desc:"仅代理信令 WebSocket,不含节点/中继 UDP。重新连接以生效。",stun_server:"STUN 服务器",stun_desc:"用于发现公网地址以便直连。重新连接以生效。",start_windows:"开机自启",updates:"更新",check_updates:"检查更新",apply_update:"更新",update_checking:"正在检查更新…",update_latest:"已是最新版本。",update_available:"有可用更新:v{v}",update_downloading:"正在下载更新…应用将重启。",update_failed:"更新失败",confirm_update:"下载并安装更新?应用将重启。",error_update_check:"检查更新失败:",error_update:"更新失败:",notif_updating:"正在安装更新…",ui_scale:"界面缩放",ui_scale_desc:"缩放整个界面。可配合窗口调整大小或 F11 全屏使用。",theme:"主题",theme_dark:"深色",theme_light:"浅色",language:"语言",save:"保存",reset:"重置为默认",language_en:"英语",language_ru:"俄语",language_zh:"中文",copied:"已复制",dismiss_error:"关闭",field_required:"必填",notif_created:"网络已创建: {name}",notif_joined:"已加入网络: {name}",notif_left:"已离开网络",notif_removed:"已删除: {name}",notif_deleted:"网络已删除: {name}",notif_connected:"已连接到 {addr}",notif_disconnected:"已断开连接",notif_settings_saved:"设置已保存",notif_reconnect_required:"请重新连接以使代理 / STUN / 令牌生效",notif_auto_join_skipped:"已跳过自动加入「{name}」— 请输入密码后手动加入",notif_need_config:"请先在连接页面设置服务器和昵称",notif_room_deleted:"网络已被所有者删除",invite_no_password:"邀请已复制但无密码(密码不存储)。分享前请自行添加。",error_create:"创建网络失败: {err}",error_join:"加入失败: {err}",error_leave:"离开失败: {err}",error_remove:"删除失败: {err}",error_connect:"连接失败: {err}",error_disconnect:"错误: {err}",error_send:"发送失败: {err}",error_not_connected:"未连接 — 无法发送消息",error_invalid_invite:"无效邀请:需要服务器和房间名",error_save:"保存设置失败: {err}",confirm_disconnect:"确定断开服务器连接?",confirm_leave:"离开当前网络?仍保留在已保存列表中。",confirm_leave_remove:"离开并从已保存列表中移除?",confirm_remove:'从已保存网络中移除 "{name}"?(不会删除他人的网络)',confirm_delete:'永久删除网络 "{name}"(对所有人)?',confirm_reset:"确定重置所有设置?"},Zl={en:Jl,ru:io,zh:oo};function ao(){const t=localStorage.getItem("entangled_locale");if(t&&Zl[t])return t;const e=navigator.language?.slice(0,2);return e==="ru"?"ru":e==="zh"?"zh":"en"}const $l=$e(ao()),kt=$i($l,t=>{const e=Zl[t]||Jl;return new Proxy(e,{get(n,i){if(typeof i=="string")return n[i]??Jl[i]??i}})});function sn(t){Zl[t]&&(localStorage.setItem("entangled_locale",t),$l.set(t))}function ne(t,e){return t?String(t).replace(/\{(\w+)\}/g,(n,i)=>e&&e[i]!=null?e[i]:`{${i}}`):""}function Hs(t,e,n){const i=t.slice();return i[62]=e[n],i}function Xs(t){let e,n,i=t[16].no_networks+"",o,a,r,_=t[16].no_networks_desc+"",d,u;return{c(){e=c("div"),n=c("div"),o=g(i),a=h(),r=c("div"),d=g(_),u=h(),s(r,"class","empty-desc svelte-1oaanap"),s(e,"class","empty-state svelte-1oaanap")},m(v,f){K(v,e,f),l(e,n),l(n,o),l(e,a),l(e,r),l(r,d),l(e,u)},p(v,f){f[0]&65536&&i!==(i=v[16].no_networks+"")&&b(o,i),f[0]&65536&&_!==(_=v[16].no_networks_desc+"")&&b(d,_)},d(v){v&&F(e)}}}function ro(t){let e=t[16].saved+"",n;return{c(){n=g(e)},m(i,o){K(i,n,o)},p(i,o){o[0]&65536&&e!==(e=i[16].saved+"")&&b(n,e)},d(i){i&&F(n)}}}function co(t){let e=t[16].offline_click+"",n;return{c(){n=g(e)},m(i,o){K(i,n,o)},p(i,o){o[0]&65536&&e!==(e=i[16].offline_click+"")&&b(n,e)},d(i){i&&F(n)}}}function uo(t){let e=ne(t[16].peer_count,{n:t[18].peerCount})+"",n;return{c(){n=g(e)},m(i,o){K(i,n,o)},p(i,o){o[0]&327680&&e!==(e=ne(i[16].peer_count,{n:i[18].peerCount})+"")&&b(n,e)},d(i){i&&F(n)}}}function _o(t){let e=t[16].connecting+"",n;return{c(){n=g(e)},m(i,o){K(i,n,o)},p(i,o){o[0]&65536&&e!==(e=i[16].connecting+"")&&b(n,e)},d(i){i&&F(n)}}}function Qs(t){let e,n,i=t[16].copy_invite+"",o,a,r,_,d=t[16].remove+"",u,v,f,m;function j(...C){return t[40](t[62],...C)}let w=t[18].room===t[62].name&&Zs(t);function A(...C){return t[42](t[62],...C)}let p=t[18].room===t[62].name&&t[18].isOwner&&$s(t);return{c(){e=c("div"),n=c("button"),o=g(i),a=h(),w&&w.c(),r=h(),_=c("button"),u=g(d),v=h(),p&&p.c(),s(n,"type","button"),s(n,"role","menuitem"),s(n,"class","menu-item svelte-1oaanap"),s(_,"type","button"),s(_,"role","menuitem"),s(_,"class","menu-item danger svelte-1oaanap"),s(e,"class","overflow-menu svelte-1oaanap"),s(e,"role","menu"),s(e,"tabindex","-1")},m(C,D){K(C,e,D),l(e,n),l(n,o),l(e,a),w&&w.m(e,null),l(e,r),l(e,_),l(_,u),l(e,v),p&&p.m(e,null),f||(m=[W(n,"click",j),W(_,"click",A),W(e,"click",Gt(t[37])),W(e,"keydown",Gt(t[38]))],f=!0)},p(C,D){t=C,D[0]&65536&&i!==(i=t[16].copy_invite+"")&&b(o,i),t[18].room===t[62].name?w?w.p(t,D):(w=Zs(t),w.c(),w.m(e,r)):w&&(w.d(1),w=null),D[0]&65536&&d!==(d=t[16].remove+"")&&b(u,d),t[18].room===t[62].name&&t[18].isOwner?p?p.p(t,D):(p=$s(t),p.c(),p.m(e,null)):p&&(p.d(1),p=null)},d(C){C&&F(e),w&&w.d(),p&&p.d(),f=!1,Le(m)}}}function Zs(t){let e,n=t[16].leave+"",i,o,a;return{c(){e=c("button"),i=g(n),s(e,"type","button"),s(e,"role","menuitem"),s(e,"class","menu-item svelte-1oaanap")},m(r,_){K(r,e,_),l(e,i),o||(a=W(e,"click",t[41]),o=!0)},p(r,_){_[0]&65536&&n!==(n=r[16].leave+"")&&b(i,n)},d(r){r&&F(e),o=!1,a()}}}function $s(t){let e,n=t[16].delete_room+"",i,o,a;function r(..._){return t[43](t[62],..._)}return{c(){e=c("button"),i=g(n),s(e,"type","button"),s(e,"role","menuitem"),s(e,"class","menu-item danger svelte-1oaanap")},m(_,d){K(_,e,d),l(e,i),o||(a=W(e,"click",r),o=!0)},p(_,d){t=_,d[0]&65536&&n!==(n=t[16].delete_room+"")&&b(i,n)},d(_){_&&F(e),o=!1,a()}}}function xs(t,e){let n,i,o,a,r,_=e[62].name+"",d,u,v,f,m,j,w,A,p,C,D,P,k,N,y,I;function J(V,Q){return V[11]===V[62].name?_o:V[18].room===V[62].name?uo:V[18].connected?ro:co}let B=J(e),S=B(e);function E(...V){return e[39](e[62],...V)}let M=e[13]===e[62].name&&Qs(e);function L(){return e[44](e[62])}function q(...V){return e[45](e[62],...V)}return{key:t,first:null,c(){n=c("div"),i=c("div"),o=h(),a=c("div"),r=c("div"),d=g(_),u=h(),v=c("div"),S.c(),f=h(),m=c("div"),j=c("button"),w=g("⋯"),D=h(),M&&M.c(),P=h(),s(i,"class","network-indicator svelte-1oaanap"),s(i,"aria-hidden","true"),ke(i,"active-dot",e[18].room===e[62].name),s(r,"class","network-name svelte-1oaanap"),s(v,"class","network-meta svelte-1oaanap"),s(a,"class","network-info svelte-1oaanap"),s(j,"type","button"),s(j,"class","menu-btn svelte-1oaanap"),s(j,"aria-haspopup","menu"),s(j,"aria-expanded",A=e[13]===e[62].name),s(j,"aria-label",p=e[16].more_actions),s(j,"title",C=e[16].more_actions),s(m,"class","row-actions svelte-1oaanap"),s(n,"class","network-item svelte-1oaanap"),s(n,"role","button"),s(n,"tabindex","0"),s(n,"aria-current",k=e[18].room===e[62].name?"true":void 0),s(n,"aria-label",N=e[62].name),ke(n,"active",e[18].room===e[62].name),ke(n,"disabled",!e[18].connected&&!e[11]),this.first=n},m(V,Q){K(V,n,Q),l(n,i),l(n,o),l(n,a),l(a,r),l(r,d),l(a,u),l(a,v),S.m(v,null),l(n,f),l(n,m),l(m,j),l(j,w),l(m,D),M&&M.m(m,null),l(n,P),y||(I=[W(j,"click",E),W(n,"click",L),W(n,"keydown",q)],y=!0)},p(V,Q){e=V,Q[0]&262145&&ke(i,"active-dot",e[18].room===e[62].name),Q[0]&1&&_!==(_=e[62].name+"")&&b(d,_),B===(B=J(e))&&S?S.p(e,Q):(S.d(1),S=B(e),S&&(S.c(),S.m(v,null))),Q[0]&8193&&A!==(A=e[13]===e[62].name)&&s(j,"aria-expanded",A),Q[0]&65536&&p!==(p=e[16].more_actions)&&s(j,"aria-label",p),Q[0]&65536&&C!==(C=e[16].more_actions)&&s(j,"title",C),e[13]===e[62].name?M?M.p(e,Q):(M=Qs(e),M.c(),M.m(m,null)):M&&(M.d(1),M=null),Q[0]&262145&&k!==(k=e[18].room===e[62].name?"true":void 0)&&s(n,"aria-current",k),Q[0]&1&&N!==(N=e[62].name)&&s(n,"aria-label",N),Q[0]&262145&&ke(n,"active",e[18].room===e[62].name),Q[0]&264192&&ke(n,"disabled",!e[18].connected&&!e[11])},d(V){V&&F(n),S.d(),M&&M.d(),y=!1,Le(I)}}}function ei(t){let e,n,i,o=t[16].create_network+"",a,r,_,d=t[16].network_name+"",u,v,f,m,j,w,A,p,C=t[16].password_opt+"",D,P,k,N,y,I,J,B,S=t[16].cancel+"",E,M,L,q=t[16].create+"",V,Q,ce,O=t[9]&&ti(t),Y=t[7]&&ni(t);return{c(){e=c("div"),n=c("div"),i=c("h3"),a=g(o),r=h(),_=c("label"),u=g(d),v=h(),f=c("input"),w=h(),O&&O.c(),A=h(),p=c("label"),D=g(C),P=h(),k=c("input"),y=h(),Y&&Y.c(),I=h(),J=c("div"),B=c("button"),E=g(S),M=h(),L=c("button"),V=g(q),s(i,"id","create-title"),s(i,"class","svelte-1oaanap"),s(_,"class","sr-only svelte-1oaanap"),s(_,"for","create-name"),s(f,"id","create-name"),s(f,"type","text"),s(f,"placeholder",m=t[16].network_name),s(f,"aria-invalid",j=!!t[9]),s(f,"class","svelte-1oaanap"),ke(f,"invalid",t[9]),s(p,"class","sr-only svelte-1oaanap"),s(p,"for","create-pass"),s(k,"id","create-pass"),s(k,"type","password"),s(k,"placeholder",N=t[16].password_opt),s(k,"class","svelte-1oaanap"),s(B,"class","btn svelte-1oaanap"),s(L,"class","btn primary svelte-1oaanap"),s(J,"class","modal-actions svelte-1oaanap"),s(n,"class","modal svelte-1oaanap"),s(n,"role","dialog"),s(n,"aria-modal","true"),s(n,"aria-labelledby","create-title"),s(e,"class","modal-overlay svelte-1oaanap"),s(e,"role","presentation")},m(U,z){K(U,e,z),l(e,n),l(n,i),l(i,a),l(n,r),l(n,_),l(_,u),l(n,v),l(n,f),ie(f,t[3]),l(n,w),O&&O.m(n,null),l(n,A),l(n,p),l(p,D),l(n,P),l(n,k),ie(k,t[4]),l(n,y),Y&&Y.m(n,null),l(n,I),l(n,J),l(J,B),l(B,E),l(J,M),l(J,L),l(L,V),t[49](n),Q||(ce=[W(f,"input",t[47]),W(k,"input",t[48]),W(B,"click",t[20]),W(L,"click",t[23]),W(n,"click",Gt(t[35])),W(n,"keydown",Gt(t[36])),W(e,"click",t[20]),W(e,"keydown",t[50])],Q=!0)},p(U,z){z[0]&65536&&o!==(o=U[16].create_network+"")&&b(a,o),z[0]&65536&&d!==(d=U[16].network_name+"")&&b(u,d),z[0]&65536&&m!==(m=U[16].network_name)&&s(f,"placeholder",m),z[0]&512&&j!==(j=!!U[9])&&s(f,"aria-invalid",j),z[0]&8&&f.value!==U[3]&&ie(f,U[3]),z[0]&512&&ke(f,"invalid",U[9]),U[9]?O?O.p(U,z):(O=ti(U),O.c(),O.m(n,A)):O&&(O.d(1),O=null),z[0]&65536&&C!==(C=U[16].password_opt+"")&&b(D,C),z[0]&65536&&N!==(N=U[16].password_opt)&&s(k,"placeholder",N),z[0]&16&&k.value!==U[4]&&ie(k,U[4]),U[7]?Y?Y.p(U,z):(Y=ni(U),Y.c(),Y.m(n,I)):Y&&(Y.d(1),Y=null),z[0]&65536&&S!==(S=U[16].cancel+"")&&b(E,S),z[0]&65536&&q!==(q=U[16].create+"")&&b(V,q)},d(U){U&&F(e),O&&O.d(),Y&&Y.d(),t[49](null),Q=!1,Le(ce)}}}function ti(t){let e,n;return{c(){e=c("span"),n=g(t[9]),s(e,"class","field-error svelte-1oaanap"),s(e,"role","alert")},m(i,o){K(i,e,o),l(e,n)},p(i,o){o[0]&512&&b(n,i[9])},d(i){i&&F(e)}}}function ni(t){let e,n;return{c(){e=c("div"),n=g(t[7]),s(e,"class","modal-error svelte-1oaanap"),s(e,"role","alert")},m(i,o){K(i,e,o),l(e,n)},p(i,o){o[0]&128&&b(n,i[7])},d(i){i&&F(e)}}}function li(t){let e,n,i,o=t[16].join_network+"",a,r,_,d=t[16].network_name+"",u,v,f,m,j,w,A,p,C=t[16].password_opt+"",D,P,k,N,y,I,J,B,S=t[16].cancel+"",E,M,L,q=t[16].join+"",V,Q,ce,O=t[10]&&si(t),Y=t[8]&&ii(t);return{c(){e=c("div"),n=c("div"),i=c("h3"),a=g(o),r=h(),_=c("label"),u=g(d),v=h(),f=c("input"),w=h(),O&&O.c(),A=h(),p=c("label"),D=g(C),P=h(),k=c("input"),y=h(),Y&&Y.c(),I=h(),J=c("div"),B=c("button"),E=g(S),M=h(),L=c("button"),V=g(q),s(i,"id","join-title"),s(i,"class","svelte-1oaanap"),s(_,"class","sr-only svelte-1oaanap"),s(_,"for","join-name"),s(f,"id","join-name"),s(f,"type","text"),s(f,"placeholder",m=t[16].network_name),s(f,"aria-invalid",j=!!t[10]),s(f,"class","svelte-1oaanap"),ke(f,"invalid",t[10]),s(p,"class","sr-only svelte-1oaanap"),s(p,"for","join-pass"),s(k,"id","join-pass"),s(k,"type","password"),s(k,"placeholder",N=t[16].password_opt),s(k,"class","svelte-1oaanap"),s(B,"class","btn svelte-1oaanap"),s(L,"class","btn primary svelte-1oaanap"),s(J,"class","modal-actions svelte-1oaanap"),s(n,"class","modal svelte-1oaanap"),s(n,"role","dialog"),s(n,"aria-modal","true"),s(n,"aria-labelledby","join-title"),s(e,"class","modal-overlay svelte-1oaanap"),s(e,"role","presentation")},m(U,z){K(U,e,z),l(e,n),l(n,i),l(i,a),l(n,r),l(n,_),l(_,u),l(n,v),l(n,f),ie(f,t[5]),l(n,w),O&&O.m(n,null),l(n,A),l(n,p),l(p,D),l(n,P),l(n,k),ie(k,t[6]),l(n,y),Y&&Y.m(n,null),l(n,I),l(n,J),l(J,B),l(B,E),l(J,M),l(J,L),l(L,V),t[54](n),Q||(ce=[W(f,"input",t[51]),W(k,"input",t[52]),W(B,"click",t[22]),W(L,"click",t[53]),W(n,"click",Gt(t[33])),W(n,"keydown",Gt(t[34])),W(e,"click",t[22]),W(e,"keydown",t[55])],Q=!0)},p(U,z){z[0]&65536&&o!==(o=U[16].join_network+"")&&b(a,o),z[0]&65536&&d!==(d=U[16].network_name+"")&&b(u,d),z[0]&65536&&m!==(m=U[16].network_name)&&s(f,"placeholder",m),z[0]&1024&&j!==(j=!!U[10])&&s(f,"aria-invalid",j),z[0]&32&&f.value!==U[5]&&ie(f,U[5]),z[0]&1024&&ke(f,"invalid",U[10]),U[10]?O?O.p(U,z):(O=si(U),O.c(),O.m(n,A)):O&&(O.d(1),O=null),z[0]&65536&&C!==(C=U[16].password_opt+"")&&b(D,C),z[0]&65536&&N!==(N=U[16].password_opt)&&s(k,"placeholder",N),z[0]&64&&k.value!==U[6]&&ie(k,U[6]),U[8]?Y?Y.p(U,z):(Y=ii(U),Y.c(),Y.m(n,I)):Y&&(Y.d(1),Y=null),z[0]&65536&&S!==(S=U[16].cancel+"")&&b(E,S),z[0]&65536&&q!==(q=U[16].join+"")&&b(V,q)},d(U){U&&F(e),O&&O.d(),Y&&Y.d(),t[54](null),Q=!1,Le(ce)}}}function si(t){let e,n;return{c(){e=c("span"),n=g(t[10]),s(e,"class","field-error svelte-1oaanap"),s(e,"role","alert")},m(i,o){K(i,e,o),l(e,n)},p(i,o){o[0]&1024&&b(n,i[10])},d(i){i&&F(e)}}}function ii(t){let e,n;return{c(){e=c("div"),n=g(t[8]),s(e,"class","modal-error svelte-1oaanap"),s(e,"role","alert")},m(i,o){K(i,e,o),l(e,n)},p(i,o){o[0]&256&&b(n,i[8])},d(i){i&&F(e)}}}function fo(t){let e,n,i,o=t[16].networks+"",a,r,_,d=ne(t[16].network_count,{n:t[0].length})+"",u,v,f,m,j=t[16].create_network+"",w,A,p,C,D=t[16].join_network+"",P,k,N,y,I=[],J=new Map,B,S,E,M=t[16].settings+"",L,q,V,Q,ce,O,Y=ft(t[0]);const U=Z=>Z[62].name;for(let Z=0;Zn(16,i=R)),ve(t,Ce,R=>n(17,o=R)),ve(t,ze,R=>n(18,a=R));let r=[],_=!1,d=!1,u="",v="",f="",m="",j="",w="",A="",p="",C="",D="",P="",k="network",N=null,y,I;fn(async()=>{n(0,r=await window.go.main.App.GetSavedRooms());const R=await window.go.main.App.LoadConfig();R.serverAddr&&(C=R.serverAddr),R.nickname&&(D=R.nickname),window.addEventListener("keydown",J),window.addEventListener("click",B)}),Mi(()=>{window.removeEventListener("keydown",J),window.removeEventListener("click",B)});function J(R){if(R.key==="Escape"){if(N){n(13,N=null);return}if(_){M();return}if(d){q();return}}}function B(){n(13,N=null)}async function S(){n(0,r=await window.go.main.App.GetSavedRooms())}function E(){n(7,j=""),n(9,A=""),n(1,_=!0),un()}function M(){n(1,_=!1),n(7,j=""),n(9,A="")}function L(){n(8,w=""),n(10,p=""),n(2,d=!0),un()}function q(){n(2,d=!1),n(8,w=""),n(10,p="")}async function V(){if(n(9,A=""),n(7,j=""),!u.trim()){n(9,A=i.field_required);return}try{await window.go.main.App.CreateRoom(u,v),ee(ne(i.notif_created,{name:u})),Ze(Ce,o="network",o),M(),n(3,u=""),n(4,v=""),await S()}catch(R){n(7,j=ne(i.error_create,{err:R})),ee(j,"error")}}async function Q(R,x){n(10,p=""),n(8,w="");const Se=R||f,Re=x!==void 0?x:m;if(!Se.trim()){n(10,p=i.field_required);return}try{await window.go.main.App.JoinRoom(Se,Re),ee(ne(i.notif_joined,{name:Se})),Ze(Ce,o="network",o),q(),n(5,f=""),n(6,m=""),await S()}catch(we){n(8,w=ne(i.error_join,{err:we})),ee(w,"error"),d||(n(5,f=Se),n(6,m=""),n(2,d=!0))}}async function ce(){if(confirm(i.confirm_leave))try{await window.go.main.App.LeaveRoom(),ee(i.notif_left),await S()}catch(R){ee(ne(i.error_leave,{err:R}),"error")}}async function O(R){const x=a.room===R;if(x){if(!confirm(i.confirm_leave_remove))return}else if(!confirm(ne(i.confirm_remove,{name:R})))return;try{x&&await window.go.main.App.LeaveRoom(),await window.go.main.App.RemoveSavedRoom(R),ee(ne(i.notif_removed,{name:R})),await S()}catch(Se){ee(ne(i.error_remove,{err:Se}),"error")}}async function Y(R){if(confirm(ne(i.confirm_delete,{name:R})))try{await window.go.main.App.DeleteRoom(R),await window.go.main.App.RemoveSavedRoom(R),ee(ne(i.notif_deleted,{name:R})),await S()}catch(x){ee(String(x),"error")}}async function U(R){try{const x=await window.go.main.App.FormatInvite(R.name,R.password||"");await window.go.main.App.CopyText(x);const Se=String(x).split("|"),Re=Se.length>=3&&Se[2]!=="";R.locked&&!Re?ee(i.invite_no_password,"info"):ee(i.invite_copied)}catch(x){ee(String(x),"error")}}async function z(){try{const R=await window.go.main.App.LoadConfig();R.serverAddr&&(C=R.serverAddr),R.nickname&&(D=R.nickname)}catch{}}async function te(R,x,Se){if(!P){n(11,P=R),n(13,N=null);try{await z();const Re=Se||C;if(!a.connected){if(!Re||!D){ee(i.notif_need_config,"error"),Ze(Ce,o="connect",o);return}const we=await window.go.main.App.Connect(Re,D);ze.set(we),await S()}await window.go.main.App.JoinRoom(R,x||""),Ze(Ce,o="network",o),await S()}catch(Re){const we=ne(i.error_join,{err:Re});ee(we,"error"),n(5,f=R),n(6,m=""),n(8,w=we),n(2,d=!0)}finally{n(11,P="")}}}function oe(R,x){(R.key==="Enter"||R.key===" ")&&(R.preventDefault(),te(x.name,x.password,x.server))}function Z(R,x){R.stopPropagation(),n(13,N=N===x?null:x)}function H(R,x){R.stopPropagation(),n(13,N=null),x()}function pe(R){zt.call(this,t,R)}function ue(R){zt.call(this,t,R)}function Ie(R){zt.call(this,t,R)}function Ne(R){zt.call(this,t,R)}function fe(R){zt.call(this,t,R)}function ye(R){zt.call(this,t,R)}const ge=(R,x)=>Z(x,R.name),je=(R,x)=>H(x,()=>U(R)),De=R=>H(R,ce),me=(R,x)=>H(x,()=>O(R.name)),le=(R,x)=>H(x,()=>Y(R.name)),We=R=>te(R.name,R.password,R.server),_e=(R,x)=>oe(x,R),he=()=>{o==="settings"?Ze(Ce,o=k,o):(n(12,k=o),Ze(Ce,o="settings",o))};function be(){u=this.value,n(3,u)}function X(){v=this.value,n(4,v)}function se(R){Vt[R?"unshift":"push"](()=>{y=R,n(14,y)})}const st=R=>R.key==="Escape"&&M();function Fe(){f=this.value,n(5,f)}function rt(){m=this.value,n(6,m)}const it=()=>Q();function re(R){Vt[R?"unshift":"push"](()=>{I=R,n(15,I)})}return[r,_,d,u,v,f,m,j,w,A,p,P,k,N,y,I,i,o,a,E,M,L,q,V,Q,ce,O,Y,U,te,oe,Z,H,pe,ue,Ie,Ne,fe,ye,ge,je,De,me,le,We,_e,he,be,X,se,st,Fe,rt,it,re,R=>R.key==="Escape"&&q()]}class vo extends wt{constructor(e){super(),gt(this,e,po,fo,dt,{},null,[-1,-1,-1])}}function oi(t,e,n){const i=t.slice();return i[24]=e[n],i}function ai(t){let e,n,i=t[5].chat+"",o,a,r,_,d,u=t[3]>0&&ri(t);return{c(){e=c("button"),n=g("[ "),o=g(i),a=g(` ] - `),u&&u.c(),s(e,"class","room-chat-btn svelte-mpjrp9"),s(e,"title",r=t[5].room_chat)},m(v,f){K(v,e,f),l(e,n),l(e,o),l(e,a),u&&u.m(e,null),_||(d=W(e,"click",t[8]),_=!0)},p(v,f){f&32&&i!==(i=v[5].chat+"")&&b(o,i),v[3]>0?u?u.p(v,f):(u=ri(v),u.c(),u.m(e,null)):u&&(u.d(1),u=null),f&32&&r!==(r=v[5].room_chat)&&s(e,"title",r)},d(v){v&&F(e),u&&u.d(),_=!1,d()}}}function ri(t){let e,n;return{c(){e=c("span"),n=g(t[3]),s(e,"class","badge svelte-mpjrp9")},m(i,o){K(i,e,o),l(e,n)},p(i,o){o&8&&b(n,i[3])},d(i){i&&F(e)}}}function ci(t){let e,n=t[5].status_relay_degraded+"",i;return{c(){e=c("span"),i=g(n),s(e,"class","relay-hint svelte-mpjrp9")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&32&&n!==(n=o[5].status_relay_degraded+"")&&b(i,n)},d(o){o&&F(e)}}}function ui(t){let e,n,i,o,a=t[5].no_peers+"",r,_,d,u=t[5].no_peers_desc+"",v,f;return{c(){e=c("div"),n=c("div"),n.textContent="[ . . . ]",i=h(),o=c("div"),r=g(a),_=h(),d=c("div"),v=g(u),f=h(),s(n,"class","empty-icon"),s(o,"class","empty-label svelte-mpjrp9"),s(d,"class","empty-desc svelte-mpjrp9"),s(e,"class","empty-peers svelte-mpjrp9")},m(m,j){K(m,e,j),l(e,n),l(e,i),l(e,o),l(o,r),l(e,_),l(e,d),l(d,v),l(e,f)},p(m,j){j&32&&a!==(a=m[5].no_peers+"")&&b(r,a),j&32&&u!==(u=m[5].no_peers_desc+"")&&b(v,u)},d(m){m&&F(e)}}}function _i(t){let e,n=t[11](t[24].id)+"",i;return{c(){e=c("span"),i=g(n),s(e,"class","badge svelte-mpjrp9")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&16&&n!==(n=o[11](o[24].id)+"")&&b(i,n)},d(o){o&&F(e)}}}function mo(t){let e;return{c(){e=g("...")},m(n,i){K(n,e,i)},p:de,d(n){n&&F(e)}}}function ho(t){let e=t[24].ping+"",n,i;return{c(){n=g(e),i=g("ms")},m(o,a){K(o,n,a),K(o,i,a)},p(o,a){a&16&&e!==(e=o[24].ping+"")&&b(n,e)},d(o){o&&(F(n),F(i))}}}function go(t){let e;return{c(){e=g("-")},m(n,i){K(n,e,i)},p:de,d(n){n&&F(e)}}}function fi(t,e){let n,i,o,a,r,_,d=(e[24].connected?e[5].status_online:e[5].status_offline)+"",u,v,f,m,j=(e[24].nickname||e[24].id)+"",w,A,p=e[11](e[24].id)>0,C,D,P=(e[24].virtualIP||e[5].na)+"",k,N,y,I,J,B=(e[24].path?e[12](e[24].path):e[5].path_unknown)+"",S,E,M,L,q,V,Q,ce,O,Y,U,z,te,oe,Z,H,pe,ue=p&&_i(e);function Ie(){return e[20](e[24])}function Ne(...me){return e[21](e[24],...me)}function fe(me,le){return me[24].connected?me[24].ping>=0&&!me[1][me[24].id]?ho:mo:go}let ye=fe(e),ge=ye(e);function je(){return e[22](e[24])}function De(){return e[23](e[24])}return{key:t,first:null,c(){n=c("div"),i=c("span"),o=c("span"),r=h(),_=c("span"),u=g(d),v=h(),f=c("span"),m=c("span"),w=g(j),A=h(),ue&&ue.c(),C=h(),D=c("span"),k=g(P),N=h(),y=c("span"),ge.c(),I=h(),J=c("span"),S=g(B),M=h(),L=c("span"),q=c("button"),V=g("[~]"),Y=h(),U=c("button"),z=g("[>]"),Z=h(),s(o,"class",a="sq "+(e[24].connected?"sq-online":"sq-offline")+" svelte-mpjrp9"),s(o,"aria-hidden","true"),s(_,"class","status-text svelte-mpjrp9"),s(i,"class","status-cell svelte-mpjrp9"),s(i,"role","cell"),s(m,"class","nickname svelte-mpjrp9"),s(f,"class","col-name"),s(f,"role","cell"),s(D,"class","col-ip clickable svelte-mpjrp9"),s(D,"role","button"),s(D,"tabindex","0"),s(y,"class","col-ping svelte-mpjrp9"),s(y,"role","cell"),ke(y,"probing",e[24].connected&&(e[24].ping<0||e[1][e[24].id])),s(J,"class","col-path svelte-mpjrp9"),s(J,"role","cell"),s(J,"title",E=e[13](e[24].path)),s(q,"class","row-btn svelte-mpjrp9"),q.disabled=Q=!e[24].connected||!!e[1][e[24].id],s(q,"title",ce=e[5].ping_peer),s(q,"aria-label",O=e[5].ping_peer),s(U,"class","row-btn svelte-mpjrp9"),s(U,"title",te=e[5].chat+" "+(e[24].nickname||e[24].id)),s(U,"aria-label",oe=e[5].chat+" "+(e[24].nickname||e[24].id)),s(L,"class","col-action svelte-mpjrp9"),s(L,"role","cell"),s(n,"class","table-row svelte-mpjrp9"),s(n,"role","row"),this.first=n},m(me,le){K(me,n,le),l(n,i),l(i,o),l(i,r),l(i,_),l(_,u),l(n,v),l(n,f),l(f,m),l(m,w),l(f,A),ue&&ue.m(f,null),l(n,C),l(n,D),l(D,k),l(n,N),l(n,y),ge.m(y,null),l(n,I),l(n,J),l(J,S),l(n,M),l(n,L),l(L,q),l(q,V),l(L,Y),l(L,U),l(U,z),l(n,Z),H||(pe=[W(D,"click",Ie),W(D,"keydown",Ne),W(q,"click",je),W(U,"click",De)],H=!0)},p(me,le){e=me,le&16&&a!==(a="sq "+(e[24].connected?"sq-online":"sq-offline")+" svelte-mpjrp9")&&s(o,"class",a),le&48&&d!==(d=(e[24].connected?e[5].status_online:e[5].status_offline)+"")&&b(u,d),le&16&&j!==(j=(e[24].nickname||e[24].id)+"")&&b(w,j),le&16&&(p=e[11](e[24].id)>0),p?ue?ue.p(e,le):(ue=_i(e),ue.c(),ue.m(f,null)):ue&&(ue.d(1),ue=null),le&48&&P!==(P=(e[24].virtualIP||e[5].na)+"")&&b(k,P),ye===(ye=fe(e))&&ge?ge.p(e,le):(ge.d(1),ge=ye(e),ge&&(ge.c(),ge.m(y,null))),le&18&&ke(y,"probing",e[24].connected&&(e[24].ping<0||e[1][e[24].id])),le&48&&B!==(B=(e[24].path?e[12](e[24].path):e[5].path_unknown)+"")&&b(S,B),le&16&&E!==(E=e[13](e[24].path))&&s(J,"title",E),le&18&&Q!==(Q=!e[24].connected||!!e[1][e[24].id])&&(q.disabled=Q),le&32&&ce!==(ce=e[5].ping_peer)&&s(q,"title",ce),le&32&&O!==(O=e[5].ping_peer)&&s(q,"aria-label",O),le&48&&te!==(te=e[5].chat+" "+(e[24].nickname||e[24].id))&&s(U,"title",te),le&48&&oe!==(oe=e[5].chat+" "+(e[24].nickname||e[24].id))&&s(U,"aria-label",oe)},d(me){me&&F(n),ue&&ue.d(),ge.d(),H=!1,Le(pe)}}}function wo(t){let e,n,i,o=(t[0].room||t[5].network)+"",a,r,_,d=(t[0].virtualIP||t[5].na)+"",u,v,f,m,j,w,A,p,C,D,P,k=t[5].col_nickname+"",N,y,I,J=t[5].col_ip+"",B,S,E,M=t[5].col_ping+"",L,q,V,Q=t[5].col_path+"",ce,O,Y,U,z,te,oe,Z,H,pe=t[5].status_online+"",ue,Ie,Ne,fe,ye=(t[6].nickname||t[5].you)+"",ge,je,De,me=t[5].me+"",le,We,_e,he=(t[0].virtualIP||t[5].na)+"",be,X,se,st,Fe,rt,it,re,Ae=[],R=new Map,x,Se,Re,we=t[0].room&&ai(t),Ee=t[2]&&ci(t),Ke=ft(t[4]);const bt=$=>$[24].id;for(let $=0;$n(5,r=E)),ve(t,Xl,E=>n(15,_=E)),ve(t,ze,E=>n(0,d=E)),ve(t,Yl,E=>n(16,u=E)),ve(t,Be,E=>n(6,v=E));let f={};function m(E){cn.set({id:E.id,nickname:E.nickname||E.id}),Kl("peer:"+E.id),Ce.set("chat")}function j(){cn.set(null),Kl("room:"+(d.room||"network")),Ce.set("chat")}async function w(E){if(E)try{await window.go.main.App.CopyText(E),ee(r.copied)}catch{}}async function A(E){if(!(!E?.id||!E.connected||f[E.id])){n(1,f={...f,[E.id]:!0});try{await window.go.main.App.PingPeer(E.id),ee(ne(r.ping_sent,{name:E.nickname||E.id}))}catch(M){ee(ne(r.error_ping,{err:M}),"error")}finally{setTimeout(()=>{const M={...f};delete M[E.id],n(1,f=M)},800)}}}function p(E){return _["peer:"+E]||0}function C(E){return E==="p2p"?r.path_p2p:E==="relay"?r.path_relay:E==="ws"?r.path_ws:r.path_unknown}function D(E){return E==="p2p"?r.path_p2p_tip:E==="relay"?r.path_relay_tip:E==="ws"?r.path_ws_tip:""}function P(E,M){(E.key==="Enter"||E.key===" ")&&(E.preventDefault(),w(M))}const k=()=>w(d.virtualIP),N=()=>w(d.virtualIP),y=E=>P(E,d.virtualIP),I=E=>w(E.virtualIP),J=(E,M)=>P(M,E.virtualIP),B=E=>A(E),S=E=>m(E);return t.$$.update=()=>{t.$$.dirty&65536&&n(4,i=[...u].sort((E,M)=>(E.nickname||"").localeCompare(M.nickname||""))),t.$$.dirty&32769&&n(3,o=_["room:"+(d.room||"network")]||0),t.$$.dirty&65536&&n(2,a=u.some(E=>E.path==="relay"||E.path==="ws"))},[d,f,a,o,i,r,v,m,j,w,A,p,C,D,P,_,u,k,N,y,I,J,B,S]}class bo extends wt{constructor(e){super(),gt(this,e,ko,wo,dt,{})}}function di(t){let e,n=t[2].status_relay_degraded+"",i,o;return{c(){e=c("span"),i=g(n),s(e,"class","degraded status-item svelte-1pzfkl"),s(e,"title",o=t[2].path_relay_tip)},m(a,r){K(a,e,r),l(e,i)},p(a,r){r&4&&n!==(n=a[2].status_relay_degraded+"")&&b(i,n),r&4&&o!==(o=a[2].path_relay_tip)&&s(e,"title",o)},d(a){a&&F(e)}}}function pi(t){let e,n=ne(t[2].status_server,{s:t[3].server})+"",i,o;return{c(){e=c("span"),i=g(n),s(e,"class","status-item status-server svelte-1pzfkl"),s(e,"title",o=t[3].server)},m(a,r){K(a,e,r),l(e,i)},p(a,r){r&12&&n!==(n=ne(a[2].status_server,{s:a[3].server})+"")&&b(i,n),r&8&&o!==(o=a[3].server)&&s(e,"title",o)},d(a){a&&F(e)}}}function vi(t){let e,n=ne(t[2].status_ip,{ip:t[3].virtualIP})+"",i,o;return{c(){e=c("span"),i=g(n),s(e,"class","status-item svelte-1pzfkl"),s(e,"title",o=t[3].virtualIP)},m(a,r){K(a,e,r),l(e,i)},p(a,r){r&12&&n!==(n=ne(a[2].status_ip,{ip:a[3].virtualIP})+"")&&b(i,n),r&8&&o!==(o=a[3].virtualIP)&&s(e,"title",o)},d(a){a&&F(e)}}}function mi(t){let e,n=ne(t[2].status_network,{room:t[3].room})+"",i,o;return{c(){e=c("span"),i=g(n),s(e,"class","status-item status-room svelte-1pzfkl"),s(e,"title",o=t[3].room)},m(a,r){K(a,e,r),l(e,i)},p(a,r){r&12&&n!==(n=ne(a[2].status_network,{room:a[3].room})+"")&&b(i,n),r&8&&o!==(o=a[3].room)&&s(e,"title",o)},d(a){a&&F(e)}}}function hi(t){let e,n=ne(t[2].status_peers,{n:t[3].peerCount})+"",i;return{c(){e=c("span"),i=g(n),s(e,"class","status-item svelte-1pzfkl")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&12&&n!==(n=ne(o[2].status_peers,{n:o[3].peerCount})+"")&&b(i,n)},d(o){o&&F(e)}}}function yo(t){let e,n,i,o,a,r,_,d,u,v,f,m,j,w,A=ne(t[2].version,{v:t[4]})+"",p,C=t[1]&&t[3].connected&&di(t),D=t[3].server&&pi(t),P=t[3].virtualIP&&vi(t),k=t[3].room&&mi(t),N=t[3].peerCount!==void 0&&hi(t);return{c(){e=c("footer"),n=c("div"),i=c("span"),o=h(),a=c("span"),r=g(t[0]),_=h(),C&&C.c(),d=h(),u=c("div"),D&&D.c(),v=h(),P&&P.c(),f=h(),k&&k.c(),m=h(),N&&N.c(),j=h(),w=c("span"),p=g(A),s(i,"class","sq svelte-1pzfkl"),s(i,"aria-hidden","true"),ke(i,"sq-connected",t[3].connected&&!t[3].reconnecting),ke(i,"sq-warn",t[3].reconnecting),s(a,"class","status-item svelte-1pzfkl"),s(a,"title",t[5]),s(n,"class","status-left svelte-1pzfkl"),s(w,"class","status-item status-ver svelte-1pzfkl"),s(u,"class","status-right svelte-1pzfkl"),s(e,"class","statusbar svelte-1pzfkl"),s(e,"role","status"),s(e,"aria-live","polite")},m(y,I){K(y,e,I),l(e,n),l(n,i),l(n,o),l(n,a),l(a,r),l(n,_),C&&C.m(n,null),l(e,d),l(e,u),D&&D.m(u,null),l(u,v),P&&P.m(u,null),l(u,f),k&&k.m(u,null),l(u,m),N&&N.m(u,null),l(u,j),l(u,w),l(w,p)},p(y,[I]){I&8&&ke(i,"sq-connected",y[3].connected&&!y[3].reconnecting),I&8&&ke(i,"sq-warn",y[3].reconnecting),I&1&&b(r,y[0]),I&32&&s(a,"title",y[5]),y[1]&&y[3].connected?C?C.p(y,I):(C=di(y),C.c(),C.m(n,null)):C&&(C.d(1),C=null),y[3].server?D?D.p(y,I):(D=pi(y),D.c(),D.m(u,v)):D&&(D.d(1),D=null),y[3].virtualIP?P?P.p(y,I):(P=vi(y),P.c(),P.m(u,f)):P&&(P.d(1),P=null),y[3].room?k?k.p(y,I):(k=mi(y),k.c(),k.m(u,m)):k&&(k.d(1),k=null),y[3].peerCount!==void 0?N?N.p(y,I):(N=hi(y),N.c(),N.m(u,j)):N&&(N.d(1),N=null),I&20&&A!==(A=ne(y[2].version,{v:y[4]})+"")&&b(p,A)},i:de,o:de,d(y){y&&F(e),C&&C.d(),D&&D.d(),P&&P.d(),k&&k.d(),N&&N.d()}}}function jo(t,e,n){let i,o,a,r,_,d;ve(t,kt,v=>n(2,r=v)),ve(t,ze,v=>n(3,_=v)),ve(t,Yl,v=>n(6,d=v));let u="1.0.0";return fn(async()=>{try{window.go?.main?.App?.GetVersion&&n(4,u=await window.go.main.App.GetVersion())}catch{}}),t.$$.update=()=>{t.$$.dirty&64&&n(1,i=(d||[]).some(v=>v.path==="relay"||v.path==="ws")),t.$$.dirty&12&&n(0,o=_.reconnecting?r.status_reconnecting:_.connected?r.status_connected:r.status_disconnected),t.$$.dirty&15&&n(5,a=i&&_.connected?r.status_relay_degraded:o)},[o,i,r,_,u,a,d]}class So extends wt{constructor(e){super(),gt(this,e,jo,yo,dt,{})}}function Eo(t){let e,n,i,o,a=t[0].server+"",r,_,d,u=t[1].server+"",v,f,m,j,w=t[0].virtual_ip+"",A,p,C,D=(t[1].virtualIP||t[0].na)+"",P,k,N,y=t[0].disconnect+"",I,J,B;function S(L,q){return L[1].reconnecting?Co:Po}let E=S(t),M=E(t);return{c(){e=c("div"),M.c(),n=h(),i=c("div"),o=c("span"),r=g(a),_=h(),d=c("span"),v=g(u),f=h(),m=c("div"),j=c("span"),A=g(w),p=h(),C=c("span"),P=g(D),k=h(),N=c("button"),I=g(y),s(d,"class","info-value svelte-1br9j49"),s(i,"class","info-line svelte-1br9j49"),s(C,"class","info-value svelte-1br9j49"),s(m,"class","info-line svelte-1br9j49"),s(N,"class","disconnect-btn svelte-1br9j49"),s(e,"class","connected-info svelte-1br9j49")},m(L,q){K(L,e,q),M.m(e,null),l(e,n),l(e,i),l(i,o),l(o,r),l(i,_),l(i,d),l(d,v),l(e,f),l(e,m),l(m,j),l(j,A),l(m,p),l(m,C),l(C,P),l(e,k),l(e,N),l(N,I),J||(B=W(N,"click",t[12]),J=!0)},p(L,q){E===(E=S(L))&&M?M.p(L,q):(M.d(1),M=E(L),M&&(M.c(),M.m(e,n))),q&1&&a!==(a=L[0].server+"")&&b(r,a),q&2&&u!==(u=L[1].server+"")&&b(v,u),q&1&&w!==(w=L[0].virtual_ip+"")&&b(A,w),q&3&&D!==(D=(L[1].virtualIP||L[0].na)+"")&&b(P,D),q&1&&y!==(y=L[0].disconnect+"")&&b(I,y)},d(L){L&&F(e),M.d(),J=!1,B()}}}function Ao(t){let e,n,i,o=t[0].brand+"",a,r,_,d=t[0].tagline+"",u,v,f,m,j=t[0].server_addr+"",w,A,p,C,D,P,k,N,y,I=t[0].nickname+"",J,B,S,E,M,L,q,V,Q=(t[5]?t[0].connecting:t[0].connect)+"",ce,O,Y,U,z,te=t[0].paste_invite+"",oe,Z,H,pe,ue=t[0].paste_invite+"",Ie,Ne,fe,ye,ge,je,De=t[0].join+"",me,le,We,_e=t[7].serverAddr&&gi(t),he=t[7].nickname&&wi(t),be=(t[5]||t[1].phase==="dialing"||t[1].phase==="auth")&&ki(t);return{c(){e=c("div"),n=c("div"),i=c("h1"),a=g(o),r=h(),_=c("p"),u=g(d),v=h(),f=c("div"),m=c("label"),w=g(j),A=h(),p=c("input"),P=h(),_e&&_e.c(),k=h(),N=c("div"),y=c("label"),J=g(I),B=h(),S=c("input"),L=h(),he&&he.c(),q=h(),V=c("button"),ce=g(Q),O=h(),be&&be.c(),Y=h(),U=c("details"),z=c("summary"),oe=g(te),Z=h(),H=c("div"),pe=c("label"),Ie=g(ue),Ne=h(),fe=c("input"),ge=h(),je=c("button"),me=g(De),s(i,"class","brand svelte-1br9j49"),s(_,"class","tagline svelte-1br9j49"),s(n,"class","brand-block svelte-1br9j49"),s(m,"for","server-input"),s(m,"class","svelte-1br9j49"),s(p,"id","server-input"),s(p,"type","text"),s(p,"placeholder","host:8080"),s(p,"aria-invalid",C=!!t[7].serverAddr),s(p,"aria-describedby",D=t[7].serverAddr?"server-err":void 0),s(p,"class","svelte-1br9j49"),ke(p,"invalid",t[7].serverAddr),s(f,"class","field svelte-1br9j49"),s(y,"for","nick-input"),s(y,"class","svelte-1br9j49"),s(S,"id","nick-input"),s(S,"type","text"),s(S,"placeholder","Player"),s(S,"aria-invalid",E=!!t[7].nickname),s(S,"aria-describedby",M=t[7].nickname?"nick-err":void 0),s(S,"class","svelte-1br9j49"),ke(S,"invalid",t[7].nickname),s(N,"class","field svelte-1br9j49"),s(V,"class","connect-btn svelte-1br9j49"),V.disabled=t[5],s(z,"class","svelte-1br9j49"),s(pe,"for","invite-input"),s(pe,"class","sr-only svelte-1br9j49"),s(fe,"id","invite-input"),s(fe,"type","text"),s(fe,"placeholder",ye=t[0].invite_placeholder),s(fe,"class","svelte-1br9j49"),s(je,"class","secondary-btn svelte-1br9j49"),je.disabled=t[5],s(H,"class","invite-block svelte-1br9j49"),s(U,"class","invite-disclosure svelte-1br9j49"),s(e,"class","connect-form svelte-1br9j49")},m(X,se){K(X,e,se),l(e,n),l(n,i),l(i,a),l(n,r),l(n,_),l(_,u),l(e,v),l(e,f),l(f,m),l(m,w),l(f,A),l(f,p),ie(p,t[2]),l(f,P),_e&&_e.m(f,null),l(e,k),l(e,N),l(N,y),l(y,J),l(N,B),l(N,S),ie(S,t[3]),l(N,L),he&&he.m(N,null),l(e,q),l(e,V),l(V,ce),l(e,O),be&&be.m(e,null),l(e,Y),l(e,U),l(U,z),l(z,oe),l(U,Z),l(U,H),l(H,pe),l(pe,Ie),l(H,Ne),l(H,fe),ie(fe,t[4]),l(H,ge),l(H,je),l(je,me),U.open=t[6],le||(We=[W(p,"input",t[13]),W(S,"input",t[14]),W(S,"keydown",t[11]),W(V,"click",t[9]),W(fe,"input",t[15]),W(je,"click",t[10]),W(U,"toggle",t[16])],le=!0)},p(X,se){se&1&&o!==(o=X[0].brand+"")&&b(a,o),se&1&&d!==(d=X[0].tagline+"")&&b(u,d),se&1&&j!==(j=X[0].server_addr+"")&&b(w,j),se&128&&C!==(C=!!X[7].serverAddr)&&s(p,"aria-invalid",C),se&128&&D!==(D=X[7].serverAddr?"server-err":void 0)&&s(p,"aria-describedby",D),se&4&&p.value!==X[2]&&ie(p,X[2]),se&128&&ke(p,"invalid",X[7].serverAddr),X[7].serverAddr?_e?_e.p(X,se):(_e=gi(X),_e.c(),_e.m(f,null)):_e&&(_e.d(1),_e=null),se&1&&I!==(I=X[0].nickname+"")&&b(J,I),se&128&&E!==(E=!!X[7].nickname)&&s(S,"aria-invalid",E),se&128&&M!==(M=X[7].nickname?"nick-err":void 0)&&s(S,"aria-describedby",M),se&8&&S.value!==X[3]&&ie(S,X[3]),se&128&&ke(S,"invalid",X[7].nickname),X[7].nickname?he?he.p(X,se):(he=wi(X),he.c(),he.m(N,null)):he&&(he.d(1),he=null),se&33&&Q!==(Q=(X[5]?X[0].connecting:X[0].connect)+"")&&b(ce,Q),se&32&&(V.disabled=X[5]),X[5]||X[1].phase==="dialing"||X[1].phase==="auth"?be?be.p(X,se):(be=ki(X),be.c(),be.m(e,Y)):be&&(be.d(1),be=null),se&1&&te!==(te=X[0].paste_invite+"")&&b(oe,te),se&1&&ue!==(ue=X[0].paste_invite+"")&&b(Ie,ue),se&1&&ye!==(ye=X[0].invite_placeholder)&&s(fe,"placeholder",ye),se&16&&fe.value!==X[4]&&ie(fe,X[4]),se&1&&De!==(De=X[0].join+"")&&b(me,De),se&32&&(je.disabled=X[5]),se&64&&(U.open=X[6])},d(X){X&&F(e),_e&&_e.d(),he&&he.d(),be&&be.d(),le=!1,Le(We)}}}function Po(t){let e,n,i,o,a=t[0].connected+"",r;return{c(){e=c("div"),n=c("span"),i=h(),o=c("span"),r=g(a),s(n,"class","sq sq-green svelte-1br9j49"),s(n,"aria-hidden","true"),s(e,"class","connected-line svelte-1br9j49"),s(e,"role","status")},m(_,d){K(_,e,d),l(e,n),l(e,i),l(e,o),l(o,r)},p(_,d){d&1&&a!==(a=_[0].connected+"")&&b(r,a)},d(_){_&&F(e)}}}function Co(t){let e,n,i,o,a=t[0].reconnecting+"",r;return{c(){e=c("div"),n=c("span"),i=h(),o=c("span"),r=g(a),s(n,"class","sq sq-warn svelte-1br9j49"),s(n,"aria-hidden","true"),s(e,"class","connected-line svelte-1br9j49"),s(e,"role","status")},m(_,d){K(_,e,d),l(e,n),l(e,i),l(e,o),l(o,r)},p(_,d){d&1&&a!==(a=_[0].reconnecting+"")&&b(r,a)},d(_){_&&F(e)}}}function gi(t){let e,n=t[7].serverAddr+"",i;return{c(){e=c("span"),i=g(n),s(e,"id","server-err"),s(e,"class","field-error svelte-1br9j49"),s(e,"role","alert")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].serverAddr+"")&&b(i,n)},d(o){o&&F(e)}}}function wi(t){let e,n=t[7].nickname+"",i;return{c(){e=c("span"),i=g(n),s(e,"id","nick-err"),s(e,"class","field-error svelte-1br9j49"),s(e,"role","alert")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].nickname+"")&&b(i,n)},d(o){o&&F(e)}}}function ki(t){let e,n=(t[8]||t[0].connecting)+"",i;return{c(){e=c("div"),i=g(n),s(e,"class","phase svelte-1br9j49"),s(e,"role","status")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&257&&n!==(n=(o[8]||o[0].connecting)+"")&&b(i,n)},d(o){o&&F(e)}}}function No(t){let e;function n(a,r){return a[1].connected?Eo:Ao}let i=n(t),o=i(t);return{c(){e=c("div"),o.c(),s(e,"class","connect-view svelte-1br9j49")},m(a,r){K(a,e,r),o.m(e,null)},p(a,[r]){i===(i=n(a))&&o?o.p(a,r):(o.d(1),o=i(a),o&&(o.c(),o.m(e,null)))},i:de,o:de,d(a){a&&F(e),o.d()}}}function Do(t,e,n){let i,o,a,r;ve(t,kt,y=>n(0,o=y)),ve(t,ze,y=>n(1,a=y)),ve(t,Be,y=>n(17,r=y));let _="",d="",u="",v=!1,f=!1,m={};fn(async()=>{const y=await window.go.main.App.LoadConfig();y.serverAddr&&n(2,_=y.serverAddr),y.nickname&&n(3,d=y.nickname),y&&Be.set({...r,...y}),y.autoConnect&&y.serverAddr&&y.nickname&&w()});function j(){const y={};return _.trim()||(y.serverAddr=o.field_required),d.trim()||(y.nickname=o.field_required),n(7,m=y),Object.keys(y).length===0}async function w(){if(!v&&j()){n(5,v=!0),un();try{const y=await window.go.main.App.Connect(_,d);ze.set(y),Ce.set("network"),ee(ne(o.notif_connected,{addr:_})),n(7,m={})}catch(y){ee(ne(o.error_connect,{err:y}),"error")}finally{n(5,v=!1)}}}async function A(){if(u.trim()){un();try{const y=await window.go.main.App.ParseInvite(u.trim());if(!y.server||!y.room){ee(o.error_invalid_invite,"error");return}if(n(2,_=y.server),!a.connected){if(!d.trim()){n(7,m={...m,nickname:o.field_required}),ee(o.notif_need_config,"error");return}n(5,v=!0);const I=await window.go.main.App.Connect(_,d);ze.set(I),n(5,v=!1)}await window.go.main.App.JoinRoom(y.room,y.password||""),ee(ne(o.notif_joined,{name:y.room})),Ce.set("network")}catch(y){ee(ne(o.error_join,{err:y}),"error"),n(5,v=!1)}}}function p(y){y.key==="Enter"&&(y.preventDefault(),w())}async function C(){if(confirm(o.confirm_disconnect))try{await window.go.main.App.Disconnect(),ze.set({connected:!1,reconnecting:!1,server:"",room:"",virtualIP:"",peerCount:0,isOwner:!1,phase:"idle"}),Ce.set("connect"),ee(o.notif_disconnected)}catch(y){ee(ne(o.error_disconnect,{err:y}),"error")}}function D(){_=this.value,n(2,_)}function P(){d=this.value,n(3,d)}function k(){u=this.value,n(4,u)}function N(){f=this.open,n(6,f)}return t.$$.update=()=>{t.$$.dirty&3&&n(8,i=a.phase==="dialing"?o.phase_dialing:a.phase==="auth"?o.phase_auth:a.phase==="ready"?o.phase_ready:a.phase==="error"?o.phase_error:"")},[o,a,_,d,u,v,f,m,i,w,A,p,C,D,P,k,N]}class Lo extends wt{constructor(e){super(),gt(this,e,Do,No,dt,{})}}function bi(t,e,n){const i=t.slice();return i[23]=e[n],i[25]=n,i}function yi(t){let e,n,i,o,a=t[0].no_messages+"",r,_,d,u=t[0].no_messages_desc+"",v;return{c(){e=c("div"),n=c("div"),n.textContent="[ . . . ]",i=h(),o=c("div"),r=g(a),_=h(),d=c("div"),v=g(u),s(n,"class","empty-chat-icon"),s(o,"class","empty-chat-label svelte-11c1d7e"),s(d,"class","empty-chat-desc svelte-11c1d7e"),s(e,"class","empty-chat svelte-11c1d7e")},m(f,m){K(f,e,m),l(e,n),l(e,i),l(e,o),l(o,r),l(e,_),l(e,d),l(d,v)},p(f,m){m&1&&a!==(a=f[0].no_messages+"")&&b(r,a),m&1&&u!==(u=f[0].no_messages_desc+"")&&b(v,u)},d(f){f&&F(e)}}}function ji(t){let e,n=t[12](t[23].timestamp)+"",i;return{c(){e=c("div"),i=g(n),s(e,"class","date-sep svelte-11c1d7e")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[12](o[23].timestamp)+"")&&b(i,n)},d(o){o&&F(e)}}}function Io(t){let e,n,i,o=(t[23].isSelf?t[6].nickname||t[0].you_self:t[23].nickname)+"",a,r,_,d,u,v=t[23].message+"",f,m,j=t[23].status==="pending"&&Si(),w=t[23].status==="failed"&&Ei(t);return{c(){e=c("div"),n=c("div"),i=c("span"),a=g(o),r=h(),j&&j.c(),_=h(),w&&w.c(),d=h(),u=c("div"),f=g(v),m=h(),s(i,"class","msg-nick"),s(n,"class","msg-meta svelte-11c1d7e"),s(u,"class","msg-body svelte-11c1d7e"),s(e,"class","msg svelte-11c1d7e"),ke(e,"self",t[23].isSelf)},m(A,p){K(A,e,p),l(e,n),l(n,i),l(i,a),l(n,r),j&&j.m(n,null),l(n,_),w&&w.m(n,null),l(e,d),l(e,u),l(u,f),l(e,m)},p(A,p){p&193&&o!==(o=(A[23].isSelf?A[6].nickname||A[0].you_self:A[23].nickname)+"")&&b(a,o),A[23].status==="pending"?j||(j=Si(),j.c(),j.m(n,_)):j&&(j.d(1),j=null),A[23].status==="failed"?w?w.p(A,p):(w=Ei(A),w.c(),w.m(n,null)):w&&(w.d(1),w=null),p&128&&v!==(v=A[23].message+"")&&b(f,v),p&128&&ke(e,"self",A[23].isSelf)},d(A){A&&F(e),j&&j.d(),w&&w.d()}}}function Ro(t){let e,n=t[23].message+"",i;return{c(){e=c("div"),i=g(n),s(e,"class","system-line svelte-11c1d7e")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[23].message+"")&&b(i,n)},d(o){o&&F(e)}}}function Si(t){let e;return{c(){e=c("span"),e.textContent="…",s(e,"class","msg-status")},m(n,i){K(n,e,i)},d(n){n&&F(e)}}}function Ei(t){let e,n=t[0].retry+"",i,o,a;function r(){return t[18](t[23])}return{c(){e=c("button"),i=g(n),s(e,"class","retry-btn svelte-11c1d7e")},m(_,d){K(_,e,d),l(e,i),o||(a=W(e,"click",r),o=!0)},p(_,d){t=_,d&1&&n!==(n=t[0].retry+"")&&b(i,n)},d(_){_&&F(e),o=!1,a()}}}function Ai(t,e){let n,i=Pi(e[23],e[25],e[7]),o,a,r=i&&ji(e);function _(v,f){return v[23].system?Ro:Io}let d=_(e),u=d(e);return{key:t,first:null,c(){n=Ul(),r&&r.c(),o=h(),u.c(),a=Ul(),this.first=n},m(v,f){K(v,n,f),r&&r.m(v,f),K(v,o,f),u.m(v,f),K(v,a,f)},p(v,f){e=v,f&128&&(i=Pi(e[23],e[25],e[7])),i?r?r.p(e,f):(r=ji(e),r.c(),r.m(o.parentNode,o)):r&&(r.d(1),r=null),d===(d=_(e))&&u?u.p(e,f):(u.d(1),u=d(e),u&&(u.c(),u.m(a.parentNode,a)))},d(v){v&&(F(n),F(o),F(a)),r&&r.d(v),u.d(v)}}}function To(t){let e,n,i,o,a,r,_,d,u,v,f,m=[],j=new Map,w,A,p,C,D,P,k,N=t[0].send+"",y,I,J,B,S=t[7].length===0&&yi(t),E=ft(t[7]);const M=L=>L[23].id;for(let L=0;Ln(15,a=S)),ve(t,kt,S=>n(0,r=S)),ve(t,cn,S=>n(16,_=S)),ve(t,Be,S=>n(6,d=S)),ve(t,Ce,S=>n(17,u=S)),ve(t,so,S=>n(7,v=S));let f="",m,j,w=!0;Gi(()=>{m&&w&&n(2,m.scrollTop=m.scrollHeight,m)});function A(){if(!m)return;w=m.scrollHeight-m.scrollTop-m.clientHeight<60}async function p(){const S=f.trim();if(!S)return;if(!a.connected){ee(r.error_not_connected,"error");return}if(!a.room){ee(r.send_disabled_no_room,"error");return}let E=S;const M=[...S];M.length>1e3&&(E=M.slice(0,1e3).join(""),ee(r.msg_truncated,"info")),n(1,f=""),j&&n(3,j.style.height="auto",j);const L="local_"+Date.now()+"_"+Math.random().toString(36).slice(2,7);Ql(L,d.nickname||r.you_self,E,!0,"pending");try{_&&_.id?await window.go.main.App.SendChat(_.id,E):await window.go.main.App.BroadcastChat(E),ln(L,"sent")}catch(q){ln(L,"failed"),ee(ne(r.error_send,{err:q}),"error")}}async function C(S){ln(S.id,"pending");try{_&&_.id?await window.go.main.App.SendChat(_.id,S.message):await window.go.main.App.BroadcastChat(S.message),ln(S.id,"sent")}catch(E){ln(S.id,"failed"),ee(ne(r.error_send,{err:E}),"error")}}function D(S){S.key==="Enter"&&!S.shiftKey&&(S.preventDefault(),p())}function P(S){const E=new Date(S),M=new Date,L=new Date(M.getFullYear(),M.getMonth(),M.getDate()),q=new Date(L-864e5),V=new Date(E.getFullYear(),E.getMonth(),E.getDate());return V.getTime()===L.getTime()?r.today:V.getTime()===q.getTime()?r.yesterday:E.toLocaleDateString([],{weekday:"long",month:"short",day:"numeric"})}function k(){j&&(n(3,j.style.height="auto",j),n(3,j.style.height=Math.min(j.scrollHeight,120)+"px",j))}function N(){cn.set(null),Ce.set("network")}const y=S=>C(S);function I(S){Vt[S?"unshift":"push"](()=>{m=S,n(2,m)})}function J(S){Vt[S?"unshift":"push"](()=>{j=S,n(3,j)})}function B(){f=this.value,n(1,f)}return t.$$.update=()=>{t.$$.dirty&131072&&u==="chat"&&Kl(_n()),t.$$.dirty&98305&&n(5,i=_?ne(r.chat_with,{name:_.nickname}):ne(r.room_chat_title,{room:a.room||r.network})),t.$$.dirty&32768&&n(4,o=a.connected&&!!a.room)},[r,f,m,j,o,i,d,v,A,p,C,D,P,k,N,a,_,u,y,I,J,B]}class Mo extends wt{constructor(e){super(),gt(this,e,Oo,To,dt,{})}}function Ci(t){let e,n=t[7].notif_reconnect_required+"",i;return{c(){e=c("p"),i=g(n),s(e,"class","reconnect-banner svelte-1pfl9w0"),s(e,"role","status")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].notif_reconnect_required+"")&&b(i,n)},d(o){o&&F(e)}}}function Ni(t){let e,n=t[7].apply_update+"",i,o,a;return{c(){e=c("button"),i=g(n),s(e,"class","btn btn-update svelte-1pfl9w0"),s(e,"type","button"),e.disabled=t[3]},m(r,_){K(r,e,_),l(e,i),o||(a=W(e,"click",t[10]),o=!0)},p(r,_){_&128&&n!==(n=r[7].apply_update+"")&&b(i,n),_&8&&(e.disabled=r[3])},d(r){r&&F(e),o=!1,a()}}}function qo(t){let e,n=t[7].update_failed+"",i,o,a;return{c(){e=c("p"),i=g(n),o=g(": "),a=g(t[6]),s(e,"class","hint reconnect-hint svelte-1pfl9w0")},m(r,_){K(r,e,_),l(e,i),l(e,o),l(e,a)},p(r,_){_&128&&n!==(n=r[7].update_failed+"")&&b(i,n),_&64&&b(a,r[6])},d(r){r&&F(e)}}}function Uo(t){let e,n=t[7].update_downloading+"",i;return{c(){e=c("p"),i=g(n),s(e,"class","hint svelte-1pfl9w0")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].update_downloading+"")&&b(i,n)},d(o){o&&F(e)}}}function zo(t){let e,n=ne(t[7].update_available,{v:t[5]})+"",i;return{c(){e=c("p"),i=g(n),s(e,"class","hint svelte-1pfl9w0")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&160&&n!==(n=ne(o[7].update_available,{v:o[5]})+"")&&b(i,n)},d(o){o&&F(e)}}}function Wo(t){let e,n=t[7].update_latest+"",i;return{c(){e=c("p"),i=g(n),s(e,"class","hint svelte-1pfl9w0")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].update_latest+"")&&b(i,n)},d(o){o&&F(e)}}}function Fo(t){let e,n=t[7].update_checking+"",i;return{c(){e=c("p"),i=g(n),s(e,"class","hint svelte-1pfl9w0")},m(o,a){K(o,e,a),l(e,i)},p(o,a){a&128&&n!==(n=o[7].update_checking+"")&&b(i,n)},d(o){o&&F(e)}}}function Ko(t){let e,n,i=t[7].settings_title+"",o,a,r,_,d,u,v,f=t[7].section_connection+"",m,j,w,A,p,C=t[7].server_addr+"",D,P,k,N,y,I,J=t[7].nickname+"",B,S,E,M,L,q,V=t[7].server_token+"",Q,ce,O,Y,U,z,te=t[7].reconnect_apply+"",oe,Z,H,pe,ue=t[7].auto_connect+"",Ie,Ne,fe,ye,ge,je=t[7].auto_connect_desc+"",De,me,le,We,_e=t[7].auto_join+"",he,be,X,se,st,Fe=t[7].auto_join_desc+"",rt,it,re,Ae,R,x=t[7].section_network+"",Se,Re,we,Ee,Ke=t[7].advanced_warn+"",bt,Pe,$,ae,Je=t[7].connection_mode+"",il,xl,xe,yt,dn=t[7].connection_direct+"",ol,jt,pn=t[7].connection_relay+"",al,es,vn,mn=t[7].connection_mode_desc+"",rl,ts,St,Yt,hn=t[7].mtu+"",cl,ns,Ge,ls,gn,wn=t[7].mtu_desc+"",ul,ss,Et,Ht,kn=t[7].dns_server+"",_l,is,et,os,bn,yn=t[7].dns_desc+"",fl,as,At,Xt,jn=t[7].socks5+"",dl,rs,He,Sn,cs,En,An=t[7].socks5_desc+"",pl,us,Pt,Qt,Pn=t[7].stun_server+"",vl,_s,tt,fs,Cn,Nn=t[7].stun_desc+"",ml,ds,Xe,Ct,ps,Dn=t[7].section_system+"",hl,vs,ms,Nt,Ln,In=t[7].start_windows+"",gl,hs,pt,gs,ot,Rn,Tn=t[7].updates+"",wl,ws,ct,On=t[7].check_updates+"",kl,ks,bs,ys,Ve,Dt,js,Mn=t[7].section_appearance+"",bl,Ss,Es,ut,Zt,qn=t[7].ui_scale+"",yl,As,Qe,Un,Ps,$t,zn=(t[0].uiScale??100)+"",jl,Cs,Ns,Wn,Fn=t[7].ui_scale_desc+"",Sl,Ds,Lt,Kn,Jn=t[7].theme+"",El,Ls,at,It,qe,Is,Gn=t[7].theme_dark+"",Al,Rs,Rt,Ue,Ts,Vn=t[7].theme_light+"",Pl,Bn,Os,Tt,xt,Yn=t[7].language+"",Cl,Ms,nt,Ot,Hn=t[7].language_en+"",Nl,Mt,Xn=t[7].language_ru+"",Dl,qt,Qn=t[7].language_zh+"",Ll,qs,Us,_t,en,Zn=t[7].save+"",Il,zs,tn,$n=t[7].reset+"",Rl,Ws,nn,Fs,Tl,Ol,Ml,Ks,Te=t[2]&&Ci(t),Oe=t[4]==="available"&&Ni(t);function Js(T,G){if(T[4]==="checking")return Fo;if(T[4]==="latest")return Wo;if(T[4]==="available")return zo;if(T[4]==="downloading")return Uo;if(T[4]==="error"&&T[6])return qo}let Ut=Js(t),Me=Ut&&Ut(t);return Ol=Fi(t[28][0]),{c(){e=c("div"),n=c("h2"),o=g(i),a=h(),r=c("div"),Te&&Te.c(),_=h(),d=c("div"),u=c("div"),v=g("─── "),m=g(f),j=g(" ───"),w=h(),A=c("div"),p=c("label"),D=g(C),P=h(),k=c("input"),N=h(),y=c("div"),I=c("label"),B=g(J),S=h(),E=c("input"),M=h(),L=c("div"),q=c("label"),Q=g(V),ce=h(),O=c("input"),U=h(),z=c("p"),oe=g(te),Z=h(),H=c("label"),pe=c("span"),Ie=g(ue),Ne=h(),fe=c("input"),ye=h(),ge=c("span"),De=g(je),me=h(),le=c("label"),We=c("span"),he=g(_e),be=h(),X=c("input"),se=h(),st=c("span"),rt=g(Fe),it=h(),re=c("div"),Ae=c("div"),R=g("─── "),Se=g(x),Re=g(" ───"),we=h(),Ee=c("p"),bt=g(Ke),Pe=h(),$=c("div"),ae=c("label"),il=g(Je),xl=h(),xe=c("select"),yt=c("option"),ol=g(dn),jt=c("option"),al=g(pn),es=h(),vn=c("p"),rl=g(mn),ts=h(),St=c("div"),Yt=c("label"),cl=g(hn),ns=h(),Ge=c("input"),ls=h(),gn=c("p"),ul=g(wn),ss=h(),Et=c("div"),Ht=c("label"),_l=g(kn),is=h(),et=c("input"),os=h(),bn=c("p"),fl=g(yn),as=h(),At=c("div"),Xt=c("label"),dl=g(jn),rs=h(),He=c("input"),cs=h(),En=c("p"),pl=g(An),us=h(),Pt=c("div"),Qt=c("label"),vl=g(Pn),_s=h(),tt=c("input"),fs=h(),Cn=c("p"),ml=g(Nn),ds=h(),Xe=c("div"),Ct=c("div"),ps=g("─── "),hl=g(Dn),vs=g(" ───"),ms=h(),Nt=c("label"),Ln=c("span"),gl=g(In),hs=h(),pt=c("input"),gs=h(),ot=c("div"),Rn=c("span"),wl=g(Tn),ws=h(),ct=c("button"),kl=g(On),ks=h(),Oe&&Oe.c(),bs=h(),Me&&Me.c(),ys=h(),Ve=c("div"),Dt=c("div"),js=g("─── "),bl=g(Mn),Ss=g(" ───"),Es=h(),ut=c("div"),Zt=c("label"),yl=g(qn),As=h(),Qe=c("input"),Ps=h(),$t=c("span"),jl=g(zn),Cs=g("%"),Ns=h(),Wn=c("p"),Sl=g(Fn),Ds=h(),Lt=c("div"),Kn=c("span"),El=g(Jn),Ls=h(),at=c("div"),It=c("label"),qe=c("input"),Is=h(),Al=g(Gn),Rs=h(),Rt=c("label"),Ue=c("input"),Ts=h(),Pl=g(Vn),Os=h(),Tt=c("div"),xt=c("label"),Cl=g(Yn),Ms=h(),nt=c("select"),Ot=c("option"),Nl=g(Hn),Mt=c("option"),Dl=g(Xn),qt=c("option"),Ll=g(Qn),Us=h(),_t=c("div"),en=c("button"),Il=g(Zn),zs=h(),tn=c("button"),Rl=g($n),Ws=h(),nn=c("div"),Fs=g("v"),Tl=g(t[1]),s(n,"class","settings-title svelte-1pfl9w0"),s(u,"class","section-title svelte-1pfl9w0"),s(p,"class","setting-label svelte-1pfl9w0"),s(p,"for","cfg-server"),s(k,"id","cfg-server"),s(k,"type","text"),s(k,"placeholder","vps.example.com:8080"),s(k,"class","svelte-1pfl9w0"),s(A,"class","setting-row svelte-1pfl9w0"),s(I,"class","setting-label svelte-1pfl9w0"),s(I,"for","cfg-nick"),s(E,"id","cfg-nick"),s(E,"type","text"),s(E,"placeholder","Your nickname"),s(E,"class","svelte-1pfl9w0"),s(y,"class","setting-row svelte-1pfl9w0"),s(q,"class","setting-label svelte-1pfl9w0"),s(q,"for","cfg-token"),s(O,"id","cfg-token"),s(O,"type","password"),s(O,"placeholder",Y=t[7].server_token_desc),s(O,"class","svelte-1pfl9w0"),s(L,"class","setting-row svelte-1pfl9w0"),s(z,"class","hint reconnect-hint svelte-1pfl9w0"),s(pe,"class","setting-label svelte-1pfl9w0"),s(fe,"type","checkbox"),s(fe,"class","svelte-1pfl9w0"),s(ge,"class","toggle-label svelte-1pfl9w0"),s(H,"class","setting-row toggle-row svelte-1pfl9w0"),s(We,"class","setting-label svelte-1pfl9w0"),s(X,"type","checkbox"),s(X,"class","svelte-1pfl9w0"),s(st,"class","toggle-label svelte-1pfl9w0"),s(le,"class","setting-row toggle-row svelte-1pfl9w0"),s(d,"class","section svelte-1pfl9w0"),s(Ae,"class","section-title svelte-1pfl9w0"),s(Ee,"class","hint svelte-1pfl9w0"),s(ae,"class","setting-label svelte-1pfl9w0"),s(ae,"for","cfg-conn"),yt.__value="direct",ie(yt,yt.__value),jt.__value="relay",ie(jt,jt.__value),s(xe,"id","cfg-conn"),s(xe,"class","svelte-1pfl9w0"),t[0].connectionMode===void 0&&tl(()=>t[21].call(xe)),s($,"class","setting-row svelte-1pfl9w0"),s(vn,"class","hint svelte-1pfl9w0"),s(Yt,"class","setting-label svelte-1pfl9w0"),s(Yt,"for","cfg-mtu"),s(Ge,"id","cfg-mtu"),s(Ge,"type","number"),s(Ge,"min","576"),s(Ge,"max","9000"),s(Ge,"placeholder","1500"),s(Ge,"class","svelte-1pfl9w0"),s(St,"class","setting-row svelte-1pfl9w0"),s(gn,"class","hint svelte-1pfl9w0"),s(Ht,"class","setting-label svelte-1pfl9w0"),s(Ht,"for","cfg-dns"),s(et,"id","cfg-dns"),s(et,"type","text"),s(et,"placeholder","system default"),s(et,"class","svelte-1pfl9w0"),s(Et,"class","setting-row svelte-1pfl9w0"),s(bn,"class","hint svelte-1pfl9w0"),s(Xt,"class","setting-label svelte-1pfl9w0"),s(Xt,"for","cfg-socks"),s(He,"id","cfg-socks"),s(He,"type","text"),s(He,"placeholder",Sn=t[7].socks5_placeholder),s(He,"class","svelte-1pfl9w0"),s(At,"class","setting-row svelte-1pfl9w0"),s(En,"class","hint reconnect-hint svelte-1pfl9w0"),s(Qt,"class","setting-label svelte-1pfl9w0"),s(Qt,"for","cfg-stun"),s(tt,"id","cfg-stun"),s(tt,"type","text"),s(tt,"placeholder","stun.l.google.com:19302"),s(tt,"class","svelte-1pfl9w0"),s(Pt,"class","setting-row svelte-1pfl9w0"),s(Cn,"class","hint reconnect-hint svelte-1pfl9w0"),s(re,"class","section svelte-1pfl9w0"),s(Ct,"class","section-title svelte-1pfl9w0"),s(Ln,"class","setting-label svelte-1pfl9w0"),s(pt,"type","checkbox"),s(pt,"class","svelte-1pfl9w0"),s(Nt,"class","setting-row toggle-row svelte-1pfl9w0"),s(Rn,"class","setting-label svelte-1pfl9w0"),s(ct,"class","btn btn-check svelte-1pfl9w0"),s(ct,"type","button"),ct.disabled=t[3],s(ot,"class","setting-row update-row svelte-1pfl9w0"),s(Xe,"class","section svelte-1pfl9w0"),s(Dt,"class","section-title svelte-1pfl9w0"),s(Zt,"class","setting-label svelte-1pfl9w0"),s(Zt,"for","cfg-scale"),s(Qe,"id","cfg-scale"),s(Qe,"type","range"),s(Qe,"min","75"),s(Qe,"max","150"),s(Qe,"step","5"),Qe.value=Un=t[0].uiScale??100,s(Qe,"class","svelte-1pfl9w0"),s($t,"class","scale-value svelte-1pfl9w0"),s(ut,"class","setting-row scale-row svelte-1pfl9w0"),s(Wn,"class","hint svelte-1pfl9w0"),s(Kn,"class","setting-label svelte-1pfl9w0"),s(qe,"type","radio"),qe.__value="dark",ie(qe,qe.__value),s(It,"class","radio-option svelte-1pfl9w0"),s(Ue,"type","radio"),Ue.__value="light",ie(Ue,Ue.__value),s(Rt,"class","radio-option svelte-1pfl9w0"),s(at,"class","radio-group svelte-1pfl9w0"),s(at,"role","radiogroup"),s(at,"aria-label",Bn=t[7].theme),s(Lt,"class","setting-row radio-row svelte-1pfl9w0"),s(xt,"class","setting-label svelte-1pfl9w0"),s(xt,"for","cfg-lang"),Ot.__value="en",ie(Ot,Ot.__value),Mt.__value="ru",ie(Mt,Mt.__value),qt.__value="zh",ie(qt,qt.__value),s(nt,"id","cfg-lang"),s(nt,"class","svelte-1pfl9w0"),s(Tt,"class","setting-row svelte-1pfl9w0"),s(Ve,"class","section svelte-1pfl9w0"),s(en,"class","btn btn-save svelte-1pfl9w0"),s(tn,"class","btn btn-reset svelte-1pfl9w0"),s(nn,"class","ver svelte-1pfl9w0"),s(_t,"class","section actions-section svelte-1pfl9w0"),s(r,"class","settings-content svelte-1pfl9w0"),s(e,"class","settings-view svelte-1pfl9w0"),Ol.p(qe,Ue)},m(T,G){K(T,e,G),l(e,n),l(n,o),l(e,a),l(e,r),Te&&Te.m(r,null),l(r,_),l(r,d),l(d,u),l(u,v),l(u,m),l(u,j),l(d,w),l(d,A),l(A,p),l(p,D),l(A,P),l(A,k),ie(k,t[0].serverAddr),l(d,N),l(d,y),l(y,I),l(I,B),l(y,S),l(y,E),ie(E,t[0].nickname),l(d,M),l(d,L),l(L,q),l(q,Q),l(L,ce),l(L,O),ie(O,t[0].serverToken),l(d,U),l(d,z),l(z,oe),l(d,Z),l(d,H),l(H,pe),l(pe,Ie),l(H,Ne),l(H,fe),fe.checked=t[0].autoConnect,l(H,ye),l(H,ge),l(ge,De),l(d,me),l(d,le),l(le,We),l(We,he),l(le,be),l(le,X),X.checked=t[0].autoJoinLastRoom,l(le,se),l(le,st),l(st,rt),l(r,it),l(r,re),l(re,Ae),l(Ae,R),l(Ae,Se),l(Ae,Re),l(re,we),l(re,Ee),l(Ee,bt),l(re,Pe),l(re,$),l($,ae),l(ae,il),l($,xl),l($,xe),l(xe,yt),l(yt,ol),l(xe,jt),l(jt,al),xn(xe,t[0].connectionMode,!0),l(re,es),l(re,vn),l(vn,rl),l(re,ts),l(re,St),l(St,Yt),l(Yt,cl),l(St,ns),l(St,Ge),ie(Ge,t[0].mtu),l(re,ls),l(re,gn),l(gn,ul),l(re,ss),l(re,Et),l(Et,Ht),l(Ht,_l),l(Et,is),l(Et,et),ie(et,t[0].dnsServer),l(re,os),l(re,bn),l(bn,fl),l(re,as),l(re,At),l(At,Xt),l(Xt,dl),l(At,rs),l(At,He),ie(He,t[0].socks5Proxy),l(re,cs),l(re,En),l(En,pl),l(re,us),l(re,Pt),l(Pt,Qt),l(Qt,vl),l(Pt,_s),l(Pt,tt),ie(tt,t[0].stunServer),l(re,fs),l(re,Cn),l(Cn,ml),l(r,ds),l(r,Xe),l(Xe,Ct),l(Ct,ps),l(Ct,hl),l(Ct,vs),l(Xe,ms),l(Xe,Nt),l(Nt,Ln),l(Ln,gl),l(Nt,hs),l(Nt,pt),pt.checked=t[0].startWithWindows,l(Xe,gs),l(Xe,ot),l(ot,Rn),l(Rn,wl),l(ot,ws),l(ot,ct),l(ct,kl),l(ot,ks),Oe&&Oe.m(ot,null),l(Xe,bs),Me&&Me.m(Xe,null),l(r,ys),l(r,Ve),l(Ve,Dt),l(Dt,js),l(Dt,bl),l(Dt,Ss),l(Ve,Es),l(Ve,ut),l(ut,Zt),l(Zt,yl),l(ut,As),l(ut,Qe),l(ut,Ps),l(ut,$t),l($t,jl),l($t,Cs),l(Ve,Ns),l(Ve,Wn),l(Wn,Sl),l(Ve,Ds),l(Ve,Lt),l(Lt,Kn),l(Kn,El),l(Lt,Ls),l(Lt,at),l(at,It),l(It,qe),qe.checked=qe.__value===t[0].theme,l(It,Is),l(It,Al),l(at,Rs),l(at,Rt),l(Rt,Ue),Ue.checked=Ue.__value===t[0].theme,l(Rt,Ts),l(Rt,Pl),l(Ve,Os),l(Ve,Tt),l(Tt,xt),l(xt,Cl),l(Tt,Ms),l(Tt,nt),l(nt,Ot),l(Ot,Nl),l(nt,Mt),l(Mt,Dl),l(nt,qt),l(qt,Ll),xn(nt,t[0].lang||t[8]),l(r,Us),l(r,_t),l(_t,en),l(en,Il),l(_t,zs),l(_t,tn),l(tn,Rl),l(_t,Ws),l(_t,nn),l(nn,Fs),l(nn,Tl),Ml||(Ks=[W(k,"input",t[16]),W(E,"input",t[17]),W(O,"input",t[18]),W(fe,"change",t[19]),W(X,"change",t[20]),W(xe,"change",t[21]),W(Ge,"input",t[22]),W(et,"input",t[23]),W(He,"input",t[24]),W(tt,"input",t[25]),W(pt,"change",t[26]),W(ct,"click",t[9]),W(Qe,"input",t[13]),W(qe,"change",t[27]),W(qe,"change",t[15]),W(Ue,"change",t[29]),W(Ue,"change",t[15]),W(nt,"change",t[14]),W(en,"click",t[11]),W(tn,"click",t[12])],Ml=!0)},p(T,[G]){G&128&&i!==(i=T[7].settings_title+"")&&b(o,i),T[2]?Te?Te.p(T,G):(Te=Ci(T),Te.c(),Te.m(r,_)):Te&&(Te.d(1),Te=null),G&128&&f!==(f=T[7].section_connection+"")&&b(m,f),G&128&&C!==(C=T[7].server_addr+"")&&b(D,C),G&1&&k.value!==T[0].serverAddr&&ie(k,T[0].serverAddr),G&128&&J!==(J=T[7].nickname+"")&&b(B,J),G&1&&E.value!==T[0].nickname&&ie(E,T[0].nickname),G&128&&V!==(V=T[7].server_token+"")&&b(Q,V),G&128&&Y!==(Y=T[7].server_token_desc)&&s(O,"placeholder",Y),G&1&&O.value!==T[0].serverToken&&ie(O,T[0].serverToken),G&128&&te!==(te=T[7].reconnect_apply+"")&&b(oe,te),G&128&&ue!==(ue=T[7].auto_connect+"")&&b(Ie,ue),G&1&&(fe.checked=T[0].autoConnect),G&128&&je!==(je=T[7].auto_connect_desc+"")&&b(De,je),G&128&&_e!==(_e=T[7].auto_join+"")&&b(he,_e),G&1&&(X.checked=T[0].autoJoinLastRoom),G&128&&Fe!==(Fe=T[7].auto_join_desc+"")&&b(rt,Fe),G&128&&x!==(x=T[7].section_network+"")&&b(Se,x),G&128&&Ke!==(Ke=T[7].advanced_warn+"")&&b(bt,Ke),G&128&&Je!==(Je=T[7].connection_mode+"")&&b(il,Je),G&128&&dn!==(dn=T[7].connection_direct+"")&&b(ol,dn),G&128&&pn!==(pn=T[7].connection_relay+"")&&b(al,pn),G&1&&xn(xe,T[0].connectionMode),G&128&&mn!==(mn=T[7].connection_mode_desc+"")&&b(rl,mn),G&128&&hn!==(hn=T[7].mtu+"")&&b(cl,hn),G&1&&Oi(Ge.value)!==T[0].mtu&&ie(Ge,T[0].mtu),G&128&&wn!==(wn=T[7].mtu_desc+"")&&b(ul,wn),G&128&&kn!==(kn=T[7].dns_server+"")&&b(_l,kn),G&1&&et.value!==T[0].dnsServer&&ie(et,T[0].dnsServer),G&128&&yn!==(yn=T[7].dns_desc+"")&&b(fl,yn),G&128&&jn!==(jn=T[7].socks5+"")&&b(dl,jn),G&128&&Sn!==(Sn=T[7].socks5_placeholder)&&s(He,"placeholder",Sn),G&1&&He.value!==T[0].socks5Proxy&&ie(He,T[0].socks5Proxy),G&128&&An!==(An=T[7].socks5_desc+"")&&b(pl,An),G&128&&Pn!==(Pn=T[7].stun_server+"")&&b(vl,Pn),G&1&&tt.value!==T[0].stunServer&&ie(tt,T[0].stunServer),G&128&&Nn!==(Nn=T[7].stun_desc+"")&&b(ml,Nn),G&128&&Dn!==(Dn=T[7].section_system+"")&&b(hl,Dn),G&128&&In!==(In=T[7].start_windows+"")&&b(gl,In),G&1&&(pt.checked=T[0].startWithWindows),G&128&&Tn!==(Tn=T[7].updates+"")&&b(wl,Tn),G&128&&On!==(On=T[7].check_updates+"")&&b(kl,On),G&8&&(ct.disabled=T[3]),T[4]==="available"?Oe?Oe.p(T,G):(Oe=Ni(T),Oe.c(),Oe.m(ot,null)):Oe&&(Oe.d(1),Oe=null),Ut===(Ut=Js(T))&&Me?Me.p(T,G):(Me&&Me.d(1),Me=Ut&&Ut(T),Me&&(Me.c(),Me.m(Xe,null))),G&128&&Mn!==(Mn=T[7].section_appearance+"")&&b(bl,Mn),G&128&&qn!==(qn=T[7].ui_scale+"")&&b(yl,qn),G&1&&Un!==(Un=T[0].uiScale??100)&&(Qe.value=Un),G&1&&zn!==(zn=(T[0].uiScale??100)+"")&&b(jl,zn),G&128&&Fn!==(Fn=T[7].ui_scale_desc+"")&&b(Sl,Fn),G&128&&Jn!==(Jn=T[7].theme+"")&&b(El,Jn),G&1&&(qe.checked=qe.__value===T[0].theme),G&128&&Gn!==(Gn=T[7].theme_dark+"")&&b(Al,Gn),G&1&&(Ue.checked=Ue.__value===T[0].theme),G&128&&Vn!==(Vn=T[7].theme_light+"")&&b(Pl,Vn),G&128&&Bn!==(Bn=T[7].theme)&&s(at,"aria-label",Bn),G&128&&Yn!==(Yn=T[7].language+"")&&b(Cl,Yn),G&128&&Hn!==(Hn=T[7].language_en+"")&&b(Nl,Hn),G&128&&Xn!==(Xn=T[7].language_ru+"")&&b(Dl,Xn),G&128&&Qn!==(Qn=T[7].language_zh+"")&&b(Ll,Qn),G&257&&qs!==(qs=T[0].lang||T[8])&&xn(nt,T[0].lang||T[8]),G&128&&Zn!==(Zn=T[7].save+"")&&b(Il,Zn),G&128&&$n!==($n=T[7].reset+"")&&b(Rl,$n),G&2&&b(Tl,T[1])},i:de,o:de,d(T){T&&F(e),Te&&Te.d(),Oe&&Oe.d(),Me&&Me.d(),Ol.r(),Ml=!1,Le(Ks)}}}function Jo(t,e,n){let i,o,a;ve(t,Be,O=>n(30,i=O)),ve(t,kt,O=>n(7,o=O)),ve(t,$l,O=>n(8,a=O));let r={...i},_="1.0.0",d=!1,u=!1,v="",f="",m="";async function j(){n(3,u=!0),n(4,v="checking"),n(6,m=""),n(5,f="");try{const O=await window.go.main.App.CheckForUpdate();O?.available?(n(4,v="available"),n(5,f=O.latest||"")):n(4,v="latest")}catch(O){n(4,v="error"),n(6,m=String(O)),ee(o.error_update_check+O,"error")}finally{n(3,u=!1)}}async function w(){if(confirm(o.confirm_update)){n(3,u=!0),n(4,v="downloading"),n(6,m="");try{await window.go.main.App.ApplyUpdate(),ee(o.notif_updating,"info")}catch(O){n(4,v="error"),n(6,m=String(O)),n(3,u=!1),ee(o.error_update+O,"error")}}}async function A(){Ze(Be,i={...r},i);try{n(2,d=!!await window.go.main.App.SaveSettings(r)),r.lang&&sn(r.lang),ee(o.notif_settings_saved,"info"),d&&ee(o.notif_reconnect_required,"info")}catch(O){ee(o.error_save+O,"error")}}async function p(){if(confirm(o.confirm_reset))try{n(0,r=await window.go.main.App.ResetSettings()),Ze(Be,i={...r},i),n(2,d=!1),r.lang&&sn(r.lang),ee(o.notif_settings_saved,"info")}catch(O){ee(o.error_save+O,"error")}}function C(O){const Y=Math.min(150,Math.max(75,Number(O.target.value)||100));n(0,r.uiScale=Y,r),Ze(Be,i={...i,uiScale:Y},i)}function D(O){n(0,r.lang=O.target.value,r),sn(O.target.value)}function P(){Ze(Be,i={...i,theme:r.theme},i)}fn(async()=>{try{const O=await window.go.main.App.GetSettings();O&&(n(0,r={...r,...O}),r.connectionMode||n(0,r.connectionMode="direct",r),Ze(Be,i=r,i),r.lang&&sn(r.lang)),window.go.main.App.GetVersion&&n(1,_=await window.go.main.App.GetVersion())}catch{}});const k=[[]];function N(){r.serverAddr=this.value,n(0,r)}function y(){r.nickname=this.value,n(0,r)}function I(){r.serverToken=this.value,n(0,r)}function J(){r.autoConnect=this.checked,n(0,r)}function B(){r.autoJoinLastRoom=this.checked,n(0,r)}function S(){r.connectionMode=Ji(this),n(0,r)}function E(){r.mtu=Oi(this.value),n(0,r)}function M(){r.dnsServer=this.value,n(0,r)}function L(){r.socks5Proxy=this.value,n(0,r)}function q(){r.stunServer=this.value,n(0,r)}function V(){r.startWithWindows=this.checked,n(0,r)}function Q(){r.theme=this.__value,n(0,r)}function ce(){r.theme=this.__value,n(0,r)}return[r,_,d,u,v,f,m,o,a,j,w,A,p,C,D,P,N,y,I,J,B,S,E,M,L,q,V,Q,k,ce]}class Go extends wt{constructor(e){super(),gt(this,e,Jo,Ko,dt,{})}}function Di(t,e,n){const i=t.slice();return i[13]=e[n],i}function Vo(t){let e,n,i,o,a,r,_,d,u,v,f,m,j,w,A,p;o=new vo({});const C=[Qo,Xo,Ho,Yo],D=[];function P(k,N){return k[3]==="network"?0:k[3]==="chat"?1:k[3]==="settings"?2:3}return v=P(t),f=D[v]=C[v](t),j=new So({}),{c(){e=c("div"),n=c("div"),i=c("div"),Bt(o.$$.fragment),a=h(),r=c("div"),_=h(),d=c("main"),u=c("div"),f.c(),m=h(),Bt(j.$$.fragment),s(r,"class","drag-handle svelte-1hi7jen"),ke(r,"active",t[1]),s(i,"class","sidebar-wrapper svelte-1hi7jen"),Vs(i,"width",t[0]+"px"),s(u,"class","view-fade svelte-1hi7jen"),s(u,"key",t[3]),s(d,"class","main-area svelte-1hi7jen"),s(n,"class","layout svelte-1hi7jen"),s(e,"class","shell svelte-1hi7jen")},m(k,N){K(k,e,N),l(e,n),l(n,i),mt(o,i,null),l(i,a),l(i,r),l(n,_),l(n,d),l(d,u),D[v].m(u,null),l(e,m),mt(j,e,null),w=!0,A||(p=W(r,"mousedown",t[6]),A=!0)},p(k,N){(!w||N&2)&&ke(r,"active",k[1]),(!w||N&1)&&Vs(i,"width",k[0]+"px");let y=v;v=P(k),v===y?D[v].p(k,N):(Ui(),lt(D[y],1,1,()=>{D[y]=null}),zi(),f=D[v],f?f.p(k,N):(f=D[v]=C[v](k),f.c()),Ye(f,1),f.m(u,null)),(!w||N&8)&&s(u,"key",k[3])},i(k){w||(Ye(o.$$.fragment,k),Ye(f),Ye(j.$$.fragment,k),w=!0)},o(k){lt(o.$$.fragment,k),lt(f),lt(j.$$.fragment,k),w=!1},d(k){k&&F(e),ht(o),D[v].d(),ht(j),A=!1,p()}}}function Bo(t){let e,n,i,o;return i=new Lo({}),{c(){e=c("div"),n=c("div"),Bt(i.$$.fragment),s(n,"class","fullscreen-connect svelte-1hi7jen"),s(e,"class","shell connect-shell svelte-1hi7jen")},m(a,r){K(a,e,r),l(e,n),mt(i,n,null),o=!0},p:de,i(a){o||(Ye(i.$$.fragment,a),o=!0)},o(a){lt(i.$$.fragment,a),o=!1},d(a){a&&F(e),ht(i)}}}function Yo(t){let e,n,i;return{c(){e=c("div"),n=g("Unknown view: "),i=g(t[3]),s(e,"class","unknown-view svelte-1hi7jen")},m(o,a){K(o,e,a),l(e,n),l(e,i)},p(o,a){a&8&&b(i,o[3])},i:de,o:de,d(o){o&&F(e)}}}function Ho(t){let e,n;return e=new Go({}),{c(){Bt(e.$$.fragment)},m(i,o){mt(e,i,o),n=!0},p:de,i(i){n||(Ye(e.$$.fragment,i),n=!0)},o(i){lt(e.$$.fragment,i),n=!1},d(i){ht(e,i)}}}function Xo(t){let e,n;return e=new Mo({}),{c(){Bt(e.$$.fragment)},m(i,o){mt(e,i,o),n=!0},p:de,i(i){n||(Ye(e.$$.fragment,i),n=!0)},o(i){lt(e.$$.fragment,i),n=!1},d(i){ht(e,i)}}}function Qo(t){let e,n;return e=new bo({}),{c(){Bt(e.$$.fragment)},m(i,o){mt(e,i,o),n=!0},p:de,i(i){n||(Ye(e.$$.fragment,i),n=!0)},o(i){lt(e.$$.fragment,i),n=!1},d(i){ht(e,i)}}}function Li(t){let e,n,i=t[4].msg+"",o,a,r,_=t[2].dismiss_error+"",d,u,v,f;return{c(){e=c("div"),n=c("span"),o=g(i),a=h(),r=c("button"),d=g(_),s(n,"class","error-strip-msg svelte-1hi7jen"),s(r,"type","button"),s(r,"class","error-strip-dismiss svelte-1hi7jen"),s(r,"aria-label",u=t[2].dismiss_error),s(e,"class","error-strip svelte-1hi7jen"),s(e,"role","alert")},m(m,j){K(m,e,j),l(e,n),l(n,o),l(e,a),l(e,r),l(r,d),v||(f=W(r,"click",un),v=!0)},p(m,j){j&16&&i!==(i=m[4].msg+"")&&b(o,i),j&4&&_!==(_=m[2].dismiss_error+"")&&b(d,_),j&4&&u!==(u=m[2].dismiss_error)&&s(r,"aria-label",u)},d(m){m&&F(e),v=!1,f()}}}function Ii(t,e){let n,i,o=e[13].type==="error"?"!":">",a,r,_=e[13].msg+"",d,u,v;return{key:t,first:null,c(){n=c("div"),i=c("span"),a=g(o),r=h(),d=g(_),u=h(),s(i,"class","notif-icon svelte-1hi7jen"),s(n,"class",v="notification "+e[13].type+" svelte-1hi7jen"),s(n,"role","status"),this.first=n},m(f,m){K(f,n,m),l(n,i),l(i,a),l(n,r),l(n,d),l(n,u)},p(f,m){e=f,m&32&&o!==(o=e[13].type==="error"?"!":">")&&b(a,o),m&32&&_!==(_=e[13].msg+"")&&b(d,_),m&32&&v!==(v="notification "+e[13].type+" svelte-1hi7jen")&&s(n,"class",v)},d(f){f&&F(n)}}}function Zo(t){let e,n,i,o,a,r,_,d,u=[],v=new Map,f,m;const j=[Bo,Vo],w=[];function A(P,k){return P[3]==="connect"?0:1}a=A(t),r=w[a]=j[a](t);let p=t[4]&&Li(t),C=ft(t[5]);const D=P=>P[13].id;for(let P=0;P{w[N]=null}),zi(),r=w[a],r?r.p(P,k):(r=w[a]=j[a](P),r.c()),Ye(r,1),r.m(_.parentNode,_)),P[4]?p?p.p(P,k):(p=Li(P),p.c(),p.m(d.parentNode,d)):p&&(p.d(1),p=null),k&32&&(C=ft(P[5]),u=sl(u,k,D,1,P,C,v,f.parentNode,ll,Ii,f,Di))},i(P){m||(Ye(r),m=!0)},o(P){lt(r),m=!1},d(P){P&&(F(e),F(n),F(i),F(o),F(_),F(d),F(f)),w[a].d(P),p&&p.d(P);for(let k=0;kn(2,i=p)),ve(t,Be,p=>n(7,o=p)),ve(t,Ce,p=>n(3,a=p)),ve(t,nl,p=>n(4,r=p)),ve(t,Wl,p=>n(5,_=p));let d=220,u=!1,v,f,m=!0;function j(p){n(1,u=!0),v=p.clientX,f=d,document.addEventListener("mousemove",w),document.addEventListener("mouseup",A),p.preventDefault()}function w(p){if(!u)return;const C=f+(p.clientX-v);n(0,d=Math.max(140,Math.min(400,C)))}function A(){n(1,u=!1),document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",A)}return fn(async()=>{window.addEventListener("keydown",Ri);try{const p=await window.go.main.App.LoadConfig();p&&(Be.set({...o,...p}),p.lang&&sn(p.lang))}catch{}window.runtime?.EventsOn&&(window.runtime.EventsOn("status_changed",p=>{m&&(ze.set(p),!p.connected&&!p.reconnecting&&Ce.set("connect"))}),window.runtime.EventsOn("peers_changed",p=>{m&&Yl.set(p||[])}),window.runtime.EventsOn("error",p=>{m&&ee(p,"error")}),window.runtime.EventsOn("chat_message",p=>{m&&Ql(p.fromID,p.nickname,p.message,!1,"sent",!1,!!p.isDM)}),window.runtime.EventsOn("system_chat",p=>{m&&lo(p.message||"")}),window.runtime.EventsOn("room_deleted",p=>{m&&(ee(i.notif_room_deleted,"error"),Ce.set("network"))}),window.runtime.EventsOn("auto_join_skipped",p=>{if(!m)return;const C=p?.room||"";ee(ne(i.notif_auto_join_skipped,{name:C}),"info")}))}),Mi(()=>{m=!1,window.removeEventListener("keydown",Ri),document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",A),window.runtime?.EventsOff&&(window.runtime.EventsOff("status_changed"),window.runtime.EventsOff("peers_changed"),window.runtime.EventsOff("error"),window.runtime.EventsOff("chat_message"),window.runtime.EventsOff("system_chat"),window.runtime.EventsOff("room_deleted"),window.runtime.EventsOff("auto_join_skipped"))}),t.$$.update=()=>{if(t.$$.dirty&128){const p=$o(o.uiScale)/100;document.documentElement.style.zoom=String(p)}t.$$.dirty&128&&(o.theme==="light"?document.documentElement.classList.add("light-theme"):document.documentElement.classList.remove("light-theme"))},[d,u,i,a,r,_,j,o]}class ea extends wt{constructor(e){super(),gt(this,e,xo,Zo,dt,{})}}new ea({target:document.getElementById("app")}); diff --git a/client/frontend/dist/assets/index-BKDyV7g3.js b/client/frontend/dist/assets/index-BKDyV7g3.js new file mode 100644 index 0000000..2c07a7f --- /dev/null +++ b/client/frontend/dist/assets/index-BKDyV7g3.js @@ -0,0 +1 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))o(i);new MutationObserver(i=>{for(const r of i)if(r.type==="childList")for(const c of r.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&o(c)}).observe(document,{childList:!0,subtree:!0});function n(i){const r={};return i.integrity&&(r.integrity=i.integrity),i.referrerPolicy&&(r.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?r.credentials="include":i.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function o(i){if(i.ep)return;i.ep=!0;const r=n(i);fetch(i.href,r)}})();function Pe(){}function li(e){return e()}function po(){return Object.create(null)}function Re(e){e.forEach(li)}function bs(e){return typeof e=="function"}function pt(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}function ri(e){return Object.keys(e).length===0}function ks(e,...t){if(e==null){for(const o of t)o(void 0);return Pe}const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function fn(e){let t;return ks(e,n=>t=n)(),t}function pe(e,t,n){e.$$.on_destroy.push(ks(t,n))}function Et(e,t,n){return e.set(n),t}function l(e,t){e.appendChild(t)}function Y(e,t,n){e.insertBefore(t,n||null)}function V(e){e.parentNode&&e.parentNode.removeChild(e)}function ci(e,t){for(let n=0;ne.removeEventListener(t,n,o)}function Dt(e){return function(t){return t.stopPropagation(),e.call(this,t)}}function s(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function si(e){return e===""?null:+e}function ui(e){return Array.from(e.childNodes)}function j(e,t){t=""+t,e.data!==t&&(e.data=t)}function _e(e,t){e.value=t??""}function vo(e,t,n,o){n==null?e.style.removeProperty(t):e.style.setProperty(t,n,"")}function Al(e,t,n){for(let o=0;oo.call(this,t))}const un=[],_n=[];let dn=[];const mo=[],_i=Promise.resolve();let ds=!1;function pi(){ds||(ds=!0,_i.then(ii))}function Pl(e){dn.push(e)}const fs=new Set;let rn=0;function ii(){if(rn!==0)return;const e=wn;do{try{for(;rne.indexOf(o)===-1?t.push(o):n.push(o)),n.forEach(o=>o()),dn=t}const Ll=new Set;let zt;function hi(){zt={r:0,c:[],p:zt}}function gi(){zt.r||Re(zt.c),zt=zt.p}function qe(e,t){e&&e.i&&(Ll.delete(e),e.i(t))}function $e(e,t,n,o){if(e&&e.o){if(Ll.has(e))return;Ll.add(e),zt.c.push(()=>{Ll.delete(e),o&&(n&&e.d(1),o())}),e.o(t)}else o&&o()}function _t(e){return e?.length!==void 0?e:Array.from(e)}function Tl(e,t){e.d(1),t.delete(e.key)}function Il(e,t,n,o,i,r,c,u,f,_,m,d){let h=e.length,g=r.length,C=h;const w={};for(;C--;)w[e[C].key]=C;const b=[],S=new Map,L=new Map,R=[];for(C=g;C--;){const M=d(i,r,C),J=n(M);let H=c.get(J);H?R.push(()=>H.p(M,t)):(H=_(J,M),H.c()),S.set(J,b[C]=H),J in w&&L.set(J,Math.abs(C-w[J]))}const D=new Set,y=new Set;function E(M){qe(M,1),M.m(u,m),c.set(M.key,M),m=M.first,g--}for(;h&&g;){const M=b[g-1],J=e[h-1],H=M.key,p=J.key;M===J?(m=M.first,h--,g--):S.has(p)?!c.has(H)||D.has(H)?E(M):y.has(p)?h--:L.get(H)>L.get(p)?(y.add(H),E(M)):(D.add(p),h--):(f(J,c),h--)}for(;h--;){const M=e[h];S.has(M.key)||f(M,c)}for(;g;)E(b[g-1]);return Re(R),b}function ot(e){e&&e.c()}function xe(e,t,n){const{fragment:o,after_update:i}=e.$$;o&&o.m(t,n),Pl(()=>{const r=e.$$.on_mount.map(li).filter(bs);e.$$.on_destroy?e.$$.on_destroy.push(...r):Re(r),e.$$.on_mount=[]}),i.forEach(Pl)}function et(e,t){const n=e.$$;n.fragment!==null&&(mi(n.after_update),Re(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function bi(e,t){e.$$.dirty[0]===-1&&(un.push(e),pi(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const C=g.length?g[0]:h;return _.ctx&&i(_.ctx[d],_.ctx[d]=C)&&(!_.skip_bound&&_.bound[d]&&_.bound[d](C),m&&bi(e,d)),h}):[],_.update(),m=!0,Re(_.before_update),_.fragment=o?o(_.ctx):!1,t.target){if(t.hydrate){const d=ui(t.target);_.fragment&&_.fragment.l(d),d.forEach(V)}else _.fragment&&_.fragment.c();t.intro&&qe(e.$$.fragment),xe(e,t.target,t.anchor),ii()}kn(f)}class yt{$$=void 0;$$set=void 0;$destroy(){et(this,1),this.$destroy=Pe}$on(t,n){if(!bs(n))return Pe;const o=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return o.push(n),()=>{const i=o.indexOf(n);i!==-1&&o.splice(i,1)}}$set(t){this.$$set&&!ri(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const ki="4",cn=[];function wi(e,t){return{subscribe:tt(e,t).subscribe}}function tt(e,t=Pe){let n;const o=new Set;function i(u){if(pt(e,u)&&(e=u,n)){const f=!cn.length;for(const _ of o)_[1](),cn.push(_,e);if(f){for(let _=0;_{o.delete(_),o.size===0&&n&&(n(),n=null)}}return{set:i,update:r,subscribe:c}}function yi(e,t,n){const o=!Array.isArray(e),i=o?[e]:e;if(!i.every(Boolean))throw new Error("derived() expects stores as input, got a falsy value");const r=t.length<2;return wi(n,(c,u)=>{let f=!1;const _=[];let m=0,d=Pe;const h=()=>{if(m)return;d();const C=t(o?_[0]:_,c,u);r?c(C):d=bs(C)?C:Pe},g=i.map((C,w)=>ks(C,b=>{_[w]=b,m&=~(1<{m|=1<[...o,{id:n,msg:e,type:t}]),setTimeout(()=>{ps.update(o=>o.filter(i=>i.id!==n))},4e3),t==="error"&&Ai(e)}function Ai(e){if(!e){Dl.set(null);return}Dl.set({id:++Ci,msg:String(e)})}function yn(){Dl.set(null)}function vs(e,t){return e&&e.id?"peer:"+e.id:"room:"+(t||"network")}function jn(){return vs(fn(Nl),fn(Fe).room)}function Ss(e,t,n,o=!1,i="sent",r=!1,c=!1){const u=++Si;let f;r?f=vs(null,fn(Fe).room):o?f=jn():c?f="peer:"+e:f=vs(null,fn(Fe).room),ys.update(m=>{const d=m[f]?[...m[f]]:[];return d.push({id:o&&e.startsWith("local_")?e:u,fromID:e,nickname:t,message:n,timestamp:Date.now(),isSelf:o,status:i,system:r}),{...m,[f]:d.length>ho?d.slice(-ho):d}});const _=jn();!o&&!(fn(it)&&f===_)&&js.update(m=>({...m,[f]:(m[f]||0)+1}))}function Li(e){Ss("system","",e,!1,"sent",!0)}function ms(e){js.update(t=>{const n={...t};return delete n[e],n})}function gn(e,t){const n=jn();ys.update(o=>{const i=o[n];return i?{...o,[n]:i.map(r=>r.id===e?{...r,status:t}:r)}:o})}const Ei={subscribe(e){return ys.subscribe(t=>{e(t[jn()]||[])})}};typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(ki);const hs={brand:"Entangled",tagline:"Private LAN for friends — anywhere.",networks:"Networks",network_count:"{n} network(s)",create_network:"Create Network",join_network:"Join Network",connecting:"Connecting...",peer_count:"{n} peer(s)",offline_click:"Offline — click to connect",saved:"Saved",no_networks:"No saved networks",no_networks_desc:"Create or join a network to get started",settings:"Settings",network_name:"Network name",password_opt:"Password (optional)",cancel:"Cancel",create:"Create",join:"Join",leave:"Leave network",remove:"Remove saved",delete_room:"Delete network",more_actions:"More actions",copy_invite:"Copy invite",paste_invite:"Have an invite?",invite_copied:"Invite copied",invite_placeholder:"server|room|password",server_addr:"Server Address",nickname:"Nickname",server_token:"Server token",server_token_desc:"optional shared secret (ENTANGLED_TOKEN)",connect:"Connect",disconnect:"Disconnect",connected:"Connected",server:"Server",virtual_ip:"Virtual IP",na:"N/A",reconnecting:"Reconnecting...",disconnected:"Disconnected",phase_dialing:"Dialing server…",phase_auth:"Authenticating…",phase_ready:"Ready",phase_error:"Error",room_chat:"Room Chat",chat:"Chat",close_chat:"Close",channels:"Channels",col_ch:"Ch",col_nickname:"Nickname",col_ip:"Virtual IP",col_ping:"Ping",ping_peer:"Ping",ping_sent:"Pinging {name}…",error_ping:"Ping failed: {err}",col_path:"Path",col_status:"Status",status_online:"Online",status_offline:"Offline",path_p2p:"Direct",path_relay:"Relay",path_ws:"Fallback",path_unknown:"—",path_p2p_tip:"Peer-to-peer direct connection",path_relay_tip:"Traffic routed through the server relay",path_ws_tip:"WebSocket fallback path",you:"You",me:"me",no_peers:"No peers connected",no_peers_desc:"Create or join a network, then wait for friends",peers_list:"Peers",network:"Network",chat_with:"Chat with {name}",room_chat_title:"Room Chat — {room}",back:"Back",no_messages:"No messages yet",no_messages_desc:"Send a message to start the conversation",type_message:"Type a message...",send:"Send",send_disabled_no_room:"Join a network to chat",msg_truncated:"Message truncated to 1000 characters",retry:"Retry",today:"Today",yesterday:"Yesterday",you_self:"You",status_connected:"Connected",status_disconnected:"Disconnected",status_reconnecting:"Reconnecting…",status_relay_degraded:"Some peers on relay",status_direct:"Direct",status_server:"Server: {s}",status_ip:"IP: {ip}",status_network:"Network: {room}",status_peers:"Peers: {n}",version:"Entangled v{v}",settings_title:"Settings",section_connection:"Connection",section_network:"Advanced network",section_system:"System",section_appearance:"Appearance",advanced_warn:"Only change these if you know what they do.",reconnect_apply:"Reconnect to apply",auto_connect:"Auto Connect",auto_connect_desc:"connect to last server on startup",auto_join:"Auto Join Last Room",auto_join_desc:"join last open room after connect (password rooms need a manual join after restart)",connection_mode:"Connection",connection_direct:"Direct (P2P)",connection_relay:"Relay (server)",connection_mode_desc:"Direct prefers peer-to-peer and uses relay only when needed. Relay forces all traffic through the server.",mtu:"MTU",mtu_desc:"Packet size. Wrong values can break games or cause lag.",dns_server:"DNS Server",dns_desc:"Optional DNS for the virtual network. Leave blank for system default.",socks5:"SOCKS5 Proxy",socks5_placeholder:"e.g. 127.0.0.1:10808",socks5_desc:"Proxies signaling (WebSocket) only — not peer/relay UDP. Reconnect to apply.",stun_server:"STUN Server",stun_desc:"Helps find your public address for direct links. Reconnect to apply.",start_windows:"Start with Windows",updates:"Updates",check_updates:"Check for updates",apply_update:"Update",update_checking:"Checking for updates…",update_latest:"You are up to date.",update_available:"Update available: v{v}",update_downloading:"Downloading update… the app will restart.",update_failed:"Update failed",confirm_update:"Download and install the update? The app will restart.",error_update_check:"Update check failed: ",error_update:"Update failed: ",notif_updating:"Installing update…",ui_scale:"UI Scale",ui_scale_desc:"Scales the whole interface. Use with window resize or F11 fullscreen.",theme:"Theme",theme_dark:"Dark",theme_light:"Light",language:"Language",save:"Save",reset:"Reset to defaults",language_en:"English",language_ru:"Russian",language_zh:"Chinese",copied:"Copied",dismiss_error:"Dismiss",field_required:"Required",notif_created:"Network created: {name}",notif_joined:"Joined network: {name}",notif_left:"Left network",notif_removed:"Removed: {name}",notif_deleted:"Network deleted: {name}",notif_connected:"Connected to {addr}",notif_disconnected:"Disconnected",notif_settings_saved:"Settings saved",notif_reconnect_required:"Reconnect to apply proxy / STUN / token changes",notif_auto_join_skipped:'Auto-join skipped for "{name}" — enter the password to join',notif_need_config:"Set server & nickname in Connect page first",notif_room_deleted:"Network was deleted by owner",invite_no_password:"Invite copied without password (not stored). Add it before sharing.",error_create:"Failed to create network: {err}",error_join:"Failed to join: {err}",error_leave:"Failed to leave: {err}",error_remove:"Failed to remove: {err}",error_connect:"Connection failed: {err}",error_disconnect:"Error: {err}",error_send:"Failed to send: {err}",error_not_connected:"Not connected — cannot send message",error_invalid_invite:"Invalid invite: server and room are required",error_save:"Failed to save settings: {err}",confirm_disconnect:"Disconnect from server?",confirm_leave:"Leave the current network? It stays in your saved list.",confirm_leave_remove:"Leave and remove this network from your saved list?",confirm_remove:'Remove "{name}" from saved networks? (does not delete the network for others)',confirm_delete:'Permanently delete network "{name}" for everyone?',confirm_reset:"Reset all settings to defaults?"},Pi={brand:"Entangled",tagline:"Частная LAN для друзей — где угодно.",networks:"Сети",network_count:"{n} сет(ь/и/ей)",create_network:"Создать сеть",join_network:"Подключиться",connecting:"Подключение...",peer_count:"{n} участник(ов)",offline_click:"Не в сети — нажмите для подключения",saved:"Сохранено",no_networks:"Нет сохранённых сетей",no_networks_desc:"Создайте или войдите в сеть, чтобы начать",settings:"Настройки",network_name:"Название сети",password_opt:"Пароль (опционально)",cancel:"Отмена",create:"Создать",join:"Войти",leave:"Покинуть сеть",remove:"Убрать из списка",delete_room:"Удалить сеть",more_actions:"Ещё действия",copy_invite:"Копировать инвайт",paste_invite:"Есть инвайт?",invite_copied:"Инвайт скопирован",invite_placeholder:"сервер|комната|пароль",server_addr:"Адрес сервера",nickname:"Псевдоним",server_token:"Токен сервера",server_token_desc:"опциональный секрет (ENTANGLED_TOKEN)",connect:"Подключиться",disconnect:"Отключиться",connected:"Подключено",server:"Сервер",virtual_ip:"Виртуальный IP",na:"Н/Д",reconnecting:"Переподключение...",disconnected:"Отключено",phase_dialing:"Подключение к серверу…",phase_auth:"Аутентификация…",phase_ready:"Готово",phase_error:"Ошибка",room_chat:"Чат комнаты",chat:"Чат",close_chat:"Закрыть",channels:"Каналы",col_ch:"Кн",col_nickname:"Псевдоним",col_ip:"Виртуальный IP",col_ping:"Пинг",ping_peer:"Пинг",ping_sent:"Пинг {name}…",error_ping:"Пинг не удался: {err}",col_path:"Путь",col_status:"Статус",status_online:"В сети",status_offline:"Не в сети",path_p2p:"Прямой",path_relay:"Релей",path_ws:"Запасной",path_unknown:"—",path_p2p_tip:"Прямое соединение peer-to-peer",path_relay_tip:"Трафик через серверный релей",path_ws_tip:"Запасной путь через WebSocket",you:"Вы",me:"я",no_peers:"Нет подключённых участников",no_peers_desc:"Создайте или войдите в сеть и дождитесь друзей",peers_list:"Участники",network:"Сеть",chat_with:"Чат с {name}",room_chat_title:"Чат комнаты — {room}",back:"Назад",no_messages:"Сообщений пока нет",no_messages_desc:"Отправьте сообщение, чтобы начать разговор",type_message:"Введите сообщение...",send:"Отправить",send_disabled_no_room:"Войдите в сеть, чтобы писать",msg_truncated:"Сообщение обрезано до 1000 символов",retry:"Повторить",today:"Сегодня",yesterday:"Вчера",you_self:"Вы",status_connected:"Подключено",status_disconnected:"Отключено",status_reconnecting:"Переподключение…",status_relay_degraded:"Часть пиров через релей",status_direct:"Прямое",status_server:"Сервер: {s}",status_ip:"IP: {ip}",status_network:"Сеть: {room}",status_peers:"Участники: {n}",version:"Entangled v{v}",settings_title:"Настройки",section_connection:"Подключение",section_network:"Доп. сеть",section_system:"Система",section_appearance:"Внешний вид",advanced_warn:"Меняйте только если понимаете, что делаете.",reconnect_apply:"Переподключитесь, чтобы применить",auto_connect:"Автоподключение",auto_connect_desc:"подключаться к последнему серверу при запуске",auto_join:"Автовход в комнату",auto_join_desc:"входить в последнюю открытую комнату (с паролем — вручную после перезапуска)",connection_mode:"Соединение",connection_direct:"Прямое (P2P)",connection_relay:"Релей (сервер)",connection_mode_desc:"Прямое предпочитает P2P и использует релей только при необходимости. Релей гонит весь трафик через сервер.",mtu:"MTU",mtu_desc:"Размер пакета. Неверное значение ломает игры или даёт лаги.",dns_server:"DNS-сервер",dns_desc:"Опциональный DNS виртуальной сети. Пусто — системный по умолчанию.",socks5:"SOCKS5-прокси",socks5_placeholder:"напр. 127.0.0.1:10808",socks5_desc:"Только сигнальный WebSocket, не UDP пиров/релея. Переподключитесь, чтобы применить.",stun_server:"STUN-сервер",stun_desc:"Помогает найти публичный адрес. Переподключитесь, чтобы применить.",start_windows:"Запуск с Windows",updates:"Обновления",check_updates:"Проверить обновления",apply_update:"Обновить",update_checking:"Проверка обновлений…",update_latest:"У вас актуальная версия.",update_available:"Доступно обновление: v{v}",update_downloading:"Загрузка обновления… приложение перезапустится.",update_failed:"Ошибка обновления",confirm_update:"Скачать и установить обновление? Приложение перезапустится.",error_update_check:"Не удалось проверить обновления: ",error_update:"Не удалось обновить: ",notif_updating:"Установка обновления…",ui_scale:"Масштаб UI",ui_scale_desc:"Масштабирует весь интерфейс. Вместе с изменением размера окна или F11.",theme:"Тема",theme_dark:"Тёмная",theme_light:"Светлая",language:"Язык",save:"Сохранить",reset:"Сбросить",language_en:"Английский",language_ru:"Русский",language_zh:"Китайский",copied:"Скопировано",dismiss_error:"Закрыть",field_required:"Обязательно",notif_created:"Сеть создана: {name}",notif_joined:"Подключились к сети: {name}",notif_left:"Покинули сеть",notif_removed:"Удалено: {name}",notif_deleted:"Сеть удалена: {name}",notif_connected:"Подключились к {addr}",notif_disconnected:"Отключились",notif_settings_saved:"Настройки сохранены",notif_reconnect_required:"Переподключитесь, чтобы применить прокси / STUN / токен",notif_auto_join_skipped:"Автовход пропущен для «{name}» — введите пароль вручную",notif_need_config:"Укажите сервер и псевдоним на странице подключения",notif_room_deleted:"Сеть удалена владельцем",invite_no_password:"Инвайт скопирован без пароля (он не хранится). Добавьте перед отправкой.",error_create:"Не удалось создать сеть: {err}",error_join:"Не удалось подключиться: {err}",error_leave:"Не удалось покинуть: {err}",error_remove:"Не удалось удалить: {err}",error_connect:"Ошибка подключения: {err}",error_disconnect:"Ошибка: {err}",error_send:"Не удалось отправить: {err}",error_not_connected:"Нет подключения — невозможно отправить сообщение",error_invalid_invite:"Неверный инвайт: нужны сервер и комната",error_save:"Не удалось сохранить настройки: {err}",confirm_disconnect:"Отключиться от сервера?",confirm_leave:"Покинуть текущую сеть? Она останется в сохранённых.",confirm_leave_remove:"Покинуть и убрать сеть из сохранённых?",confirm_remove:'Убрать "{name}" из сохранённых? (сеть для других не удалится)',confirm_delete:'Навсегда удалить сеть "{name}" для всех?',confirm_reset:"Сбросить все настройки?"},Di={brand:"Entangled",tagline:"朋友的私人局域网 — 随时随地。",networks:"网络",network_count:"{n} 个网络",create_network:"创建网络",join_network:"加入网络",connecting:"连接中...",peer_count:"{n} 个节点",offline_click:"离线 — 点击连接",saved:"已保存",no_networks:"暂无保存的网络",no_networks_desc:"创建或加入网络以开始",settings:"设置",network_name:"网络名称",password_opt:"密码(可选)",cancel:"取消",create:"创建",join:"加入",leave:"离开网络",remove:"移除已保存",delete_room:"删除网络",more_actions:"更多操作",copy_invite:"复制邀请",paste_invite:"有邀请码?",invite_copied:"邀请已复制",invite_placeholder:"服务器|房间|密码",server_addr:"服务器地址",nickname:"昵称",server_token:"服务器令牌",server_token_desc:"可选共享密钥(ENTANGLED_TOKEN)",connect:"连接",disconnect:"断开",connected:"已连接",server:"服务器",virtual_ip:"虚拟 IP",na:"无",reconnecting:"重新连接...",disconnected:"已断开",phase_dialing:"正在连接服务器…",phase_auth:"正在验证…",phase_ready:"就绪",phase_error:"错误",room_chat:"房间聊天",chat:"聊天",close_chat:"关闭",channels:"通道",col_ch:"通",col_nickname:"昵称",col_ip:"虚拟 IP",col_ping:"延迟",ping_peer:"测速",ping_sent:"正在测速 {name}…",error_ping:"测速失败: {err}",col_path:"路径",col_status:"状态",status_online:"在线",status_offline:"离线",path_p2p:"直连",path_relay:"中继",path_ws:"备用",path_unknown:"—",path_p2p_tip:"点对点直连",path_relay_tip:"经服务器中继转发",path_ws_tip:"WebSocket 备用路径",you:"你",me:"我",no_peers:"暂无连接的节点",no_peers_desc:"创建或加入网络,然后等待好友",peers_list:"节点",network:"网络",chat_with:"与 {name} 聊天",room_chat_title:"房间聊天 — {room}",back:"返回",no_messages:"暂无消息",no_messages_desc:"发送一条消息开始对话",type_message:"输入消息...",send:"发送",send_disabled_no_room:"加入网络后才能聊天",msg_truncated:"消息已截断至 1000 个字符",retry:"重试",today:"今天",yesterday:"昨天",you_self:"你",status_connected:"已连接",status_disconnected:"已断开",status_reconnecting:"重新连接…",status_relay_degraded:"部分节点经中继",status_direct:"直连",status_server:"服务器: {s}",status_ip:"IP: {ip}",status_network:"网络: {room}",status_peers:"节点: {n}",version:"Entangled v{v}",settings_title:"设置",section_connection:"连接",section_network:"高级网络",section_system:"系统",section_appearance:"外观",advanced_warn:"仅在了解作用时更改这些选项。",reconnect_apply:"重新连接以生效",auto_connect:"自动连接",auto_connect_desc:"启动时自动连接到上次的服务器",auto_join:"自动加入房间",auto_join_desc:"连接后自动加入上次的开放房间(有密码的房间重启后需手动加入)",connection_mode:"连接方式",connection_direct:"直连 (P2P)",connection_relay:"中继 (服务器)",connection_mode_desc:"直连优先点对点,必要时才用中继。中继模式强制全部流量经服务器。",mtu:"MTU",mtu_desc:"数据包大小。错误值可能导致游戏异常或卡顿。",dns_server:"DNS 服务器",dns_desc:"虚拟网络可选 DNS。留空则使用系统默认。",socks5:"SOCKS5 代理",socks5_placeholder:"例如 127.0.0.1:10808",socks5_desc:"仅代理信令 WebSocket,不含节点/中继 UDP。重新连接以生效。",stun_server:"STUN 服务器",stun_desc:"用于发现公网地址以便直连。重新连接以生效。",start_windows:"开机自启",updates:"更新",check_updates:"检查更新",apply_update:"更新",update_checking:"正在检查更新…",update_latest:"已是最新版本。",update_available:"有可用更新:v{v}",update_downloading:"正在下载更新…应用将重启。",update_failed:"更新失败",confirm_update:"下载并安装更新?应用将重启。",error_update_check:"检查更新失败:",error_update:"更新失败:",notif_updating:"正在安装更新…",ui_scale:"界面缩放",ui_scale_desc:"缩放整个界面。可配合窗口调整大小或 F11 全屏使用。",theme:"主题",theme_dark:"深色",theme_light:"浅色",language:"语言",save:"保存",reset:"重置为默认",language_en:"英语",language_ru:"俄语",language_zh:"中文",copied:"已复制",dismiss_error:"关闭",field_required:"必填",notif_created:"网络已创建: {name}",notif_joined:"已加入网络: {name}",notif_left:"已离开网络",notif_removed:"已删除: {name}",notif_deleted:"网络已删除: {name}",notif_connected:"已连接到 {addr}",notif_disconnected:"已断开连接",notif_settings_saved:"设置已保存",notif_reconnect_required:"请重新连接以使代理 / STUN / 令牌生效",notif_auto_join_skipped:"已跳过自动加入「{name}」— 请输入密码后手动加入",notif_need_config:"请先在连接页面设置服务器和昵称",notif_room_deleted:"网络已被所有者删除",invite_no_password:"邀请已复制但无密码(密码不存储)。分享前请自行添加。",error_create:"创建网络失败: {err}",error_join:"加入失败: {err}",error_leave:"离开失败: {err}",error_remove:"删除失败: {err}",error_connect:"连接失败: {err}",error_disconnect:"错误: {err}",error_send:"发送失败: {err}",error_not_connected:"未连接 — 无法发送消息",error_invalid_invite:"无效邀请:需要服务器和房间名",error_save:"保存设置失败: {err}",confirm_disconnect:"确定断开服务器连接?",confirm_leave:"离开当前网络?仍保留在已保存列表中。",confirm_leave_remove:"离开并从已保存列表中移除?",confirm_remove:'从已保存网络中移除 "{name}"?(不会删除他人的网络)',confirm_delete:'永久删除网络 "{name}"(对所有人)?',confirm_reset:"确定重置所有设置?"},Cs={en:hs,ru:Pi,zh:Di};function Ni(){const e=localStorage.getItem("entangled_locale");if(e&&Cs[e])return e;const t=navigator.language?.slice(0,2);return t==="ru"?"ru":t==="zh"?"zh":"en"}const As=tt(Ni()),jt=yi(As,e=>{const t=Cs[e]||hs;return new Proxy(t,{get(n,o){if(typeof o=="string")return n[o]??hs[o]??o}})});function bn(e){Cs[e]&&(localStorage.setItem("entangled_locale",e),As.set(e))}function ke(e,t){return e?String(e).replace(/\{(\w+)\}/g,(n,o)=>t&&t[o]!=null?t[o]:`{${o}}`):""}function go(e){let t,n,o,i,r,c,u,f=e[4].cancel+"",_,m,d,h=(e[2]||e[4].disconnect)+"",g,C,w;return{c(){t=a("div"),n=a("div"),o=a("p"),i=k(e[1]),r=v(),c=a("div"),u=a("button"),_=k(f),m=v(),d=a("button"),g=k(h),s(o,"id","confirm-msg"),s(o,"class","svelte-1g1nook"),s(u,"type","button"),s(u,"class","btn"),s(d,"type","button"),s(d,"class","btn"),X(d,"danger",e[3]),X(d,"btn-cta",!e[3]),s(c,"class","modal-actions svelte-1g1nook"),s(n,"class","modal svelte-1g1nook"),s(n,"role","dialog"),s(n,"aria-modal","true"),s(n,"aria-labelledby","confirm-msg"),s(t,"class","modal-overlay svelte-1g1nook"),s(t,"role","presentation")},m(b,S){Y(b,t,S),l(t,n),l(n,o),l(o,i),l(n,r),l(n,c),l(c,u),l(u,_),l(c,m),l(c,d),l(d,g),C||(w=[W(u,"click",e[5]),W(d,"click",e[6]),W(n,"click",Dt(e[10])),W(n,"keydown",Dt(e[11])),W(t,"click",e[5]),W(t,"keydown",e[7])],C=!0)},p(b,S){S&2&&j(i,b[1]),S&16&&f!==(f=b[4].cancel+"")&&j(_,f),S&20&&h!==(h=(b[2]||b[4].disconnect)+"")&&j(g,h),S&8&&X(d,"danger",b[3]),S&8&&X(d,"btn-cta",!b[3])},d(b){b&&V(t),C=!1,Re(w)}}}function Mi(e){let t,n=e[0]&&go(e);return{c(){n&&n.c(),t=El()},m(o,i){n&&n.m(o,i),Y(o,t,i)},p(o,[i]){o[0]?n?n.p(o,i):(n=go(o),n.c(),n.m(t.parentNode,t)):n&&(n.d(1),n=null)},i:Pe,o:Pe,d(o){o&&V(t),n&&n.d(o)}}}function Ti(e,t,n){let o;pe(e,jt,w=>n(4,o=w));let{open:i=!1}=t,{message:r=""}=t,{confirmLabel:c=""}=t,{danger:u=!1}=t,{onConfirm:f=()=>{}}=t,{onCancel:_=()=>{}}=t;function m(){_()}function d(){f()}function h(w){w.key==="Escape"&&m()}function g(w){Lt.call(this,e,w)}function C(w){Lt.call(this,e,w)}return e.$$set=w=>{"open"in w&&n(0,i=w.open),"message"in w&&n(1,r=w.message),"confirmLabel"in w&&n(2,c=w.confirmLabel),"danger"in w&&n(3,u=w.danger),"onConfirm"in w&&n(8,f=w.onConfirm),"onCancel"in w&&n(9,_=w.onCancel)},[i,r,c,u,o,m,d,h,f,_,g,C]}class Ls extends yt{constructor(t){super(),wt(this,t,Ti,Mi,pt,{open:0,message:1,confirmLabel:2,danger:3,onConfirm:8,onCancel:9})}}function bo(e,t,n){const o=e.slice();return o[63]=t[n],o}function ko(e){let t,n,o=e[16].no_networks+"",i,r,c,u=e[16].no_networks_desc+"",f,_;return{c(){t=a("div"),n=a("div"),i=k(o),r=v(),c=a("div"),f=k(u),_=v(),s(c,"class","empty-desc svelte-1vrsv4c"),s(t,"class","empty-state svelte-1vrsv4c")},m(m,d){Y(m,t,d),l(t,n),l(n,i),l(t,r),l(t,c),l(c,f),l(t,_)},p(m,d){d[0]&65536&&o!==(o=m[16].no_networks+"")&&j(i,o),d[0]&65536&&u!==(u=m[16].no_networks_desc+"")&&j(f,u)},d(m){m&&V(t)}}}function Ii(e){let t=e[16].saved+"",n;return{c(){n=k(t)},m(o,i){Y(o,n,i)},p(o,i){i[0]&65536&&t!==(t=o[16].saved+"")&&j(n,t)},d(o){o&&V(n)}}}function Ri(e){let t=e[16].offline_click+"",n;return{c(){n=k(t)},m(o,i){Y(o,n,i)},p(o,i){i[0]&65536&&t!==(t=o[16].offline_click+"")&&j(n,t)},d(o){o&&V(n)}}}function Oi(e){let t=ke(e[16].peer_count,{n:e[17].peerCount})+"",n;return{c(){n=k(t)},m(o,i){Y(o,n,i)},p(o,i){i[0]&196608&&t!==(t=ke(o[16].peer_count,{n:o[17].peerCount})+"")&&j(n,t)},d(o){o&&V(n)}}}function zi(e){let t=e[16].connecting+"",n;return{c(){n=k(t)},m(o,i){Y(o,n,i)},p(o,i){i[0]&65536&&t!==(t=o[16].connecting+"")&&j(n,t)},d(o){o&&V(n)}}}function wo(e){let t,n,o=e[16].copy_invite+"",i,r,c,u,f=e[16].remove+"",_,m,d,h;function g(...S){return e[39](e[63],...S)}let C=e[17].room===e[63].name&&yo(e);function w(...S){return e[41](e[63],...S)}let b=e[17].room===e[63].name&&e[17].isOwner&&jo(e);return{c(){t=a("div"),n=a("button"),i=k(o),r=v(),C&&C.c(),c=v(),u=a("button"),_=k(f),m=v(),b&&b.c(),s(n,"type","button"),s(n,"role","menuitem"),s(n,"class","menu-item svelte-1vrsv4c"),s(u,"type","button"),s(u,"role","menuitem"),s(u,"class","menu-item danger svelte-1vrsv4c"),s(t,"class","overflow-menu svelte-1vrsv4c"),s(t,"role","menu"),s(t,"tabindex","-1")},m(S,L){Y(S,t,L),l(t,n),l(n,i),l(t,r),C&&C.m(t,null),l(t,c),l(t,u),l(u,_),l(t,m),b&&b.m(t,null),d||(h=[W(n,"click",g),W(u,"click",w),W(t,"click",Dt(e[36])),W(t,"keydown",Dt(e[37]))],d=!0)},p(S,L){e=S,L[0]&65536&&o!==(o=e[16].copy_invite+"")&&j(i,o),e[17].room===e[63].name?C?C.p(e,L):(C=yo(e),C.c(),C.m(t,c)):C&&(C.d(1),C=null),L[0]&65536&&f!==(f=e[16].remove+"")&&j(_,f),e[17].room===e[63].name&&e[17].isOwner?b?b.p(e,L):(b=jo(e),b.c(),b.m(t,null)):b&&(b.d(1),b=null)},d(S){S&&V(t),C&&C.d(),b&&b.d(),d=!1,Re(h)}}}function yo(e){let t,n=e[16].leave+"",o,i,r;return{c(){t=a("button"),o=k(n),s(t,"type","button"),s(t,"role","menuitem"),s(t,"class","menu-item svelte-1vrsv4c")},m(c,u){Y(c,t,u),l(t,o),i||(r=W(t,"click",e[40]),i=!0)},p(c,u){u[0]&65536&&n!==(n=c[16].leave+"")&&j(o,n)},d(c){c&&V(t),i=!1,r()}}}function jo(e){let t,n=e[16].delete_room+"",o,i,r;function c(...u){return e[42](e[63],...u)}return{c(){t=a("button"),o=k(n),s(t,"type","button"),s(t,"role","menuitem"),s(t,"class","menu-item danger svelte-1vrsv4c")},m(u,f){Y(u,t,f),l(t,o),i||(r=W(t,"click",c),i=!0)},p(u,f){e=u,f[0]&65536&&n!==(n=e[16].delete_room+"")&&j(o,n)},d(u){u&&V(t),i=!1,r()}}}function So(e,t){let n,o,i,r,c,u=t[63].name+"",f,_,m,d,h,g,C,w,b,S,L,R,D,y,E,M,J,H,p;function T(G,O){return G[11]===G[63].name?zi:G[17].room===G[63].name?Oi:G[17].connected?Ii:Ri}let Z=T(t),z=Z(t);function I(...G){return t[38](t[63],...G)}let U=t[12]===t[63].name&&wo(t);function le(){return t[43](t[63])}function oe(...G){return t[44](t[63],...G)}return{key:e,first:null,c(){n=a("div"),o=a("span"),i=v(),r=a("div"),c=a("div"),f=k(u),_=v(),m=a("div"),z.c(),d=v(),h=a("div"),g=a("button"),C=Pt("svg"),w=Pt("circle"),b=Pt("circle"),S=Pt("circle"),y=v(),U&&U.c(),E=v(),s(o,"class","tick svelte-1vrsv4c"),s(o,"aria-hidden","true"),X(o,"on",t[17].room===t[63].name),s(c,"class","network-name svelte-1vrsv4c"),s(m,"class","network-meta svelte-1vrsv4c"),s(r,"class","network-info svelte-1vrsv4c"),s(w,"cx","3.5"),s(w,"cy","8"),s(w,"r","0.9"),s(b,"cx","8"),s(b,"cy","8"),s(b,"r","0.9"),s(S,"cx","12.5"),s(S,"cy","8"),s(S,"r","0.9"),s(C,"viewBox","0 0 16 16"),s(C,"fill","none"),s(C,"stroke","currentColor"),s(C,"stroke-width","1.8"),s(C,"stroke-linecap","round"),s(C,"aria-hidden","true"),s(C,"class","svelte-1vrsv4c"),s(g,"type","button"),s(g,"class","menu-btn svelte-1vrsv4c"),s(g,"aria-haspopup","menu"),s(g,"aria-expanded",L=t[12]===t[63].name),s(g,"aria-label",R=t[16].more_actions),s(g,"title",D=t[16].more_actions),s(h,"class","row-actions svelte-1vrsv4c"),s(n,"class","network-item svelte-1vrsv4c"),s(n,"role","button"),s(n,"tabindex","0"),s(n,"aria-current",M=t[17].room===t[63].name?"true":void 0),s(n,"aria-label",J=t[63].name),X(n,"active",t[17].room===t[63].name),X(n,"disabled",!t[17].connected&&!t[11]),this.first=n},m(G,O){Y(G,n,O),l(n,o),l(n,i),l(n,r),l(r,c),l(c,f),l(r,_),l(r,m),z.m(m,null),l(n,d),l(n,h),l(h,g),l(g,C),l(C,w),l(C,b),l(C,S),l(h,y),U&&U.m(h,null),l(n,E),H||(p=[W(g,"click",I),W(n,"click",le),W(n,"keydown",oe)],H=!0)},p(G,O){t=G,O[0]&131073&&X(o,"on",t[17].room===t[63].name),O[0]&1&&u!==(u=t[63].name+"")&&j(f,u),Z===(Z=T(t))&&z?z.p(t,O):(z.d(1),z=Z(t),z&&(z.c(),z.m(m,null))),O[0]&4097&&L!==(L=t[12]===t[63].name)&&s(g,"aria-expanded",L),O[0]&65536&&R!==(R=t[16].more_actions)&&s(g,"aria-label",R),O[0]&65536&&D!==(D=t[16].more_actions)&&s(g,"title",D),t[12]===t[63].name?U?U.p(t,O):(U=wo(t),U.c(),U.m(h,null)):U&&(U.d(1),U=null),O[0]&131073&&M!==(M=t[17].room===t[63].name?"true":void 0)&&s(n,"aria-current",M),O[0]&1&&J!==(J=t[63].name)&&s(n,"aria-label",J),O[0]&131073&&X(n,"active",t[17].room===t[63].name),O[0]&133120&&X(n,"disabled",!t[17].connected&&!t[11])},d(G){G&&V(n),z.d(),U&&U.d(),H=!1,Re(p)}}}function Co(e){let t,n,o,i=e[16].create_network+"",r,c,u,f=e[16].network_name+"",_,m,d,h,g,C,w,b,S=e[16].password_opt+"",L,R,D,y,E,M,J,H,p=e[16].cancel+"",T,Z,z,I=e[16].create+"",U,le,oe,G=e[9]&&Ao(e),O=e[7]&&Lo(e);return{c(){t=a("div"),n=a("div"),o=a("h3"),r=k(i),c=v(),u=a("label"),_=k(f),m=v(),d=a("input"),C=v(),G&&G.c(),w=v(),b=a("label"),L=k(S),R=v(),D=a("input"),E=v(),O&&O.c(),M=v(),J=a("div"),H=a("button"),T=k(p),Z=v(),z=a("button"),U=k(I),s(o,"id","create-title"),s(o,"class","svelte-1vrsv4c"),s(u,"class","sr-only svelte-1vrsv4c"),s(u,"for","create-name"),s(d,"id","create-name"),s(d,"type","text"),s(d,"placeholder",h=e[16].network_name),s(d,"aria-invalid",g=!!e[9]),s(d,"class","svelte-1vrsv4c"),X(d,"invalid",e[9]),s(b,"class","sr-only svelte-1vrsv4c"),s(b,"for","create-pass"),s(D,"id","create-pass"),s(D,"type","password"),s(D,"placeholder",y=e[16].password_opt),s(H,"class","btn"),s(z,"class","btn primary"),s(J,"class","modal-actions svelte-1vrsv4c"),s(n,"class","modal svelte-1vrsv4c"),s(n,"role","dialog"),s(n,"aria-modal","true"),s(n,"aria-labelledby","create-title"),s(t,"class","modal-overlay svelte-1vrsv4c"),s(t,"role","presentation")},m(N,B){Y(N,t,B),l(t,n),l(n,o),l(o,r),l(n,c),l(n,u),l(u,_),l(n,m),l(n,d),_e(d,e[3]),l(n,C),G&&G.m(n,null),l(n,w),l(n,b),l(b,L),l(n,R),l(n,D),_e(D,e[4]),l(n,E),O&&O.m(n,null),l(n,M),l(n,J),l(J,H),l(H,T),l(J,Z),l(J,z),l(z,U),e[47](n),le||(oe=[W(d,"input",e[45]),W(D,"input",e[46]),W(H,"click",e[19]),W(z,"click",e[22]),W(n,"click",Dt(e[34])),W(n,"keydown",Dt(e[35])),W(t,"click",e[19]),W(t,"keydown",e[48])],le=!0)},p(N,B){B[0]&65536&&i!==(i=N[16].create_network+"")&&j(r,i),B[0]&65536&&f!==(f=N[16].network_name+"")&&j(_,f),B[0]&65536&&h!==(h=N[16].network_name)&&s(d,"placeholder",h),B[0]&512&&g!==(g=!!N[9])&&s(d,"aria-invalid",g),B[0]&8&&d.value!==N[3]&&_e(d,N[3]),B[0]&512&&X(d,"invalid",N[9]),N[9]?G?G.p(N,B):(G=Ao(N),G.c(),G.m(n,w)):G&&(G.d(1),G=null),B[0]&65536&&S!==(S=N[16].password_opt+"")&&j(L,S),B[0]&65536&&y!==(y=N[16].password_opt)&&s(D,"placeholder",y),B[0]&16&&D.value!==N[4]&&_e(D,N[4]),N[7]?O?O.p(N,B):(O=Lo(N),O.c(),O.m(n,M)):O&&(O.d(1),O=null),B[0]&65536&&p!==(p=N[16].cancel+"")&&j(T,p),B[0]&65536&&I!==(I=N[16].create+"")&&j(U,I)},d(N){N&&V(t),G&&G.d(),O&&O.d(),e[47](null),le=!1,Re(oe)}}}function Ao(e){let t,n;return{c(){t=a("span"),n=k(e[9]),s(t,"class","field-error svelte-1vrsv4c"),s(t,"role","alert")},m(o,i){Y(o,t,i),l(t,n)},p(o,i){i[0]&512&&j(n,o[9])},d(o){o&&V(t)}}}function Lo(e){let t,n;return{c(){t=a("div"),n=k(e[7]),s(t,"class","modal-error svelte-1vrsv4c"),s(t,"role","alert")},m(o,i){Y(o,t,i),l(t,n)},p(o,i){i[0]&128&&j(n,o[7])},d(o){o&&V(t)}}}function Eo(e){let t,n,o,i=e[16].join_network+"",r,c,u,f=e[16].network_name+"",_,m,d,h,g,C,w,b,S=e[16].password_opt+"",L,R,D,y,E,M,J,H,p=e[16].cancel+"",T,Z,z,I=e[16].join+"",U,le,oe,G=e[10]&&Po(e),O=e[8]&&Do(e);return{c(){t=a("div"),n=a("div"),o=a("h3"),r=k(i),c=v(),u=a("label"),_=k(f),m=v(),d=a("input"),C=v(),G&&G.c(),w=v(),b=a("label"),L=k(S),R=v(),D=a("input"),E=v(),O&&O.c(),M=v(),J=a("div"),H=a("button"),T=k(p),Z=v(),z=a("button"),U=k(I),s(o,"id","join-title"),s(o,"class","svelte-1vrsv4c"),s(u,"class","sr-only svelte-1vrsv4c"),s(u,"for","join-name"),s(d,"id","join-name"),s(d,"type","text"),s(d,"placeholder",h=e[16].network_name),s(d,"aria-invalid",g=!!e[10]),s(d,"class","svelte-1vrsv4c"),X(d,"invalid",e[10]),s(b,"class","sr-only svelte-1vrsv4c"),s(b,"for","join-pass"),s(D,"id","join-pass"),s(D,"type","password"),s(D,"placeholder",y=e[16].password_opt),s(H,"class","btn"),s(z,"class","btn primary"),s(J,"class","modal-actions svelte-1vrsv4c"),s(n,"class","modal svelte-1vrsv4c"),s(n,"role","dialog"),s(n,"aria-modal","true"),s(n,"aria-labelledby","join-title"),s(t,"class","modal-overlay svelte-1vrsv4c"),s(t,"role","presentation")},m(N,B){Y(N,t,B),l(t,n),l(n,o),l(o,r),l(n,c),l(n,u),l(u,_),l(n,m),l(n,d),_e(d,e[5]),l(n,C),G&&G.m(n,null),l(n,w),l(n,b),l(b,L),l(n,R),l(n,D),_e(D,e[6]),l(n,E),O&&O.m(n,null),l(n,M),l(n,J),l(J,H),l(H,T),l(J,Z),l(J,z),l(z,U),e[52](n),le||(oe=[W(d,"input",e[49]),W(D,"input",e[50]),W(H,"click",e[21]),W(z,"click",e[51]),W(n,"click",Dt(e[32])),W(n,"keydown",Dt(e[33])),W(t,"click",e[21]),W(t,"keydown",e[53])],le=!0)},p(N,B){B[0]&65536&&i!==(i=N[16].join_network+"")&&j(r,i),B[0]&65536&&f!==(f=N[16].network_name+"")&&j(_,f),B[0]&65536&&h!==(h=N[16].network_name)&&s(d,"placeholder",h),B[0]&1024&&g!==(g=!!N[10])&&s(d,"aria-invalid",g),B[0]&32&&d.value!==N[5]&&_e(d,N[5]),B[0]&1024&&X(d,"invalid",N[10]),N[10]?G?G.p(N,B):(G=Po(N),G.c(),G.m(n,w)):G&&(G.d(1),G=null),B[0]&65536&&S!==(S=N[16].password_opt+"")&&j(L,S),B[0]&65536&&y!==(y=N[16].password_opt)&&s(D,"placeholder",y),B[0]&64&&D.value!==N[6]&&_e(D,N[6]),N[8]?O?O.p(N,B):(O=Do(N),O.c(),O.m(n,M)):O&&(O.d(1),O=null),B[0]&65536&&p!==(p=N[16].cancel+"")&&j(T,p),B[0]&65536&&I!==(I=N[16].join+"")&&j(U,I)},d(N){N&&V(t),G&&G.d(),O&&O.d(),e[52](null),le=!1,Re(oe)}}}function Po(e){let t,n;return{c(){t=a("span"),n=k(e[10]),s(t,"class","field-error svelte-1vrsv4c"),s(t,"role","alert")},m(o,i){Y(o,t,i),l(t,n)},p(o,i){i[0]&1024&&j(n,o[10])},d(o){o&&V(t)}}}function Do(e){let t,n;return{c(){t=a("div"),n=k(e[8]),s(t,"class","modal-error svelte-1vrsv4c"),s(t,"role","alert")},m(o,i){Y(o,t,i),l(t,n)},p(o,i){i[0]&256&&j(n,o[8])},d(o){o&&V(t)}}}function Ui(e){let t,n,o,i=e[16].networks+"",r,c,u,f=ke(e[16].network_count,{n:e[0].length})+"",_,m,d,h,g=e[16].create_network+"",C,w,b,S,L=e[16].join_network+"",R,D,y,E,M=[],J=new Map,H,p,T,Z,z,I,U,le,oe=_t(e[0]);const G=F=>F[63].name;for(let F=0;Fn(16,o=P)),pe(e,We,P=>n(58,i=P)),pe(e,Fe,P=>n(17,r=P));let c=[],u=!1,f=!1,_="",m="",d="",h="",g="",C="",w="",b="",S="",L="",R="",D=null,y,E,M=null;Ml(async()=>{n(0,c=await window.go.main.App.GetSavedRooms());const P=await window.go.main.App.LoadConfig();P.serverAddr&&(S=P.serverAddr),P.nickname&&(L=P.nickname),window.addEventListener("keydown",J),window.addEventListener("click",H)}),oi(()=>{window.removeEventListener("keydown",J),window.removeEventListener("click",H)});function J(P){if(P.key==="Escape"){if(D){n(12,D=null);return}if(u){Z();return}if(f){I();return}}}function H(){n(12,D=null)}async function p(){n(0,c=await window.go.main.App.GetSavedRooms())}function T(){n(7,g=""),n(9,w=""),n(1,u=!0),yn()}function Z(){n(1,u=!1),n(7,g=""),n(9,w="")}function z(){n(8,C=""),n(10,b=""),n(2,f=!0),yn()}function I(){n(2,f=!1),n(8,C=""),n(10,b="")}async function U(){if(n(9,w=""),n(7,g=""),!_.trim()){n(9,w=o.field_required);return}try{await window.go.main.App.CreateRoom(_,m),se(ke(o.notif_created,{name:_})),Et(We,i="network",i),Z(),n(3,_=""),n(4,m=""),await p()}catch(P){n(7,g=ke(o.error_create,{err:P})),se(g,"error")}}async function le(P,ee){n(10,b=""),n(8,C="");const Ce=P||d,me=ee!==void 0?ee:h;if(!Ce.trim()){n(10,b=o.field_required);return}try{await window.go.main.App.JoinRoom(Ce,me),se(ke(o.notif_joined,{name:Ce})),Et(We,i="network",i),I(),n(5,d=""),n(6,h=""),await p()}catch(te){n(8,C=ke(o.error_join,{err:te})),se(C,"error"),f||(n(5,d=Ce),n(6,h=""),n(2,f=!0))}}async function oe(){n(15,M={message:o.confirm_leave,confirmLabel:o.leave,run:async()=>{try{await window.go.main.App.LeaveRoom(),se(o.notif_left),await p()}catch(P){se(ke(o.error_leave,{err:P}),"error")}}})}async function G(P){const ee=r.room===P;n(15,M={message:ee?o.confirm_leave_remove:ke(o.confirm_remove,{name:P}),confirmLabel:o.remove,danger:!0,run:async()=>{try{ee&&await window.go.main.App.LeaveRoom(),await window.go.main.App.RemoveSavedRoom(P),se(ke(o.notif_removed,{name:P})),await p()}catch(Ce){se(ke(o.error_remove,{err:Ce}),"error")}}})}async function O(P){n(15,M={message:ke(o.confirm_delete,{name:P}),confirmLabel:o.delete_room,danger:!0,run:async()=>{try{await window.go.main.App.DeleteRoom(P),await window.go.main.App.RemoveSavedRoom(P),se(ke(o.notif_deleted,{name:P})),await p()}catch(ee){se(String(ee),"error")}}})}async function N(P){try{const ee=await window.go.main.App.FormatInvite(P.name,P.password||"");await window.go.main.App.CopyText(ee);const Ce=String(ee).split("|"),me=Ce.length>=3&&Ce[2]!=="";P.locked&&!me?se(o.invite_no_password,"info"):se(o.invite_copied)}catch(ee){se(String(ee),"error")}}async function B(){try{const P=await window.go.main.App.LoadConfig();P.serverAddr&&(S=P.serverAddr),P.nickname&&(L=P.nickname)}catch{}}async function F(P,ee,Ce){if(!R){n(11,R=P),n(12,D=null);try{await B();const me=Ce||S;if(!r.connected){if(!me||!L){se(o.notif_need_config,"error"),Et(We,i="connect",i);return}const te=await window.go.main.App.Connect(me,L);Fe.set(te),await p()}await window.go.main.App.JoinRoom(P,ee||""),Et(We,i="network",i),await p()}catch(me){const te=ke(o.error_join,{err:me});se(te,"error"),n(5,d=P),n(6,h=""),n(8,C=te),n(2,f=!0)}finally{n(11,R="")}}}function q(P,ee){(P.key==="Enter"||P.key===" ")&&(P.preventDefault(),F(ee.name,ee.password,ee.server))}function ne(P,ee){P.stopPropagation(),n(12,D=D===ee?null:ee)}function ie(P,ee){P.stopPropagation(),n(12,D=null),ee()}function ye(P){Lt.call(this,e,P)}function Me(P){Lt.call(this,e,P)}function Te(P){Lt.call(this,e,P)}function ge(P){Lt.call(this,e,P)}function fe(P){Lt.call(this,e,P)}function De(P){Lt.call(this,e,P)}const Se=(P,ee)=>ne(ee,P.name),re=(P,ee)=>ie(ee,()=>N(P)),ce=P=>ie(P,oe),Ke=(P,ee)=>ie(ee,()=>G(P.name)),Le=(P,ee)=>ie(ee,()=>O(P.name)),Ee=P=>F(P.name,P.password,P.server),je=(P,ee)=>q(ee,P);function we(){_=this.value,n(3,_)}function ve(){m=this.value,n(4,m)}function x(P){_n[P?"unshift":"push"](()=>{y=P,n(13,y)})}const ae=P=>P.key==="Escape"&&Z();function nt(){d=this.value,n(5,d)}function Ne(){h=this.value,n(6,h)}const Be=()=>le();function He(P){_n[P?"unshift":"push"](()=>{E=P,n(14,E)})}return[c,u,f,_,m,d,h,g,C,w,b,R,D,y,E,M,o,r,T,Z,z,I,U,le,oe,G,O,N,F,q,ne,ie,ye,Me,Te,ge,fe,De,Se,re,ce,Ke,Le,Ee,je,we,ve,x,ae,nt,Ne,Be,He,P=>P.key==="Escape"&&I(),async()=>{const P=M?.run;n(15,M=null),P&&await P()},()=>n(15,M=null)]}class Wi extends yt{constructor(t){super(),wt(this,t,qi,Ui,pt,{},null,[-1,-1,-1])}}function No(e,t,n){const o=e.slice();o[23]=t[n],o[26]=n;const i=Gi(o[23]);return o[24]=i,o}function Mo(e){let t,n=e[5].room_chat+"",o,i,r,c,u=e[3]>0&&To(e);return{c(){t=a("button"),o=k(n),i=v(),u&&u.c(),s(t,"type","button"),s(t,"class","bezel")},m(f,_){Y(f,t,_),l(t,o),l(t,i),u&&u.m(t,null),r||(c=W(t,"click",e[8]),r=!0)},p(f,_){_&32&&n!==(n=f[5].room_chat+"")&&j(o,n),f[3]>0?u?u.p(f,_):(u=To(f),u.c(),u.m(t,null)):u&&(u.d(1),u=null)},d(f){f&&V(t),u&&u.d(),r=!1,c()}}}function To(e){let t,n;return{c(){t=a("span"),n=k(e[3]),s(t,"class","badge svelte-1ywbfno")},m(o,i){Y(o,t,i),l(t,n)},p(o,i){i&8&&j(n,o[3])},d(o){o&&V(t)}}}function Io(e){let t,n=e[5].status_relay_degraded+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","section-hint fault svelte-1ywbfno")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&32&&n!==(n=i[5].status_relay_degraded+"")&&j(o,n)},d(i){i&&V(t)}}}function Ro(e){let t,n,o=e[5].no_peers+"",i,r,c,u=e[5].no_peers_desc+"",f,_;return{c(){t=a("div"),n=a("div"),i=k(o),r=v(),c=a("p"),f=k(u),_=v(),s(n,"class","empty-label svelte-1ywbfno"),s(c,"class","empty-desc svelte-1ywbfno"),s(t,"class","empty svelte-1ywbfno")},m(m,d){Y(m,t,d),l(t,n),l(n,i),l(t,r),l(t,c),l(c,f),l(t,_)},p(m,d){d&32&&o!==(o=m[5].no_peers+"")&&j(i,o),d&32&&u!==(u=m[5].no_peers_desc+"")&&j(f,u)},d(m){m&&V(t)}}}function Oo(e){let t,n=e[11](e[23].id)+"",o;return{c(){t=a("span"),o=k(n),s(t,"class","badge svelte-1ywbfno")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&16&&n!==(n=i[11](i[23].id)+"")&&j(o,n)},d(i){i&&V(t)}}}function Fi(e){let t;return{c(){t=k("…")},m(n,o){Y(n,t,o)},p:Pe,d(n){n&&V(t)}}}function Ki(e){let t=e[23].ping+"",n,o;return{c(){n=k(t),o=k("ms")},m(i,r){Y(i,n,r),Y(i,o,r)},p(i,r){r&16&&t!==(t=i[23].ping+"")&&j(n,t)},d(i){i&&(V(n),V(o))}}}function Bi(e){let t;return{c(){t=k("—")},m(n,o){Y(n,t,o)},p:Pe,d(n){n&&V(t)}}}function zo(e,t){let n,o,i=gs(t[26]+1)+"",r,c,u,f=(t[23].nickname||t[23].id)+"",_,m,d=t[11](t[23].id)>0,h,g,C=(t[23].virtualIP||t[5].na)+"",w,b,S,L,R,D,y,E,M=(t[23].connected&&t[23].path?t[12](t[23].path):t[5].path_unknown)+"",J,H,p,T,Z,z,I,U=t[5].ping_peer+"",le,oe,G,O,N=t[5].chat+"",B,F,q,ne,ie=d&&Oo(t);function ye(){return t[19](t[23])}function Me(...re){return t[20](t[23],...re)}function Te(re,ce){return re[23].connected?re[23].ping>=0&&!re[2][re[23].id]?Ki:Fi:Bi}let ge=Te(t),fe=ge(t);function De(){return t[21](t[23])}function Se(){return t[22](t[23])}return{key:e,first:null,c(){n=a("article"),o=a("span"),r=k(i),c=v(),u=a("div"),_=k(f),m=v(),ie&&ie.c(),h=v(),g=a("button"),w=k(C),b=v(),S=a("span"),L=Pt("svg"),R=Pt("line"),D=Pt("circle"),y=Pt("circle"),E=v(),J=k(M),p=v(),T=a("span"),fe.c(),Z=v(),z=a("div"),I=a("button"),le=k(U),G=v(),O=a("button"),B=k(N),F=v(),s(o,"class","ch svelte-1ywbfno"),s(u,"class","nickname svelte-1ywbfno"),s(g,"type","button"),s(g,"class","peer-ip svelte-1ywbfno"),s(R,"x1","3"),s(R,"y1","5"),s(R,"x2","37"),s(R,"y2","5"),s(R,"class","svelte-1ywbfno"),s(D,"cx","5"),s(D,"cy","5"),s(D,"r","2"),s(D,"class","svelte-1ywbfno"),s(y,"cx","35"),s(y,"cy","5"),s(y,"r","2"),s(y,"class","svelte-1ywbfno"),s(L,"class","trace svelte-1ywbfno"),s(L,"viewBox","0 0 40 10"),s(L,"aria-hidden","true"),s(S,"class","col-path svelte-1ywbfno"),s(S,"title",H=t[13](t[23].path)),X(S,"live",t[24]==="live"),X(S,"fault",t[24]==="fault"),X(S,"idle",t[24]==="idle"),s(T,"class","col-ping svelte-1ywbfno"),X(T,"probing",t[23].connected&&(t[23].ping<0||t[2][t[23].id])),s(I,"class","btn btn-ghost btn-sm svelte-1ywbfno"),I.disabled=oe=!t[23].connected||!!t[2][t[23].id],s(O,"class","btn btn-ghost btn-sm svelte-1ywbfno"),s(z,"class","peer-actions svelte-1ywbfno"),s(n,"class","peer-row svelte-1ywbfno"),s(n,"role","listitem"),this.first=n},m(re,ce){Y(re,n,ce),l(n,o),l(o,r),l(n,c),l(n,u),l(u,_),l(u,m),ie&&ie.m(u,null),l(n,h),l(n,g),l(g,w),l(n,b),l(n,S),l(S,L),l(L,R),l(L,D),l(L,y),l(S,E),l(S,J),l(n,p),l(n,T),fe.m(T,null),l(n,Z),l(n,z),l(z,I),l(I,le),l(z,G),l(z,O),l(O,B),l(n,F),q||(ne=[W(g,"click",ye),W(g,"keydown",Me),W(I,"click",De),W(O,"click",Se)],q=!0)},p(re,ce){t=re,ce&16&&i!==(i=gs(t[26]+1)+"")&&j(r,i),ce&16&&f!==(f=(t[23].nickname||t[23].id)+"")&&j(_,f),ce&16&&(d=t[11](t[23].id)>0),d?ie?ie.p(t,ce):(ie=Oo(t),ie.c(),ie.m(u,null)):ie&&(ie.d(1),ie=null),ce&48&&C!==(C=(t[23].virtualIP||t[5].na)+"")&&j(w,C),ce&48&&M!==(M=(t[23].connected&&t[23].path?t[12](t[23].path):t[5].path_unknown)+"")&&j(J,M),ce&16&&H!==(H=t[13](t[23].path))&&s(S,"title",H),ce&16&&X(S,"live",t[24]==="live"),ce&16&&X(S,"fault",t[24]==="fault"),ce&16&&X(S,"idle",t[24]==="idle"),ge===(ge=Te(t))&&fe?fe.p(t,ce):(fe.d(1),fe=ge(t),fe&&(fe.c(),fe.m(T,null))),ce&20&&X(T,"probing",t[23].connected&&(t[23].ping<0||t[2][t[23].id])),ce&32&&U!==(U=t[5].ping_peer+"")&&j(le,U),ce&20&&oe!==(oe=!t[23].connected||!!t[2][t[23].id])&&(I.disabled=oe),ce&32&&N!==(N=t[5].chat+"")&&j(B,N)},d(re){re&&V(n),ie&&ie.d(),fe.d(),q=!1,Re(ne)}}}function Hi(e){let t,n,o,i=e[5].channels+"",r,c,u=(e[1].room||e[5].peers_list)+"",f,_,m,d,h,g,C,w=e[5].col_ch+"",b,S,L,R=e[5].col_nickname+"",D,y,E,M=e[5].col_ip+"",J,H,p,T=e[5].col_path+"",Z,z,I,U=e[5].col_ping+"",le,oe,G,O,N,B,F,q,ne,ie=(e[6].nickname||e[5].you)+"",ye,Me,Te,ge=e[5].me+"",fe,De,Se,re=(e[1].virtualIP||e[5].na)+"",ce,Ke,Le,Ee=e[5].path_unknown+"",je,we,ve,x,ae,nt,Ne=[],Be=new Map,He,at,Ie,be=e[1].room&&Mo(e),P=e[0]==="relay"&&Io(e),ee=_t(e[4]);const Ce=te=>te[23].id;for(let te=0;ten(5,r=p)),pe(e,js,p=>n(15,c=p)),pe(e,Fe,p=>n(1,u=p)),pe(e,_s,p=>n(16,f=p)),pe(e,Ze,p=>n(6,_=p));let{pathAggregate:m="disconnected"}=t,d={};function h(p){Nl.set({id:p.id,nickname:p.nickname||p.id}),ms("peer:"+p.id),it.set(!0)}function g(){Nl.set(null),ms("room:"+(u.room||"network")),it.set(!0)}async function C(p){if(p)try{await window.go.main.App.CopyText(p),se(r.copied)}catch{}}async function w(p){if(!(!p?.id||!p.connected||d[p.id])){n(2,d={...d,[p.id]:!0});try{await window.go.main.App.PingPeer(p.id),se(ke(r.ping_sent,{name:p.nickname||p.id}))}catch(T){se(ke(r.error_ping,{err:T}),"error")}finally{setTimeout(()=>{const T={...d};delete T[p.id],n(2,d=T)},800)}}}function b(p){return c["peer:"+p]||0}function S(p){return p==="p2p"?r.path_p2p:p==="relay"?r.path_relay:p==="ws"?r.path_ws:r.path_unknown}function L(p){return p==="p2p"?r.path_p2p_tip:p==="relay"?r.path_relay_tip:p==="ws"?r.path_ws_tip:""}function R(p,T){(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),C(T))}const D=()=>C(u.virtualIP),y=p=>R(p,u.virtualIP),E=p=>C(p.virtualIP),M=(p,T)=>R(T,p.virtualIP),J=p=>w(p),H=p=>h(p);return e.$$set=p=>{"pathAggregate"in p&&n(0,m=p.pathAggregate)},e.$$.update=()=>{e.$$.dirty&65536&&n(4,o=[...f].sort((p,T)=>(p.nickname||"").localeCompare(T.nickname||""))),e.$$.dirty&32770&&n(3,i=c["room:"+(u.room||"network")]||0)},[m,u,d,i,o,r,_,h,g,C,w,b,S,L,R,c,f,D,y,E,M,J,H]}class Yi extends yt{constructor(t){super(),wt(this,t,Vi,Hi,pt,{pathAggregate:0})}}function Xi(e){let t,n,o,i,r,c,u,f,_,m,d,h;return{c(){t=a("button"),n=a("span"),o=v(),i=a("span"),i.innerHTML='',r=v(),c=a("span"),c.innerHTML='',u=v(),f=a("span"),s(n,"class","theme-switch-track"),s(i,"class","theme-switch-icon theme-switch-sun"),s(i,"aria-hidden","true"),s(c,"class","theme-switch-icon theme-switch-moon"),s(c,"aria-hidden","true"),s(f,"class","theme-switch-thumb"),s(t,"type","button"),s(t,"class","theme-switch"),s(t,"aria-label",_=e[1]?e[2].theme_light:e[2].theme_dark),s(t,"title",m=e[2].theme),X(t,"is-dark",e[1]),X(t,"is-chrome",e[0])},m(g,C){Y(g,t,C),l(t,n),l(t,o),l(t,i),l(t,r),l(t,c),l(t,u),l(t,f),d||(h=W(t,"click",e[3]),d=!0)},p(g,[C]){C&6&&_!==(_=g[1]?g[2].theme_light:g[2].theme_dark)&&s(t,"aria-label",_),C&4&&m!==(m=g[2].theme)&&s(t,"title",m),C&2&&X(t,"is-dark",g[1]),C&1&&X(t,"is-chrome",g[0])},i:Pe,o:Pe,d(g){g&&V(t),d=!1,h()}}}function Zi(e,t,n){let o,i,r;pe(e,Ze,_=>n(4,i=_)),pe(e,jt,_=>n(2,r=_));let{chrome:c=!1}=t;async function u(_){Ze.update(m=>({...m,theme:_}));try{const m=fn(Ze);await window.go.main.App.SaveSettings({...m,theme:_})}catch{}}function f(){u(o?"light":"dark")}return e.$$set=_=>{"chrome"in _&&n(0,c=_.chrome)},e.$$.update=()=>{e.$$.dirty&16&&n(1,o=(i.theme||"dark")!=="light")},[c,o,r,f,i]}class ai extends yt{constructor(t){super(),wt(this,t,Zi,Xi,pt,{chrome:0})}}function Uo(e,t,n){const o=e.slice();return o[18]=t[n],o[20]=n,o}function qo(e){let t;return{c(){t=a("span"),s(t,"class","seg svelte-10p19bf"),X(t,"on",e[20]n(1,f=y)),pe(e,Fe,y=>n(8,_=y)),pe(e,We,y=>n(9,m=y));let{pathAggregate:d="disconnected"}=t,{relayCount:h=0}=t;const g={direct:{n:7,tone:"live"},relay:{n:5,tone:"fault"},reconnecting:{n:3,tone:"fault"},disconnected:{n:0,tone:"idle"}};function C(){We.set("network")}function w(){We.set(m==="settings"?"network":"settings")}async function b(){if(_.virtualIP)try{await window.go.main.App.CopyText(_.virtualIP),se(f.copied)}catch{}}let S=!1;async function L(){n(2,S=!1);try{await window.go.main.App.Disconnect(),Fe.set({connected:!1,reconnecting:!1,server:"",room:"",virtualIP:"",peerCount:0,isOwner:!1,phase:"idle"}),it.set(!1),We.set("connect"),se(f.notif_disconnected)}catch(y){se(ke(f.error_disconnect,{err:y}),"error")}}const R=()=>n(2,S=!0),D=()=>n(2,S=!1);return e.$$set=y=>{"pathAggregate"in y&&n(14,d=y.pathAggregate),"relayCount"in y&&n(0,h=y.relayCount)},e.$$.update=()=>{e.$$.dirty&16384&&n(7,o=d==="direct"),e.$$.dirty&16384&&n(6,i=d==="relay"||d==="reconnecting"),e.$$.dirty&16384&&n(5,r=d==="disconnected"),e.$$.dirty&16384&&n(4,c=g[d]||g.disconnected),e.$$.dirty&16386&&n(3,u=d==="reconnecting"?f.status_reconnecting:d==="relay"?f.status_relay_degraded:d==="direct"?f.status_direct:f.status_disconnected)},[h,f,S,u,c,r,i,o,_,m,C,w,b,L,d,R,D]}class xi extends yt{constructor(t){super(),wt(this,t,$i,Qi,pt,{pathAggregate:14,relayCount:0})}}function ea(e){let t,n,o,i,r=e[0].server+"",c,u,f,_=e[1].server+"",m,d,h,g,C=e[0].virtual_ip+"",w,b,S,L=(e[1].virtualIP||e[0].na)+"",R,D,y,E=e[0].disconnect+"",M,J,H;function p(z,I){return z[1].reconnecting?la:na}let T=p(e),Z=T(e);return{c(){t=a("div"),Z.c(),n=v(),o=a("div"),i=a("span"),c=k(r),u=v(),f=a("span"),m=k(_),d=v(),h=a("div"),g=a("span"),w=k(C),b=v(),S=a("span"),R=k(L),D=v(),y=a("button"),M=k(E),s(f,"class","info-value svelte-1br9j49"),s(o,"class","info-line svelte-1br9j49"),s(S,"class","info-value svelte-1br9j49"),s(h,"class","info-line svelte-1br9j49"),s(y,"class","disconnect-btn svelte-1br9j49"),s(t,"class","connected-info svelte-1br9j49")},m(z,I){Y(z,t,I),Z.m(t,null),l(t,n),l(t,o),l(o,i),l(i,c),l(o,u),l(o,f),l(f,m),l(t,d),l(t,h),l(h,g),l(g,w),l(h,b),l(h,S),l(S,R),l(t,D),l(t,y),l(y,M),J||(H=W(y,"click",e[12]),J=!0)},p(z,I){T===(T=p(z))&&Z?Z.p(z,I):(Z.d(1),Z=T(z),Z&&(Z.c(),Z.m(t,n))),I&1&&r!==(r=z[0].server+"")&&j(c,r),I&2&&_!==(_=z[1].server+"")&&j(m,_),I&1&&C!==(C=z[0].virtual_ip+"")&&j(w,C),I&3&&L!==(L=(z[1].virtualIP||z[0].na)+"")&&j(R,L),I&1&&E!==(E=z[0].disconnect+"")&&j(M,E)},d(z){z&&V(t),Z.d(),J=!1,H()}}}function ta(e){let t,n,o,i=e[0].brand+"",r,c,u,f=e[0].tagline+"",_,m,d,h,g=e[0].server_addr+"",C,w,b,S,L,R,D,y,E,M=e[0].nickname+"",J,H,p,T,Z,z,I,U,le=(e[5]?e[0].connecting:e[0].connect)+"",oe,G,O,N,B,F=e[0].paste_invite+"",q,ne,ie,ye,Me=e[0].paste_invite+"",Te,ge,fe,De,Se,re,ce=e[0].join+"",Ke,Le,Ee,je=e[7].serverAddr&&Fo(e),we=e[7].nickname&&Ko(e),ve=(e[5]||e[1].phase==="dialing"||e[1].phase==="auth")&&Bo(e);return{c(){t=a("div"),n=a("div"),o=a("h1"),r=k(i),c=v(),u=a("p"),_=k(f),m=v(),d=a("div"),h=a("label"),C=k(g),w=v(),b=a("input"),R=v(),je&&je.c(),D=v(),y=a("div"),E=a("label"),J=k(M),H=v(),p=a("input"),z=v(),we&&we.c(),I=v(),U=a("button"),oe=k(le),G=v(),ve&&ve.c(),O=v(),N=a("details"),B=a("summary"),q=k(F),ne=v(),ie=a("div"),ye=a("label"),Te=k(Me),ge=v(),fe=a("input"),Se=v(),re=a("button"),Ke=k(ce),s(o,"class","brand svelte-1br9j49"),s(u,"class","tagline svelte-1br9j49"),s(n,"class","brand-block svelte-1br9j49"),s(h,"for","server-input"),s(h,"class","svelte-1br9j49"),s(b,"id","server-input"),s(b,"type","text"),s(b,"placeholder","host:8080"),s(b,"aria-invalid",S=!!e[7].serverAddr),s(b,"aria-describedby",L=e[7].serverAddr?"server-err":void 0),s(b,"class","svelte-1br9j49"),X(b,"invalid",e[7].serverAddr),s(d,"class","field svelte-1br9j49"),s(E,"for","nick-input"),s(E,"class","svelte-1br9j49"),s(p,"id","nick-input"),s(p,"type","text"),s(p,"placeholder","Player"),s(p,"aria-invalid",T=!!e[7].nickname),s(p,"aria-describedby",Z=e[7].nickname?"nick-err":void 0),s(p,"class","svelte-1br9j49"),X(p,"invalid",e[7].nickname),s(y,"class","field svelte-1br9j49"),s(U,"class","connect-btn svelte-1br9j49"),U.disabled=e[5],s(B,"class","svelte-1br9j49"),s(ye,"for","invite-input"),s(ye,"class","sr-only svelte-1br9j49"),s(fe,"id","invite-input"),s(fe,"type","text"),s(fe,"placeholder",De=e[0].invite_placeholder),s(fe,"class","svelte-1br9j49"),s(re,"class","secondary-btn svelte-1br9j49"),re.disabled=e[5],s(ie,"class","invite-block svelte-1br9j49"),s(N,"class","invite-disclosure svelte-1br9j49"),s(t,"class","connect-form svelte-1br9j49")},m(x,ae){Y(x,t,ae),l(t,n),l(n,o),l(o,r),l(n,c),l(n,u),l(u,_),l(t,m),l(t,d),l(d,h),l(h,C),l(d,w),l(d,b),_e(b,e[2]),l(d,R),je&&je.m(d,null),l(t,D),l(t,y),l(y,E),l(E,J),l(y,H),l(y,p),_e(p,e[3]),l(y,z),we&&we.m(y,null),l(t,I),l(t,U),l(U,oe),l(t,G),ve&&ve.m(t,null),l(t,O),l(t,N),l(N,B),l(B,q),l(N,ne),l(N,ie),l(ie,ye),l(ye,Te),l(ie,ge),l(ie,fe),_e(fe,e[4]),l(ie,Se),l(ie,re),l(re,Ke),N.open=e[6],Le||(Ee=[W(b,"input",e[13]),W(p,"input",e[14]),W(p,"keydown",e[11]),W(U,"click",e[9]),W(fe,"input",e[15]),W(re,"click",e[10]),W(N,"toggle",e[16])],Le=!0)},p(x,ae){ae&1&&i!==(i=x[0].brand+"")&&j(r,i),ae&1&&f!==(f=x[0].tagline+"")&&j(_,f),ae&1&&g!==(g=x[0].server_addr+"")&&j(C,g),ae&128&&S!==(S=!!x[7].serverAddr)&&s(b,"aria-invalid",S),ae&128&&L!==(L=x[7].serverAddr?"server-err":void 0)&&s(b,"aria-describedby",L),ae&4&&b.value!==x[2]&&_e(b,x[2]),ae&128&&X(b,"invalid",x[7].serverAddr),x[7].serverAddr?je?je.p(x,ae):(je=Fo(x),je.c(),je.m(d,null)):je&&(je.d(1),je=null),ae&1&&M!==(M=x[0].nickname+"")&&j(J,M),ae&128&&T!==(T=!!x[7].nickname)&&s(p,"aria-invalid",T),ae&128&&Z!==(Z=x[7].nickname?"nick-err":void 0)&&s(p,"aria-describedby",Z),ae&8&&p.value!==x[3]&&_e(p,x[3]),ae&128&&X(p,"invalid",x[7].nickname),x[7].nickname?we?we.p(x,ae):(we=Ko(x),we.c(),we.m(y,null)):we&&(we.d(1),we=null),ae&33&&le!==(le=(x[5]?x[0].connecting:x[0].connect)+"")&&j(oe,le),ae&32&&(U.disabled=x[5]),x[5]||x[1].phase==="dialing"||x[1].phase==="auth"?ve?ve.p(x,ae):(ve=Bo(x),ve.c(),ve.m(t,O)):ve&&(ve.d(1),ve=null),ae&1&&F!==(F=x[0].paste_invite+"")&&j(q,F),ae&1&&Me!==(Me=x[0].paste_invite+"")&&j(Te,Me),ae&1&&De!==(De=x[0].invite_placeholder)&&s(fe,"placeholder",De),ae&16&&fe.value!==x[4]&&_e(fe,x[4]),ae&1&&ce!==(ce=x[0].join+"")&&j(Ke,ce),ae&32&&(re.disabled=x[5]),ae&64&&(N.open=x[6])},d(x){x&&V(t),je&&je.d(),we&&we.d(),ve&&ve.d(),Le=!1,Re(Ee)}}}function na(e){let t,n,o,i,r=e[0].connected+"",c;return{c(){t=a("div"),n=a("span"),o=v(),i=a("span"),c=k(r),s(n,"class","sq sq-green svelte-1br9j49"),s(n,"aria-hidden","true"),s(t,"class","connected-line svelte-1br9j49"),s(t,"role","status")},m(u,f){Y(u,t,f),l(t,n),l(t,o),l(t,i),l(i,c)},p(u,f){f&1&&r!==(r=u[0].connected+"")&&j(c,r)},d(u){u&&V(t)}}}function la(e){let t,n,o,i,r=e[0].reconnecting+"",c;return{c(){t=a("div"),n=a("span"),o=v(),i=a("span"),c=k(r),s(n,"class","sq sq-warn svelte-1br9j49"),s(n,"aria-hidden","true"),s(t,"class","connected-line svelte-1br9j49"),s(t,"role","status")},m(u,f){Y(u,t,f),l(t,n),l(t,o),l(t,i),l(i,c)},p(u,f){f&1&&r!==(r=u[0].reconnecting+"")&&j(c,r)},d(u){u&&V(t)}}}function Fo(e){let t,n=e[7].serverAddr+"",o;return{c(){t=a("span"),o=k(n),s(t,"id","server-err"),s(t,"class","field-error svelte-1br9j49"),s(t,"role","alert")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&128&&n!==(n=i[7].serverAddr+"")&&j(o,n)},d(i){i&&V(t)}}}function Ko(e){let t,n=e[7].nickname+"",o;return{c(){t=a("span"),o=k(n),s(t,"id","nick-err"),s(t,"class","field-error svelte-1br9j49"),s(t,"role","alert")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&128&&n!==(n=i[7].nickname+"")&&j(o,n)},d(i){i&&V(t)}}}function Bo(e){let t,n=(e[8]||e[0].connecting)+"",o;return{c(){t=a("div"),o=k(n),s(t,"class","phase svelte-1br9j49"),s(t,"role","status")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&257&&n!==(n=(i[8]||i[0].connecting)+"")&&j(o,n)},d(i){i&&V(t)}}}function sa(e){let t;function n(r,c){return r[1].connected?ea:ta}let o=n(e),i=o(e);return{c(){t=a("div"),i.c(),s(t,"class","connect-view svelte-1br9j49")},m(r,c){Y(r,t,c),i.m(t,null)},p(r,[c]){o===(o=n(r))&&i?i.p(r,c):(i.d(1),i=o(r),i&&(i.c(),i.m(t,null)))},i:Pe,o:Pe,d(r){r&&V(t),i.d()}}}function oa(e,t,n){let o,i,r,c;pe(e,jt,E=>n(0,i=E)),pe(e,Fe,E=>n(1,r=E)),pe(e,Ze,E=>n(17,c=E));let u="",f="",_="",m=!1,d=!1,h={};Ml(async()=>{const E=await window.go.main.App.LoadConfig();E.serverAddr&&n(2,u=E.serverAddr),E.nickname&&n(3,f=E.nickname),E&&Ze.set({...c,...E}),E.autoConnect&&E.serverAddr&&E.nickname&&C()});function g(){const E={};return u.trim()||(E.serverAddr=i.field_required),f.trim()||(E.nickname=i.field_required),n(7,h=E),Object.keys(E).length===0}async function C(){if(!m&&g()){n(5,m=!0),yn();try{const E=await window.go.main.App.Connect(u,f);Fe.set(E),We.set("network"),it.set(!0),se(ke(i.notif_connected,{addr:u})),n(7,h={})}catch(E){se(ke(i.error_connect,{err:E}),"error")}finally{n(5,m=!1)}}}async function w(){if(_.trim()){yn();try{const E=await window.go.main.App.ParseInvite(_.trim());if(!E.server||!E.room){se(i.error_invalid_invite,"error");return}if(n(2,u=E.server),!r.connected){if(!f.trim()){n(7,h={...h,nickname:i.field_required}),se(i.notif_need_config,"error");return}n(5,m=!0);const M=await window.go.main.App.Connect(u,f);Fe.set(M),n(5,m=!1)}await window.go.main.App.JoinRoom(E.room,E.password||""),se(ke(i.notif_joined,{name:E.room})),We.set("network"),it.set(!0)}catch(E){se(ke(i.error_join,{err:E}),"error"),n(5,m=!1)}}}function b(E){E.key==="Enter"&&(E.preventDefault(),C())}async function S(){if(confirm(i.confirm_disconnect))try{await window.go.main.App.Disconnect(),Fe.set({connected:!1,reconnecting:!1,server:"",room:"",virtualIP:"",peerCount:0,isOwner:!1,phase:"idle"}),it.set(!1),We.set("connect"),se(i.notif_disconnected)}catch(E){se(ke(i.error_disconnect,{err:E}),"error")}}function L(){u=this.value,n(2,u)}function R(){f=this.value,n(3,f)}function D(){_=this.value,n(4,_)}function y(){d=this.open,n(6,d)}return e.$$.update=()=>{e.$$.dirty&3&&n(8,o=r.phase==="dialing"?i.phase_dialing:r.phase==="auth"?i.phase_auth:r.phase==="ready"?i.phase_ready:r.phase==="error"?i.phase_error:"")},[i,r,u,f,_,m,d,h,o,C,w,b,S,L,R,D,y]}class ia extends yt{constructor(t){super(),wt(this,t,oa,sa,pt,{})}}function Ho(e,t,n){const o=e.slice();return o[23]=t[n],o[25]=n,o}function Jo(e){let t,n,o=e[0].no_messages+"",i,r,c,u=e[0].no_messages_desc+"",f;return{c(){t=a("div"),n=a("div"),i=k(o),r=v(),c=a("p"),f=k(u),s(n,"class","empty-label svelte-19atdil"),s(c,"class","empty-desc svelte-19atdil"),s(t,"class","empty svelte-19atdil")},m(_,m){Y(_,t,m),l(t,n),l(n,i),l(t,r),l(t,c),l(c,f)},p(_,m){m&1&&o!==(o=_[0].no_messages+"")&&j(i,o),m&1&&u!==(u=_[0].no_messages_desc+"")&&j(f,u)},d(_){_&&V(t)}}}function Go(e){let t,n=e[12](e[23].timestamp)+"",o;return{c(){t=a("div"),o=k(n),s(t,"class","date-sep svelte-19atdil")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&128&&n!==(n=i[12](i[23].timestamp)+"")&&j(o,n)},d(i){i&&V(t)}}}function aa(e){let t,n,o,i=new Date(e[23].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})+"",r,c,u,f=(e[23].isSelf?e[6].nickname||e[0].you_self:e[23].nickname)+"",_,m,d,h,g,C=e[23].message+"",w,b,S=e[23].status==="pending"&&Vo(),L=e[23].status==="failed"&&Yo(e);return{c(){t=a("div"),n=a("div"),o=a("span"),r=k(i),c=v(),u=a("span"),_=k(f),m=v(),S&&S.c(),d=v(),L&&L.c(),h=v(),g=a("div"),w=k(C),b=v(),s(o,"class","msg-time svelte-19atdil"),s(u,"class","msg-nick svelte-19atdil"),s(n,"class","msg-meta svelte-19atdil"),s(g,"class","msg-body svelte-19atdil"),s(t,"class","msg svelte-19atdil"),X(t,"self",e[23].isSelf),X(t,"failed",e[23].status==="failed")},m(R,D){Y(R,t,D),l(t,n),l(n,o),l(o,r),l(n,c),l(n,u),l(u,_),l(n,m),S&&S.m(n,null),l(n,d),L&&L.m(n,null),l(t,h),l(t,g),l(g,w),l(t,b)},p(R,D){D&128&&i!==(i=new Date(R[23].timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1})+"")&&j(r,i),D&193&&f!==(f=(R[23].isSelf?R[6].nickname||R[0].you_self:R[23].nickname)+"")&&j(_,f),R[23].status==="pending"?S||(S=Vo(),S.c(),S.m(n,d)):S&&(S.d(1),S=null),R[23].status==="failed"?L?L.p(R,D):(L=Yo(R),L.c(),L.m(n,null)):L&&(L.d(1),L=null),D&128&&C!==(C=R[23].message+"")&&j(w,C),D&128&&X(t,"self",R[23].isSelf),D&128&&X(t,"failed",R[23].status==="failed")},d(R){R&&V(t),S&&S.d(),L&&L.d()}}}function ra(e){let t,n=e[23].message+"",o;return{c(){t=a("div"),o=k(n),s(t,"class","system-line svelte-19atdil")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r&128&&n!==(n=i[23].message+"")&&j(o,n)},d(i){i&&V(t)}}}function Vo(e){let t;return{c(){t=a("span"),t.textContent="…",s(t,"class","msg-status")},m(n,o){Y(n,t,o)},d(n){n&&V(t)}}}function Yo(e){let t,n=e[0].retry+"",o,i,r;function c(){return e[18](e[23])}return{c(){t=a("button"),o=k(n),s(t,"class","retry-btn svelte-19atdil")},m(u,f){Y(u,t,f),l(t,o),i||(r=W(t,"click",c),i=!0)},p(u,f){e=u,f&1&&n!==(n=e[0].retry+"")&&j(o,n)},d(u){u&&V(t),i=!1,r()}}}function Xo(e,t){let n,o=Zo(t[23],t[25],t[7]),i,r,c=o&&Go(t);function u(m,d){return m[23].system?ra:aa}let f=u(t),_=f(t);return{key:e,first:null,c(){n=El(),c&&c.c(),i=v(),_.c(),r=El(),this.first=n},m(m,d){Y(m,n,d),c&&c.m(m,d),Y(m,i,d),_.m(m,d),Y(m,r,d)},p(m,d){t=m,d&128&&(o=Zo(t[23],t[25],t[7])),o?c?c.p(t,d):(c=Go(t),c.c(),c.m(i.parentNode,i)):c&&(c.d(1),c=null),f===(f=u(t))&&_?_.p(t,d):(_.d(1),_=f(t),_&&(_.c(),_.m(r.parentNode,r)))},d(m){m&&(V(n),V(i),V(r)),c&&c.d(m),_.d(m)}}}function ca(e){let t,n,o,i,r,c,u=e[0].close_chat+"",f,_,m,d,h=[],g=new Map,C,w,b,S,L,R,D,y,E=e[0].send+"",M,J,H,p,T=e[7].length===0&&Jo(e),Z=_t(e[7]);const z=I=>I[23].id;for(let I=0;In(15,r=p)),pe(e,jt,p=>n(0,c=p)),pe(e,Nl,p=>n(16,u=p)),pe(e,Ze,p=>n(6,f=p)),pe(e,it,p=>n(17,_=p)),pe(e,Ei,p=>n(7,m=p));let d="",h,g,C=!0;di(()=>{h&&C&&n(2,h.scrollTop=h.scrollHeight,h)});function w(){if(!h)return;C=h.scrollHeight-h.scrollTop-h.clientHeight<60}async function b(){const p=d.trim();if(!p)return;if(!r.connected){se(c.error_not_connected,"error");return}if(!r.room){se(c.send_disabled_no_room,"error");return}let T=p;const Z=[...p];Z.length>1e3&&(T=Z.slice(0,1e3).join(""),se(c.msg_truncated,"info")),n(1,d=""),g&&n(3,g.style.height="auto",g);const z="local_"+Date.now()+"_"+Math.random().toString(36).slice(2,7);Ss(z,f.nickname||c.you_self,T,!0,"pending");try{u&&u.id?await window.go.main.App.SendChat(u.id,T):await window.go.main.App.BroadcastChat(T),gn(z,"sent")}catch(I){gn(z,"failed"),se(ke(c.error_send,{err:I}),"error")}}async function S(p){gn(p.id,"pending");try{u&&u.id?await window.go.main.App.SendChat(u.id,p.message):await window.go.main.App.BroadcastChat(p.message),gn(p.id,"sent")}catch(T){gn(p.id,"failed"),se(ke(c.error_send,{err:T}),"error")}}function L(p){p.key==="Enter"&&!p.shiftKey&&(p.preventDefault(),b())}function R(p){const T=new Date(p),Z=new Date,z=new Date(Z.getFullYear(),Z.getMonth(),Z.getDate()),I=new Date(z-864e5),U=new Date(T.getFullYear(),T.getMonth(),T.getDate());return U.getTime()===z.getTime()?c.today:U.getTime()===I.getTime()?c.yesterday:T.toLocaleDateString([],{weekday:"long",month:"short",day:"numeric"})}function D(){g&&(n(3,g.style.height="auto",g),n(3,g.style.height=Math.min(g.scrollHeight,120)+"px",g))}function y(){it.set(!1)}const E=p=>S(p);function M(p){_n[p?"unshift":"push"](()=>{h=p,n(2,h)})}function J(p){_n[p?"unshift":"push"](()=>{g=p,n(3,g)})}function H(){d=this.value,n(1,d)}return e.$$.update=()=>{e.$$.dirty&131072&&_&&ms(jn()),e.$$.dirty&98305&&n(5,o=u?ke(c.chat_with,{name:u.nickname}):ke(c.room_chat_title,{room:r.room||c.network})),e.$$.dirty&32768&&n(4,i=r.connected&&!!r.room)},[c,d,h,g,i,o,f,m,w,b,S,L,R,D,y,r,u,_,E,M,J,H]}class fa extends yt{constructor(t){super(),wt(this,t,ua,ca,pt,{})}}function Qo(e){let t,n=e[9].notif_reconnect_required+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","reconnect-banner svelte-zmj6k4"),s(t,"role","status")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r[0]&512&&n!==(n=i[9].notif_reconnect_required+"")&&j(o,n)},d(i){i&&V(t)}}}function $o(e){let t,n=e[9].apply_update+"",o,i,r;return{c(){t=a("button"),o=k(n),s(t,"class","btn btn-cta"),s(t,"type","button"),t.disabled=e[3]},m(c,u){Y(c,t,u),l(t,o),i||(r=W(t,"click",e[12]),i=!0)},p(c,u){u[0]&512&&n!==(n=c[9].apply_update+"")&&j(o,n),u[0]&8&&(t.disabled=c[3])},d(c){c&&V(t),i=!1,r()}}}function da(e){let t,n=e[9].update_failed+"",o,i,r;return{c(){t=a("p"),o=k(n),i=k(": "),r=k(e[6]),s(t,"class","hint reconnect-hint")},m(c,u){Y(c,t,u),l(t,o),l(t,i),l(t,r)},p(c,u){u[0]&512&&n!==(n=c[9].update_failed+"")&&j(o,n),u[0]&64&&j(r,c[6])},d(c){c&&V(t)}}}function _a(e){let t,n=e[9].update_downloading+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","hint")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r[0]&512&&n!==(n=i[9].update_downloading+"")&&j(o,n)},d(i){i&&V(t)}}}function pa(e){let t,n=ke(e[9].update_available,{v:e[5]})+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","hint")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r[0]&544&&n!==(n=ke(i[9].update_available,{v:i[5]})+"")&&j(o,n)},d(i){i&&V(t)}}}function va(e){let t,n=e[9].update_latest+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","hint")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r[0]&512&&n!==(n=i[9].update_latest+"")&&j(o,n)},d(i){i&&V(t)}}}function ma(e){let t,n=e[9].update_checking+"",o;return{c(){t=a("p"),o=k(n),s(t,"class","hint")},m(i,r){Y(i,t,r),l(t,o)},p(i,r){r[0]&512&&n!==(n=i[9].update_checking+"")&&j(o,n)},d(i){i&&V(t)}}}function ha(e){let t,n,o,i=e[9].settings_title+"",r,c,u,f,_,m,d,h,g,C,w,b,S,L,R=e[9].section_connection+"",D,y,E,M,J,H,p,T,Z=e[9].server_addr+"",z,I,U,le,oe,G,O=e[9].nickname+"",N,B,F,q,ne,ie,ye=e[9].server_token+"",Me,Te,ge,fe,De,Se,re=e[9].reconnect_apply+"",ce,Ke,Le,Ee,je,we,ve,x=e[9].auto_connect+"",ae,nt,Ne,Be=e[9].auto_connect_desc+"",He,at,Ie,be,P,ee,Ce,me=e[9].auto_join+"",te,de,ze,St=e[9].auto_join_desc+"",Nt,rt,he,Ct,Qe,Je,Oe,$,ue=e[9].section_network+"",Ge,Ue,pn,Ut,Sn,Ae,Cn,An=e[9].advanced_warn+"",Rl,Es,qt,Ln,En=e[9].connection_mode+"",Ol,Ps,vt,Wt,Pn=e[9].connection_direct+"",zl,Ft,Dn=e[9].connection_relay+"",Ul,Ds,Nn,Mn=e[9].connection_mode_desc+"",ql,Ns,Kt,Tn,In=e[9].mtu+"",Wl,Ms,lt,Ts,Rn,On=e[9].mtu_desc+"",Fl,Is,Bt,zn,Un=e[9].dns_server+"",Kl,Rs,mt,Os,qn,Wn=e[9].dns_desc+"",Bl,zs,Ht,Fn,Kn=e[9].socks5+"",Hl,Us,ct,Bn,qs,Hn,Jn=e[9].socks5_desc+"",Jl,Ws,Jt,Gn,Vn=e[9].stun_server+"",Gl,Fs,ht,Ks,Yn,Xn=e[9].stun_desc+"",Vl,Bs,Gt,Zn,ut,Mt,Hs,Qn,$n=e[9].section_system+"",Yl,xn,Js,Vt,el,gt,Yt,Tt,Gs,tl,nl=e[9].start_windows+"",Xl,Vs,bt,ll,sl=e[9].updates+"",Zl,Ys,At,ol=e[9].check_updates+"",Ql,Xs,Zs,Qs,Xt,il,ft,It,$s,al,rl=e[9].section_appearance+"",$l,cl,xs,Zt,ul,dt,Qt,fl,dl=e[9].ui_scale+"",xl,eo,$t,st,_l,to,vn,pl=(e[0].uiScale??100)+"",es,no,lo,vl,ml=e[9].ui_scale_desc+"",ts,so,xt,hl,gl=e[9].theme+"",ns,oo,en,io,tn,bl,kl=e[9].language+"",ls,ao,kt,nn,wl=e[9].language_en+"",ss,ln,yl=e[9].language_ru+"",os,sn,jl=e[9].language_zh+"",is,ro,co,on,mn,Sl=e[9].reset+"",as,uo,hn,Cl=e[9].save+"",rs,cs,Rt,Q,us,fo,Ve=e[2]&&Qo(e),Ye=e[4]==="available"&&$o(e);function _o(A,K){if(A[4]==="checking")return ma;if(A[4]==="latest")return va;if(A[4]==="available")return pa;if(A[4]==="downloading")return _a;if(A[4]==="error"&&A[6])return da}let an=_o(e),Xe=an&&an(e);return en=new ai({}),Rt=new Ls({props:{open:!!e[7],message:e[7]?.message||"",confirmLabel:e[7]?.confirmLabel||e[9].save,danger:!!e[7]?.danger,onConfirm:e[34],onCancel:e[35]}}),{c(){t=a("div"),n=a("div"),o=a("h2"),r=k(i),c=v(),u=a("span"),f=k("v"),_=k(e[1]),m=v(),d=a("div"),Ve&&Ve.c(),h=v(),g=a("article"),C=a("div"),w=a("button"),b=a("span"),b.innerHTML='',S=v(),L=a("span"),D=k(R),E=v(),M=a("div"),J=a("div"),H=a("div"),p=a("div"),T=a("label"),z=k(Z),I=v(),U=a("input"),le=v(),oe=a("div"),G=a("label"),N=k(O),B=v(),F=a("input"),q=v(),ne=a("div"),ie=a("label"),Me=k(ye),Te=v(),ge=a("input"),De=v(),Se=a("p"),ce=k(re),Ke=v(),Le=a("label"),Ee=a("input"),je=v(),we=a("span"),ve=a("span"),ae=k(x),nt=v(),Ne=a("span"),He=k(Be),at=v(),Ie=a("label"),be=a("input"),P=v(),ee=a("span"),Ce=a("span"),te=k(me),de=v(),ze=a("span"),Nt=k(St),rt=v(),he=a("article"),Ct=a("div"),Qe=a("button"),Je=a("span"),Je.innerHTML='',Oe=v(),$=a("span"),Ge=k(ue),pn=v(),Ut=a("div"),Sn=a("div"),Ae=a("div"),Cn=a("p"),Rl=k(An),Es=v(),qt=a("div"),Ln=a("label"),Ol=k(En),Ps=v(),vt=a("select"),Wt=a("option"),zl=k(Pn),Ft=a("option"),Ul=k(Dn),Ds=v(),Nn=a("p"),ql=k(Mn),Ns=v(),Kt=a("div"),Tn=a("label"),Wl=k(In),Ms=v(),lt=a("input"),Ts=v(),Rn=a("p"),Fl=k(On),Is=v(),Bt=a("div"),zn=a("label"),Kl=k(Un),Rs=v(),mt=a("input"),Os=v(),qn=a("p"),Bl=k(Wn),zs=v(),Ht=a("div"),Fn=a("label"),Hl=k(Kn),Us=v(),ct=a("input"),qs=v(),Hn=a("p"),Jl=k(Jn),Ws=v(),Jt=a("div"),Gn=a("label"),Gl=k(Vn),Fs=v(),ht=a("input"),Ks=v(),Yn=a("p"),Vl=k(Xn),Bs=v(),Gt=a("article"),Zn=a("div"),ut=a("button"),Mt=a("span"),Mt.innerHTML='',Hs=v(),Qn=a("span"),Yl=k($n),Js=v(),Vt=a("div"),el=a("div"),gt=a("div"),Yt=a("label"),Tt=a("input"),Gs=v(),tl=a("span"),Xl=k(nl),Vs=v(),bt=a("div"),ll=a("span"),Zl=k(sl),Ys=v(),At=a("button"),Ql=k(ol),Xs=v(),Ye&&Ye.c(),Zs=v(),Xe&&Xe.c(),Qs=v(),Xt=a("article"),il=a("div"),ft=a("button"),It=a("span"),It.innerHTML='',$s=v(),al=a("span"),$l=k(rl),xs=v(),Zt=a("div"),ul=a("div"),dt=a("div"),Qt=a("div"),fl=a("label"),xl=k(dl),eo=v(),$t=a("div"),st=a("input"),to=v(),vn=a("span"),es=k(pl),no=k("%"),lo=v(),vl=a("p"),ts=k(ml),so=v(),xt=a("div"),hl=a("span"),ns=k(gl),oo=v(),ot(en.$$.fragment),io=v(),tn=a("div"),bl=a("label"),ls=k(kl),ao=v(),kt=a("select"),nn=a("option"),ss=k(wl),ln=a("option"),os=k(yl),sn=a("option"),is=k(jl),co=v(),on=a("div"),mn=a("button"),as=k(Sl),uo=v(),hn=a("button"),rs=k(Cl),cs=v(),ot(Rt.$$.fragment),s(o,"class","svelte-zmj6k4"),s(u,"class","ver svelte-zmj6k4"),s(n,"class","section-head svelte-zmj6k4"),s(b,"class","collapse-chevron"),s(b,"aria-hidden","true"),X(b,"collapsed",!e[8].connection),s(L,"class","card-title"),s(w,"type","button"),s(w,"class","card-disclosure"),s(w,"aria-expanded",y=e[8].connection),s(C,"class","card-head"),s(T,"for","cfg-server"),s(U,"id","cfg-server"),s(U,"type","text"),s(U,"placeholder","vps.example.com:8080"),s(p,"class","field"),s(G,"for","cfg-nick"),s(F,"id","cfg-nick"),s(F,"type","text"),s(F,"placeholder","Your nickname"),s(oe,"class","field"),s(ie,"for","cfg-token"),s(ge,"id","cfg-token"),s(ge,"type","password"),s(ge,"placeholder",fe=e[9].server_token_desc),s(ne,"class","field"),s(Se,"class","hint reconnect-hint"),s(Ee,"type","checkbox"),s(Ee,"class","svelte-zmj6k4"),s(ve,"class","toggle-title svelte-zmj6k4"),s(Ne,"class","toggle-label svelte-zmj6k4"),s(Le,"class","toggle-row svelte-zmj6k4"),s(be,"type","checkbox"),s(be,"class","svelte-zmj6k4"),s(Ce,"class","toggle-title svelte-zmj6k4"),s(ze,"class","toggle-label svelte-zmj6k4"),s(Ie,"class","toggle-row svelte-zmj6k4"),s(H,"class","card-body-pad"),s(J,"class","card-body-inner"),s(M,"class","card-body"),X(M,"collapsed",!e[8].connection),s(g,"class","card"),s(Je,"class","collapse-chevron"),s(Je,"aria-hidden","true"),X(Je,"collapsed",!e[8].network),s($,"class","card-title"),s(Qe,"type","button"),s(Qe,"class","card-disclosure"),s(Qe,"aria-expanded",Ue=e[8].network),s(Ct,"class","card-head"),s(Cn,"class","hint"),s(Ln,"for","cfg-conn"),Wt.__value="direct",_e(Wt,Wt.__value),Ft.__value="relay",_e(Ft,Ft.__value),s(vt,"id","cfg-conn"),e[0].connectionMode===void 0&&Pl(()=>e[26].call(vt)),s(qt,"class","field"),s(Nn,"class","hint"),s(Tn,"for","cfg-mtu"),s(lt,"id","cfg-mtu"),s(lt,"type","number"),s(lt,"min","576"),s(lt,"max","9000"),s(lt,"placeholder","1500"),s(Kt,"class","field"),s(Rn,"class","hint"),s(zn,"for","cfg-dns"),s(mt,"id","cfg-dns"),s(mt,"type","text"),s(mt,"placeholder","system default"),s(Bt,"class","field"),s(qn,"class","hint"),s(Fn,"for","cfg-socks"),s(ct,"id","cfg-socks"),s(ct,"type","text"),s(ct,"placeholder",Bn=e[9].socks5_placeholder),s(Ht,"class","field"),s(Hn,"class","hint reconnect-hint"),s(Gn,"for","cfg-stun"),s(ht,"id","cfg-stun"),s(ht,"type","text"),s(ht,"placeholder","stun.l.google.com:19302"),s(Jt,"class","field"),s(Yn,"class","hint reconnect-hint"),s(Ae,"class","card-body-pad"),s(Sn,"class","card-body-inner"),s(Ut,"class","card-body"),X(Ut,"collapsed",!e[8].network),s(he,"class","card"),s(Mt,"class","collapse-chevron"),s(Mt,"aria-hidden","true"),X(Mt,"collapsed",!e[8].system),s(Qn,"class","card-title"),s(ut,"type","button"),s(ut,"class","card-disclosure"),s(ut,"aria-expanded",xn=e[8].system),s(Zn,"class","card-head"),s(Tt,"type","checkbox"),s(Tt,"class","svelte-zmj6k4"),s(tl,"class","toggle-title svelte-zmj6k4"),s(Yt,"class","toggle-row svelte-zmj6k4"),s(ll,"class","toggle-title svelte-zmj6k4"),s(At,"class","btn btn-ghost"),s(At,"type","button"),At.disabled=e[3],s(bt,"class","update-row svelte-zmj6k4"),s(gt,"class","card-body-pad"),s(el,"class","card-body-inner"),s(Vt,"class","card-body"),X(Vt,"collapsed",!e[8].system),s(Gt,"class","card"),s(It,"class","collapse-chevron"),s(It,"aria-hidden","true"),X(It,"collapsed",!e[8].appearance),s(al,"class","card-title"),s(ft,"type","button"),s(ft,"class","card-disclosure"),s(ft,"aria-expanded",cl=e[8].appearance),s(il,"class","card-head"),s(fl,"for","cfg-scale"),s(st,"id","cfg-scale"),s(st,"type","range"),s(st,"min","75"),s(st,"max","150"),s(st,"step","5"),st.value=_l=e[0].uiScale??100,s(st,"class","svelte-zmj6k4"),s(vn,"class","scale-value svelte-zmj6k4"),s($t,"class","scale-row svelte-zmj6k4"),s(Qt,"class","field"),s(vl,"class","hint"),s(hl,"class","toggle-title svelte-zmj6k4"),s(xt,"class","theme-row svelte-zmj6k4"),s(bl,"for","cfg-lang"),nn.__value="en",_e(nn,nn.__value),ln.__value="ru",_e(ln,ln.__value),sn.__value="zh",_e(sn,sn.__value),s(kt,"id","cfg-lang"),s(tn,"class","field"),s(dt,"class","card-body-pad"),s(ul,"class","card-body-inner"),s(Zt,"class","card-body"),X(Zt,"collapsed",!e[8].appearance),s(Xt,"class","card"),s(d,"class","settings-content svelte-zmj6k4"),s(mn,"class","btn btn-ghost"),s(hn,"class","btn btn-cta"),s(on,"class","commit-plate svelte-zmj6k4"),s(t,"class","settings-view svelte-zmj6k4")},m(A,K){Y(A,t,K),l(t,n),l(n,o),l(o,r),l(n,c),l(n,u),l(u,f),l(u,_),l(t,m),l(t,d),Ve&&Ve.m(d,null),l(d,h),l(d,g),l(g,C),l(C,w),l(w,b),l(w,S),l(w,L),l(L,D),l(g,E),l(g,M),l(M,J),l(J,H),l(H,p),l(p,T),l(T,z),l(p,I),l(p,U),_e(U,e[0].serverAddr),l(H,le),l(H,oe),l(oe,G),l(G,N),l(oe,B),l(oe,F),_e(F,e[0].nickname),l(H,q),l(H,ne),l(ne,ie),l(ie,Me),l(ne,Te),l(ne,ge),_e(ge,e[0].serverToken),l(H,De),l(H,Se),l(Se,ce),l(H,Ke),l(H,Le),l(Le,Ee),Ee.checked=e[0].autoConnect,l(Le,je),l(Le,we),l(we,ve),l(ve,ae),l(we,nt),l(we,Ne),l(Ne,He),l(H,at),l(H,Ie),l(Ie,be),be.checked=e[0].autoJoinLastRoom,l(Ie,P),l(Ie,ee),l(ee,Ce),l(Ce,te),l(ee,de),l(ee,ze),l(ze,Nt),l(d,rt),l(d,he),l(he,Ct),l(Ct,Qe),l(Qe,Je),l(Qe,Oe),l(Qe,$),l($,Ge),l(he,pn),l(he,Ut),l(Ut,Sn),l(Sn,Ae),l(Ae,Cn),l(Cn,Rl),l(Ae,Es),l(Ae,qt),l(qt,Ln),l(Ln,Ol),l(qt,Ps),l(qt,vt),l(vt,Wt),l(Wt,zl),l(vt,Ft),l(Ft,Ul),Al(vt,e[0].connectionMode,!0),l(Ae,Ds),l(Ae,Nn),l(Nn,ql),l(Ae,Ns),l(Ae,Kt),l(Kt,Tn),l(Tn,Wl),l(Kt,Ms),l(Kt,lt),_e(lt,e[0].mtu),l(Ae,Ts),l(Ae,Rn),l(Rn,Fl),l(Ae,Is),l(Ae,Bt),l(Bt,zn),l(zn,Kl),l(Bt,Rs),l(Bt,mt),_e(mt,e[0].dnsServer),l(Ae,Os),l(Ae,qn),l(qn,Bl),l(Ae,zs),l(Ae,Ht),l(Ht,Fn),l(Fn,Hl),l(Ht,Us),l(Ht,ct),_e(ct,e[0].socks5Proxy),l(Ae,qs),l(Ae,Hn),l(Hn,Jl),l(Ae,Ws),l(Ae,Jt),l(Jt,Gn),l(Gn,Gl),l(Jt,Fs),l(Jt,ht),_e(ht,e[0].stunServer),l(Ae,Ks),l(Ae,Yn),l(Yn,Vl),l(d,Bs),l(d,Gt),l(Gt,Zn),l(Zn,ut),l(ut,Mt),l(ut,Hs),l(ut,Qn),l(Qn,Yl),l(Gt,Js),l(Gt,Vt),l(Vt,el),l(el,gt),l(gt,Yt),l(Yt,Tt),Tt.checked=e[0].startWithWindows,l(Yt,Gs),l(Yt,tl),l(tl,Xl),l(gt,Vs),l(gt,bt),l(bt,ll),l(ll,Zl),l(bt,Ys),l(bt,At),l(At,Ql),l(bt,Xs),Ye&&Ye.m(bt,null),l(gt,Zs),Xe&&Xe.m(gt,null),l(d,Qs),l(d,Xt),l(Xt,il),l(il,ft),l(ft,It),l(ft,$s),l(ft,al),l(al,$l),l(Xt,xs),l(Xt,Zt),l(Zt,ul),l(ul,dt),l(dt,Qt),l(Qt,fl),l(fl,xl),l(Qt,eo),l(Qt,$t),l($t,st),l($t,to),l($t,vn),l(vn,es),l(vn,no),l(dt,lo),l(dt,vl),l(vl,ts),l(dt,so),l(dt,xt),l(xt,hl),l(hl,ns),l(xt,oo),xe(en,xt,null),l(dt,io),l(dt,tn),l(tn,bl),l(bl,ls),l(tn,ao),l(tn,kt),l(kt,nn),l(nn,ss),l(kt,ln),l(ln,os),l(kt,sn),l(sn,is),Al(kt,e[0].lang||e[10]),l(t,co),l(t,on),l(on,mn),l(mn,as),l(on,uo),l(on,hn),l(hn,rs),Y(A,cs,K),xe(Rt,A,K),Q=!0,us||(fo=[W(w,"click",e[19]),W(U,"input",e[20]),W(F,"input",e[21]),W(ge,"input",e[22]),W(Ee,"change",e[23]),W(be,"change",e[24]),W(Qe,"click",e[25]),W(vt,"change",e[26]),W(lt,"input",e[27]),W(mt,"input",e[28]),W(ct,"input",e[29]),W(ht,"input",e[30]),W(ut,"click",e[31]),W(Tt,"change",e[32]),W(At,"click",e[11]),W(ft,"click",e[33]),W(st,"input",e[15]),W(kt,"change",e[16]),W(mn,"click",e[14]),W(hn,"click",e[13])],us=!0)},p(A,K){(!Q||K[0]&512)&&i!==(i=A[9].settings_title+"")&&j(r,i),(!Q||K[0]&2)&&j(_,A[1]),A[2]?Ve?Ve.p(A,K):(Ve=Qo(A),Ve.c(),Ve.m(d,h)):Ve&&(Ve.d(1),Ve=null),(!Q||K[0]&256)&&X(b,"collapsed",!A[8].connection),(!Q||K[0]&512)&&R!==(R=A[9].section_connection+"")&&j(D,R),(!Q||K[0]&256&&y!==(y=A[8].connection))&&s(w,"aria-expanded",y),(!Q||K[0]&512)&&Z!==(Z=A[9].server_addr+"")&&j(z,Z),K[0]&1&&U.value!==A[0].serverAddr&&_e(U,A[0].serverAddr),(!Q||K[0]&512)&&O!==(O=A[9].nickname+"")&&j(N,O),K[0]&1&&F.value!==A[0].nickname&&_e(F,A[0].nickname),(!Q||K[0]&512)&&ye!==(ye=A[9].server_token+"")&&j(Me,ye),(!Q||K[0]&512&&fe!==(fe=A[9].server_token_desc))&&s(ge,"placeholder",fe),K[0]&1&&ge.value!==A[0].serverToken&&_e(ge,A[0].serverToken),(!Q||K[0]&512)&&re!==(re=A[9].reconnect_apply+"")&&j(ce,re),K[0]&1&&(Ee.checked=A[0].autoConnect),(!Q||K[0]&512)&&x!==(x=A[9].auto_connect+"")&&j(ae,x),(!Q||K[0]&512)&&Be!==(Be=A[9].auto_connect_desc+"")&&j(He,Be),K[0]&1&&(be.checked=A[0].autoJoinLastRoom),(!Q||K[0]&512)&&me!==(me=A[9].auto_join+"")&&j(te,me),(!Q||K[0]&512)&&St!==(St=A[9].auto_join_desc+"")&&j(Nt,St),(!Q||K[0]&256)&&X(M,"collapsed",!A[8].connection),(!Q||K[0]&256)&&X(Je,"collapsed",!A[8].network),(!Q||K[0]&512)&&ue!==(ue=A[9].section_network+"")&&j(Ge,ue),(!Q||K[0]&256&&Ue!==(Ue=A[8].network))&&s(Qe,"aria-expanded",Ue),(!Q||K[0]&512)&&An!==(An=A[9].advanced_warn+"")&&j(Rl,An),(!Q||K[0]&512)&&En!==(En=A[9].connection_mode+"")&&j(Ol,En),(!Q||K[0]&512)&&Pn!==(Pn=A[9].connection_direct+"")&&j(zl,Pn),(!Q||K[0]&512)&&Dn!==(Dn=A[9].connection_relay+"")&&j(Ul,Dn),K[0]&1&&Al(vt,A[0].connectionMode),(!Q||K[0]&512)&&Mn!==(Mn=A[9].connection_mode_desc+"")&&j(ql,Mn),(!Q||K[0]&512)&&In!==(In=A[9].mtu+"")&&j(Wl,In),K[0]&1&&si(lt.value)!==A[0].mtu&&_e(lt,A[0].mtu),(!Q||K[0]&512)&&On!==(On=A[9].mtu_desc+"")&&j(Fl,On),(!Q||K[0]&512)&&Un!==(Un=A[9].dns_server+"")&&j(Kl,Un),K[0]&1&&mt.value!==A[0].dnsServer&&_e(mt,A[0].dnsServer),(!Q||K[0]&512)&&Wn!==(Wn=A[9].dns_desc+"")&&j(Bl,Wn),(!Q||K[0]&512)&&Kn!==(Kn=A[9].socks5+"")&&j(Hl,Kn),(!Q||K[0]&512&&Bn!==(Bn=A[9].socks5_placeholder))&&s(ct,"placeholder",Bn),K[0]&1&&ct.value!==A[0].socks5Proxy&&_e(ct,A[0].socks5Proxy),(!Q||K[0]&512)&&Jn!==(Jn=A[9].socks5_desc+"")&&j(Jl,Jn),(!Q||K[0]&512)&&Vn!==(Vn=A[9].stun_server+"")&&j(Gl,Vn),K[0]&1&&ht.value!==A[0].stunServer&&_e(ht,A[0].stunServer),(!Q||K[0]&512)&&Xn!==(Xn=A[9].stun_desc+"")&&j(Vl,Xn),(!Q||K[0]&256)&&X(Ut,"collapsed",!A[8].network),(!Q||K[0]&256)&&X(Mt,"collapsed",!A[8].system),(!Q||K[0]&512)&&$n!==($n=A[9].section_system+"")&&j(Yl,$n),(!Q||K[0]&256&&xn!==(xn=A[8].system))&&s(ut,"aria-expanded",xn),K[0]&1&&(Tt.checked=A[0].startWithWindows),(!Q||K[0]&512)&&nl!==(nl=A[9].start_windows+"")&&j(Xl,nl),(!Q||K[0]&512)&&sl!==(sl=A[9].updates+"")&&j(Zl,sl),(!Q||K[0]&512)&&ol!==(ol=A[9].check_updates+"")&&j(Ql,ol),(!Q||K[0]&8)&&(At.disabled=A[3]),A[4]==="available"?Ye?Ye.p(A,K):(Ye=$o(A),Ye.c(),Ye.m(bt,null)):Ye&&(Ye.d(1),Ye=null),an===(an=_o(A))&&Xe?Xe.p(A,K):(Xe&&Xe.d(1),Xe=an&&an(A),Xe&&(Xe.c(),Xe.m(gt,null))),(!Q||K[0]&256)&&X(Vt,"collapsed",!A[8].system),(!Q||K[0]&256)&&X(It,"collapsed",!A[8].appearance),(!Q||K[0]&512)&&rl!==(rl=A[9].section_appearance+"")&&j($l,rl),(!Q||K[0]&256&&cl!==(cl=A[8].appearance))&&s(ft,"aria-expanded",cl),(!Q||K[0]&512)&&dl!==(dl=A[9].ui_scale+"")&&j(xl,dl),(!Q||K[0]&1&&_l!==(_l=A[0].uiScale??100))&&(st.value=_l),(!Q||K[0]&1)&&pl!==(pl=(A[0].uiScale??100)+"")&&j(es,pl),(!Q||K[0]&512)&&ml!==(ml=A[9].ui_scale_desc+"")&&j(ts,ml),(!Q||K[0]&512)&&gl!==(gl=A[9].theme+"")&&j(ns,gl),(!Q||K[0]&512)&&kl!==(kl=A[9].language+"")&&j(ls,kl),(!Q||K[0]&512)&&wl!==(wl=A[9].language_en+"")&&j(ss,wl),(!Q||K[0]&512)&&yl!==(yl=A[9].language_ru+"")&&j(os,yl),(!Q||K[0]&512)&&jl!==(jl=A[9].language_zh+"")&&j(is,jl),(!Q||K[0]&1025&&ro!==(ro=A[0].lang||A[10]))&&Al(kt,A[0].lang||A[10]),(!Q||K[0]&256)&&X(Zt,"collapsed",!A[8].appearance),(!Q||K[0]&512)&&Sl!==(Sl=A[9].reset+"")&&j(as,Sl),(!Q||K[0]&512)&&Cl!==(Cl=A[9].save+"")&&j(rs,Cl);const Ot={};K[0]&128&&(Ot.open=!!A[7]),K[0]&128&&(Ot.message=A[7]?.message||""),K[0]&640&&(Ot.confirmLabel=A[7]?.confirmLabel||A[9].save),K[0]&128&&(Ot.danger=!!A[7]?.danger),K[0]&128&&(Ot.onConfirm=A[34]),K[0]&128&&(Ot.onCancel=A[35]),Rt.$set(Ot)},i(A){Q||(qe(en.$$.fragment,A),qe(Rt.$$.fragment,A),Q=!0)},o(A){$e(en.$$.fragment,A),$e(Rt.$$.fragment,A),Q=!1},d(A){A&&(V(t),V(cs)),Ve&&Ve.d(),Ye&&Ye.d(),Xe&&Xe.d(),et(en),et(Rt,A),us=!1,Re(fo)}}}function ga(e,t,n){let o,i,r;pe(e,Ze,q=>n(18,o=q)),pe(e,jt,q=>n(9,i=q)),pe(e,As,q=>n(10,r=q));let c={...o},u="1.0.0",f=!1,_=!1,m="",d="",h="",g=null;async function C(){n(3,_=!0),n(4,m="checking"),n(6,h=""),n(5,d="");try{const q=await window.go.main.App.CheckForUpdate();q?.available?(n(4,m="available"),n(5,d=q.latest||"")):n(4,m="latest")}catch(q){n(4,m="error"),n(6,h=String(q)),se(i.error_update_check+q,"error")}finally{n(3,_=!1)}}async function w(){n(7,g={message:i.confirm_update,confirmLabel:i.apply_update,run:async()=>{n(3,_=!0),n(4,m="downloading"),n(6,h="");try{await window.go.main.App.ApplyUpdate(),se(i.notif_updating,"info")}catch(q){n(4,m="error"),n(6,h=String(q)),n(3,_=!1),se(i.error_update+q,"error")}}})}async function b(){Et(Ze,o={...c},o);try{n(2,f=!!await window.go.main.App.SaveSettings(c)),c.lang&&bn(c.lang),se(i.notif_settings_saved,"info"),f&&se(i.notif_reconnect_required,"info")}catch(q){se(i.error_save+q,"error")}}async function S(){n(7,g={message:i.confirm_reset,confirmLabel:i.reset,danger:!0,run:async()=>{try{n(0,c=await window.go.main.App.ResetSettings()),Et(Ze,o={...c},o),n(2,f=!1),c.lang&&bn(c.lang),se(i.notif_settings_saved,"info")}catch(q){se(i.error_save+q,"error")}}})}function L(q){const ne=Math.min(150,Math.max(75,Number(q.target.value)||100));n(0,c.uiScale=ne,c),Et(Ze,o={...o,uiScale:ne},o)}function R(q){n(0,c.lang=q.target.value,c),bn(q.target.value)}let D={connection:!0,network:!1,system:!1,appearance:!0};function y(q){n(8,D={...D,[q]:!D[q]})}Ml(async()=>{try{const q=await window.go.main.App.GetSettings();if(q){const ne=o.theme||q.theme;n(0,c={...c,...q,theme:ne}),c.connectionMode||n(0,c.connectionMode="direct",c),Et(Ze,o={...c,theme:ne},o),c.lang&&bn(c.lang)}window.go.main.App.GetVersion&&n(1,u=await window.go.main.App.GetVersion())}catch{}});const E=()=>y("connection");function M(){c.serverAddr=this.value,n(0,c),n(18,o)}function J(){c.nickname=this.value,n(0,c),n(18,o)}function H(){c.serverToken=this.value,n(0,c),n(18,o)}function p(){c.autoConnect=this.checked,n(0,c),n(18,o)}function T(){c.autoJoinLastRoom=this.checked,n(0,c),n(18,o)}const Z=()=>y("network");function z(){c.connectionMode=fi(this),n(0,c),n(18,o)}function I(){c.mtu=si(this.value),n(0,c),n(18,o)}function U(){c.dnsServer=this.value,n(0,c),n(18,o)}function le(){c.socks5Proxy=this.value,n(0,c),n(18,o)}function oe(){c.stunServer=this.value,n(0,c),n(18,o)}const G=()=>y("system");function O(){c.startWithWindows=this.checked,n(0,c),n(18,o)}const N=()=>y("appearance"),B=async()=>{const q=g?.run;n(7,g=null),q&&await q()},F=()=>n(7,g=null);return e.$$.update=()=>{e.$$.dirty[0]&262145&&o.theme&&c.theme!==o.theme&&n(0,c.theme=o.theme,c)},[c,u,f,_,m,d,h,g,D,i,r,C,w,b,S,L,R,y,o,E,M,J,H,p,T,Z,z,I,U,le,oe,G,O,N,B,F]}class ba extends yt{constructor(t){super(),wt(this,t,ga,ha,pt,{},null,[-1,-1])}}function xo(e,t,n){const o=e.slice();return o[18]=t[n],o}function ka(e){let t,n,o,i,r,c,u,f,_,m,d,h,g,C,w,b,S,L,R,D,y,E,M,J,H;return n=new xi({props:{pathAggregate:e[4],relayCount:e[3]}}),c=new Wi({}),g=new Yi({props:{pathAggregate:e[4]}}),S=new fa({}),y=new ba({}),{c(){t=a("div"),ot(n.$$.fragment),o=v(),i=a("div"),r=a("div"),ot(c.$$.fragment),u=v(),f=a("div"),_=v(),m=a("main"),d=a("div"),h=a("div"),ot(g.$$.fragment),w=v(),b=a("div"),ot(S.$$.fragment),R=v(),D=a("div"),ot(y.$$.fragment),s(f,"class","drag-handle svelte-8tj0mu"),X(f,"active",e[2]),s(r,"class","sidebar-wrapper svelte-8tj0mu"),vo(r,"width",e[1]+"px"),s(h,"class","peer-pane svelte-8tj0mu"),h.hidden=C=e[0]==="settings",s(b,"class","chat-pane svelte-8tj0mu"),b.hidden=L=e[0]==="settings"||!e[6],s(D,"class","settings-pane svelte-8tj0mu"),D.hidden=E=e[0]!=="settings",s(d,"class","workspace svelte-8tj0mu"),s(m,"class","main-area svelte-8tj0mu"),s(i,"class","layout svelte-8tj0mu"),s(t,"class","shell bench svelte-8tj0mu")},m(p,T){Y(p,t,T),xe(n,t,null),l(t,o),l(t,i),l(i,r),xe(c,r,null),l(r,u),l(r,f),l(i,_),l(i,m),l(m,d),l(d,h),xe(g,h,null),l(d,w),l(d,b),xe(S,b,null),l(d,R),l(d,D),xe(y,D,null),M=!0,J||(H=W(f,"mousedown",e[9]),J=!0)},p(p,T){const Z={};T&16&&(Z.pathAggregate=p[4]),T&8&&(Z.relayCount=p[3]),n.$set(Z),(!M||T&4)&&X(f,"active",p[2]),(!M||T&2)&&vo(r,"width",p[1]+"px");const z={};T&16&&(z.pathAggregate=p[4]),g.$set(z),(!M||T&1&&C!==(C=p[0]==="settings"))&&(h.hidden=C),(!M||T&65&&L!==(L=p[0]==="settings"||!p[6]))&&(b.hidden=L),(!M||T&1&&E!==(E=p[0]!=="settings"))&&(D.hidden=E)},i(p){M||(qe(n.$$.fragment,p),qe(c.$$.fragment,p),qe(g.$$.fragment,p),qe(S.$$.fragment,p),qe(y.$$.fragment,p),M=!0)},o(p){$e(n.$$.fragment,p),$e(c.$$.fragment,p),$e(g.$$.fragment,p),$e(S.$$.fragment,p),$e(y.$$.fragment,p),M=!1},d(p){p&&V(t),et(n),et(c),et(g),et(S),et(y),J=!1,H()}}}function wa(e){let t,n,o,i,r,c,u,f;return u=new ia({}),{c(){t=a("div"),n=v(),o=a("div"),i=v(),r=a("div"),c=a("div"),ot(u.$$.fragment),s(t,"class","space-bg"),s(t,"aria-hidden","true"),s(o,"class","space-dust"),s(o,"aria-hidden","true"),s(c,"class","fullscreen-connect svelte-8tj0mu"),s(r,"class","shell connect-shell svelte-8tj0mu")},m(_,m){Y(_,t,m),Y(_,n,m),Y(_,o,m),Y(_,i,m),Y(_,r,m),l(r,c),xe(u,c,null),f=!0},p:Pe,i(_){f||(qe(u.$$.fragment,_),f=!0)},o(_){$e(u.$$.fragment,_),f=!1},d(_){_&&(V(t),V(n),V(o),V(i),V(r)),et(u)}}}function ei(e){let t,n,o=e[7].msg+"",i,r,c,u=e[5].dismiss_error+"",f,_,m,d;return{c(){t=a("div"),n=a("span"),i=k(o),r=v(),c=a("button"),f=k(u),s(n,"class","error-strip-msg svelte-8tj0mu"),s(c,"type","button"),s(c,"class","error-strip-dismiss svelte-8tj0mu"),s(c,"aria-label",_=e[5].dismiss_error),s(t,"class","error-strip svelte-8tj0mu"),s(t,"role","alert")},m(h,g){Y(h,t,g),l(t,n),l(n,i),l(t,r),l(t,c),l(c,f),m||(d=W(c,"click",yn),m=!0)},p(h,g){g&128&&o!==(o=h[7].msg+"")&&j(i,o),g&32&&u!==(u=h[5].dismiss_error+"")&&j(f,u),g&32&&_!==(_=h[5].dismiss_error)&&s(c,"aria-label",_)},d(h){h&&V(t),m=!1,d()}}}function ti(e,t){let n,o=t[18].msg+"",i,r,c;return{key:e,first:null,c(){n=a("div"),i=k(o),r=v(),s(n,"class",c="notification "+t[18].type+" svelte-8tj0mu"),s(n,"role","status"),this.first=n},m(u,f){Y(u,n,f),l(n,i),l(n,r)},p(u,f){t=u,f&256&&o!==(o=t[18].msg+"")&&j(i,o),f&256&&c!==(c="notification "+t[18].type+" svelte-8tj0mu")&&s(n,"class",c)},d(u){u&&V(n)}}}function ya(e){let t,n,o,i,r=[],c=new Map,u,f;const _=[wa,ka],m=[];function d(w,b){return w[0]==="connect"?0:1}t=d(e),n=m[t]=_[t](e);let h=e[7]&&ei(e),g=_t(e[8]);const C=w=>w[18].id;for(let w=0;w{m[S]=null}),gi(),n=m[t],n?n.p(w,b):(n=m[t]=_[t](w),n.c()),qe(n,1),n.m(o.parentNode,o)),w[7]?h?h.p(w,b):(h=ei(w),h.c(),h.m(i.parentNode,i)):h&&(h.d(1),h=null),b&256&&(g=_t(w[8]),r=Il(r,b,C,1,w,g,c,u.parentNode,Tl,ti,u,xo))},i(w){f||(qe(n),f=!0)},o(w){$e(n),f=!1},d(w){w&&(V(o),V(i),V(u)),m[t].d(w),h&&h.d(w);for(let b=0;bo.connected&&(o.path==="relay"||o.path==="ws"))?"relay":"direct":"disconnected"}async function ni(e){if(e.key==="F11"){e.preventDefault();try{await window.runtime?.WindowIsFullscreen?.()?window.runtime.WindowUnfullscreen():window.runtime.WindowFullscreen()}catch{}}}function Ca(e,t,n){let o,i,r,c,u,f,_,m,d,h;pe(e,jt,y=>n(5,r=y)),pe(e,Ze,y=>n(10,c=y)),pe(e,_s,y=>n(11,u=y)),pe(e,Fe,y=>n(12,f=y)),pe(e,We,y=>n(0,_=y)),pe(e,it,y=>n(6,m=y)),pe(e,Dl,y=>n(7,d=y)),pe(e,ps,y=>n(8,h=y));let g=220,C=!1,w,b,S=!0;function L(y){n(2,C=!0),w=y.clientX,b=g,document.addEventListener("mousemove",R),document.addEventListener("mouseup",D),y.preventDefault()}function R(y){if(!C)return;const E=b+(y.clientX-w);n(1,g=Math.max(140,Math.min(400,E)))}function D(){n(2,C=!1),document.removeEventListener("mousemove",R),document.removeEventListener("mouseup",D)}return Ml(async()=>{window.addEventListener("keydown",ni);try{const y=await window.go.main.App.LoadConfig();y&&(Ze.set({...c,...y}),y.lang&&bn(y.lang))}catch{}window.runtime?.EventsOn&&(window.runtime.EventsOn("status_changed",y=>{S&&(Fe.set(y),!y.connected&&!y.reconnecting&&(it.set(!1),We.set("connect")))}),window.runtime.EventsOn("peers_changed",y=>{S&&_s.set(y||[])}),window.runtime.EventsOn("error",y=>{S&&se(y,"error")}),window.runtime.EventsOn("chat_message",y=>{S&&Ss(y.fromID,y.nickname,y.message,!1,"sent",!1,!!y.isDM)}),window.runtime.EventsOn("system_chat",y=>{S&&Li(y.message||"")}),window.runtime.EventsOn("room_deleted",y=>{S&&(se(r.notif_room_deleted,"error"),We.set("network"))}),window.runtime.EventsOn("auto_join_skipped",y=>{if(!S)return;const E=y?.room||"";se(ke(r.notif_auto_join_skipped,{name:E}),"info")}))}),oi(()=>{S=!1,window.removeEventListener("keydown",ni),document.removeEventListener("mousemove",R),document.removeEventListener("mouseup",D),window.runtime?.EventsOff&&(window.runtime.EventsOff("status_changed"),window.runtime.EventsOff("peers_changed"),window.runtime.EventsOff("error"),window.runtime.EventsOff("chat_message"),window.runtime.EventsOff("system_chat"),window.runtime.EventsOff("room_deleted"),window.runtime.EventsOff("auto_join_skipped"))}),e.$$.update=()=>{if(e.$$.dirty&1024){const y=ja(c.uiScale)/100;document.documentElement.style.zoom=String(y)}if(e.$$.dirty&1025){const y=document.documentElement,E=c.theme==="light";y.classList.toggle("light-theme",E),y.dataset.theme=E?"light":"dark",y.dataset.app=_==="connect"?"connect":"connected"}e.$$.dirty&6144&&n(4,o=Sa(f,u)),e.$$.dirty&2048&&n(3,i=(u||[]).filter(y=>y.connected&&(y.path==="relay"||y.path==="ws")).length)},[_,g,C,i,o,r,m,d,h,L,c,u,f]}class Aa extends yt{constructor(t){super(),wt(this,t,Ca,ya,pt,{})}}new Aa({target:document.getElementById("app")}); diff --git a/client/frontend/dist/assets/index-CoV4PMmG.css b/client/frontend/dist/assets/index-CoV4PMmG.css new file mode 100644 index 0000000..2f4806e --- /dev/null +++ b/client/frontend/dist/assets/index-CoV4PMmG.css @@ -0,0 +1 @@ +.modal-overlay.svelte-1g1nook.svelte-1g1nook{position:fixed;inset:0;background:color-mix(in srgb,var(--bg, #0a0a0a) 55%,transparent);display:flex;align-items:center;justify-content:center;z-index:200}.modal.svelte-1g1nook.svelte-1g1nook{background:var(--surface, #141414);border:1px solid var(--border, #2a2a2a);border-radius:var(--radius, 2px);padding:20px;width:340px;display:flex;flex-direction:column;gap:16px}.modal.svelte-1g1nook p.svelte-1g1nook{color:var(--text, #f5f5f5);font-size:14px;line-height:1.45}.modal-actions.svelte-1g1nook.svelte-1g1nook{display:flex;gap:8px;justify-content:flex-end}.sidebar.svelte-1vrsv4c.svelte-1vrsv4c{width:100%;height:100%;background:transparent;display:flex;flex-direction:column;flex-shrink:0}.sidebar-header.svelte-1vrsv4c.svelte-1vrsv4c{padding:14px 14px 8px}.sidebar-meta.svelte-1vrsv4c.svelte-1vrsv4c{font-size:11px;font-family:var(--font-mono);color:var(--muted);margin-top:4px}.sidebar-actions.svelte-1vrsv4c.svelte-1vrsv4c{padding:8px 12px 12px;display:flex;flex-direction:column;gap:6px}.sidebar-actions.svelte-1vrsv4c .bezel.svelte-1vrsv4c{width:100%}.sidebar-actions.svelte-1vrsv4c .bezel.svelte-1vrsv4c:disabled{opacity:.45;cursor:not-allowed}.network-list.svelte-1vrsv4c.svelte-1vrsv4c{flex:1;overflow-y:auto;overflow-x:hidden;padding:0 0 12px;border-top:1px solid var(--border)}.network-item.svelte-1vrsv4c.svelte-1vrsv4c{display:flex;align-items:center;padding:10px 12px;min-height:44px;cursor:pointer;gap:10px;border-bottom:1px solid var(--border);transition:background var(--dur-fast, .15s) var(--ease-out, ease);position:relative}.network-item.svelte-1vrsv4c.svelte-1vrsv4c:hover,.network-item.svelte-1vrsv4c.svelte-1vrsv4c:focus-visible{background:var(--bg-hover)}.network-item.active.svelte-1vrsv4c.svelte-1vrsv4c{background:var(--surface-2)}.network-item.disabled.svelte-1vrsv4c.svelte-1vrsv4c{opacity:.55}.tick.svelte-1vrsv4c.svelte-1vrsv4c{width:6px;height:6px;flex-shrink:0;background:var(--text-dim)}.tick.on.svelte-1vrsv4c.svelte-1vrsv4c{background:var(--text)}.network-info.svelte-1vrsv4c.svelte-1vrsv4c{flex:1;min-width:0}.network-name.svelte-1vrsv4c.svelte-1vrsv4c{font-size:13px;font-weight:500;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.network-meta.svelte-1vrsv4c.svelte-1vrsv4c{font-size:11px;font-family:var(--font-mono);color:var(--muted);line-height:1.4}.row-actions.svelte-1vrsv4c.svelte-1vrsv4c{position:relative;flex-shrink:0}.menu-btn.svelte-1vrsv4c.svelte-1vrsv4c{background:none;border:1px solid transparent;color:var(--muted);cursor:pointer;font-size:16px;line-height:1;width:32px;height:32px;border-radius:var(--radius);display:flex;align-items:center;justify-content:center}.menu-btn.svelte-1vrsv4c svg.svelte-1vrsv4c{width:16px;height:16px;display:block}.menu-btn.svelte-1vrsv4c.svelte-1vrsv4c:hover,.menu-btn[aria-expanded=true].svelte-1vrsv4c.svelte-1vrsv4c{color:var(--text);border-color:var(--border);background:var(--surface)}.overflow-menu.svelte-1vrsv4c.svelte-1vrsv4c{position:absolute;right:0;top:100%;z-index:20;min-width:168px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);display:flex;flex-direction:column;box-shadow:0 8px 24px #00000047}.menu-item.svelte-1vrsv4c.svelte-1vrsv4c{background:none;border:none;text-align:left;padding:10px 12px;min-height:36px;color:var(--text);font-size:13px;cursor:pointer}.menu-item.svelte-1vrsv4c.svelte-1vrsv4c:hover{background:var(--bg-hover)}.menu-item.danger.svelte-1vrsv4c.svelte-1vrsv4c{color:var(--error)}.menu-item.danger.svelte-1vrsv4c.svelte-1vrsv4c:hover{background:color-mix(in srgb,var(--error) 12%,transparent)}.empty-desc.svelte-1vrsv4c.svelte-1vrsv4c{margin-top:6px;font-size:12px;color:var(--text-dim)}.empty-state.svelte-1vrsv4c.svelte-1vrsv4c{padding:16px 12px;text-align:left;color:var(--muted);font-size:13px}.modal-overlay.svelte-1vrsv4c.svelte-1vrsv4c{position:fixed;inset:0;background:color-mix(in srgb,var(--bg) 55%,transparent);display:flex;align-items:center;justify-content:center;z-index:100}.modal.svelte-1vrsv4c.svelte-1vrsv4c{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;width:340px;max-height:80vh;overflow-y:auto;display:flex;flex-direction:column;gap:10px;box-shadow:0 12px 40px #00000047}.modal.svelte-1vrsv4c h3.svelte-1vrsv4c{font-size:1.05rem;color:var(--text);font-weight:600;letter-spacing:-.02em}.modal.svelte-1vrsv4c input.invalid.svelte-1vrsv4c{border-color:var(--error)}.field-error.svelte-1vrsv4c.svelte-1vrsv4c,.modal-error.svelte-1vrsv4c.svelte-1vrsv4c{font-size:12px;color:var(--error)}.modal-error.svelte-1vrsv4c.svelte-1vrsv4c{padding:8px;border:1px solid var(--error);border-radius:var(--radius);background:color-mix(in srgb,var(--error) 10%,transparent)}.modal-actions.svelte-1vrsv4c.svelte-1vrsv4c{display:flex;gap:8px;justify-content:flex-end;margin-top:6px}.sr-only.svelte-1vrsv4c.svelte-1vrsv4c{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.peer-view.svelte-1ywbfno.svelte-1ywbfno{display:flex;flex-direction:column;height:100%;padding:12px 16px 20px;overflow:auto;min-height:0}.section-head.svelte-1ywbfno.svelte-1ywbfno{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.section-head.svelte-1ywbfno h2.svelte-1ywbfno{font-size:1.05rem;font-weight:600;letter-spacing:-.03em;color:var(--text);margin:0}.section-hint.svelte-1ywbfno.svelte-1ywbfno{color:var(--muted);font-size:13px;margin:0 0 10px}.section-hint.fault.svelte-1ywbfno.svelte-1ywbfno{color:var(--fault)}.scope-face.svelte-1ywbfno.svelte-1ywbfno{position:relative;flex:1;min-height:0;border:1px solid var(--border);background-color:var(--bg);background-image:repeating-linear-gradient(to right,var(--graticule) 0 1px,transparent 1px 48px),repeating-linear-gradient(to bottom,var(--graticule) 0 1px,transparent 1px 28px)}.channel-head.svelte-1ywbfno.svelte-1ywbfno,.peer-row.svelte-1ywbfno.svelte-1ywbfno{display:grid;grid-template-columns:36px minmax(0,1.3fr) 118px 128px 56px auto;gap:10px;align-items:center;padding:0 12px}.channel-head.svelte-1ywbfno.svelte-1ywbfno{min-height:28px;border-bottom:1px solid var(--border);background:color-mix(in srgb,var(--surface) 82%,transparent);font-size:10px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}.stack.svelte-1ywbfno.svelte-1ywbfno{display:flex;flex-direction:column}.peer-row.svelte-1ywbfno.svelte-1ywbfno{min-height:44px;border-bottom:1px solid var(--border);background:color-mix(in srgb,var(--bg) 55%,transparent)}.self-row.svelte-1ywbfno.svelte-1ywbfno{opacity:.88}.ch.svelte-1ywbfno.svelte-1ywbfno{font-family:var(--font-mono);font-size:11px;color:var(--muted)}.nickname.svelte-1ywbfno.svelte-1ywbfno{color:var(--text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.self-tag.svelte-1ywbfno.svelte-1ywbfno{margin-left:6px;font-size:11px;color:var(--muted);font-weight:400;letter-spacing:.06em;text-transform:uppercase}.peer-ip.svelte-1ywbfno.svelte-1ywbfno{font-family:var(--font-mono);font-size:12px;color:var(--muted);background:none;border:0;padding:0;cursor:pointer;text-align:left}.peer-ip.svelte-1ywbfno.svelte-1ywbfno:hover{color:var(--text)}.col-ping.svelte-1ywbfno.svelte-1ywbfno{font-family:var(--font-mono);font-size:12px;color:var(--muted)}.col-ping.probing.svelte-1ywbfno.svelte-1ywbfno{color:var(--text)}.col-path.svelte-1ywbfno.svelte-1ywbfno{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:500;font-family:var(--font-mono);color:var(--muted);min-width:0}.col-path.live.svelte-1ywbfno.svelte-1ywbfno{color:var(--live)}.col-path.fault.svelte-1ywbfno.svelte-1ywbfno{color:var(--fault)}.col-path.idle.svelte-1ywbfno.svelte-1ywbfno{color:var(--muted)}.trace.svelte-1ywbfno.svelte-1ywbfno{width:36px;height:10px;flex-shrink:0}.trace.svelte-1ywbfno line.svelte-1ywbfno,.trace.svelte-1ywbfno circle.svelte-1ywbfno{fill:currentColor;stroke:currentColor;stroke-width:1}.peer-actions.svelte-1ywbfno.svelte-1ywbfno{display:flex;gap:6px;justify-content:flex-end}.badge.svelte-1ywbfno.svelte-1ywbfno{display:inline-block;min-width:16px;padding:0 5px;margin-left:6px;background:var(--fault);color:#fff;font-size:10px;line-height:16px;text-align:center;border-radius:var(--radius)}.empty.svelte-1ywbfno.svelte-1ywbfno{padding:20px 12px;color:var(--muted);background:color-mix(in srgb,var(--bg) 70%,transparent)}.empty-label.svelte-1ywbfno.svelte-1ywbfno{color:var(--text);margin-bottom:4px;font-weight:500}.empty-desc.svelte-1ywbfno.svelte-1ywbfno{font-family:var(--font-serif);font-style:italic;font-size:13px;margin:0}.btn-sm.svelte-1ywbfno.svelte-1ywbfno{min-height:28px;padding:4px 10px;font-size:12px}@media (max-width: 720px){.channel-head.svelte-1ywbfno.svelte-1ywbfno,.peer-row.svelte-1ywbfno.svelte-1ywbfno{grid-template-columns:28px minmax(0,1fr) auto}.channel-head.svelte-1ywbfno span.svelte-1ywbfno:nth-child(3),.channel-head.svelte-1ywbfno span.svelte-1ywbfno:nth-child(4),.channel-head.svelte-1ywbfno span.svelte-1ywbfno:nth-child(5),.peer-ip.svelte-1ywbfno.svelte-1ywbfno,.col-path.svelte-1ywbfno.svelte-1ywbfno,.col-ping.svelte-1ywbfno.svelte-1ywbfno{display:none}}.topbar.svelte-10p19bf.svelte-10p19bf{flex-shrink:0;border-bottom:1px solid var(--border);background:var(--surface)}.topbar-row.svelte-10p19bf.svelte-10p19bf{display:flex;align-items:stretch;gap:0;min-height:72px}.cell.svelte-10p19bf.svelte-10p19bf{display:flex;flex-direction:column;justify-content:center;gap:4px;padding:10px 16px;border-right:1px solid var(--border);min-width:0;text-align:left}button.cell.svelte-10p19bf.svelte-10p19bf{background:none;border-top:0;border-bottom:0;border-left:0;cursor:pointer;font:inherit;color:inherit}button.cell.svelte-10p19bf:hover .cell-value.svelte-10p19bf{color:var(--muted)}button.cell.svelte-10p19bf.svelte-10p19bf:disabled{cursor:default;opacity:1}button.cell.svelte-10p19bf:disabled:hover .cell-value.svelte-10p19bf{color:var(--text)}.brand.svelte-10p19bf.svelte-10p19bf{flex:0 0 auto;justify-content:center;padding-right:18px}.wordmark.svelte-10p19bf.svelte-10p19bf{font-size:11px;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--text);line-height:1.2}.room.svelte-10p19bf.svelte-10p19bf{flex:1.4 1 140px}.vip.svelte-10p19bf.svelte-10p19bf{flex:1 1 168px}.path.svelte-10p19bf.svelte-10p19bf{flex:1.4 1 200px}.peers.svelte-10p19bf.svelte-10p19bf{flex:0 0 72px}.cell-value.svelte-10p19bf.svelte-10p19bf{font-size:1.05rem;font-weight:600;letter-spacing:-.03em;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.15}.cell-value.mono.svelte-10p19bf.svelte-10p19bf{font-family:var(--font-mono);font-weight:500;letter-spacing:0;font-size:1rem}.vip-value.svelte-10p19bf.svelte-10p19bf{font-size:1.35rem;font-weight:500;letter-spacing:.02em}.path-value.svelte-10p19bf.svelte-10p19bf{font-family:var(--font-mono);font-size:1.22rem;font-weight:500;letter-spacing:0}.cell-value.live.svelte-10p19bf.svelte-10p19bf{color:var(--live)}.cell-value.fault.svelte-10p19bf.svelte-10p19bf{color:var(--fault)}.path-row.svelte-10p19bf.svelte-10p19bf{display:flex;align-items:center;gap:8px;min-width:0}.leds.svelte-10p19bf.svelte-10p19bf{display:flex;gap:4px;flex-shrink:0}.seg-bar.svelte-10p19bf.svelte-10p19bf{display:flex;gap:3px;margin-top:2px}.seg.svelte-10p19bf.svelte-10p19bf{flex:1;height:3px;background:var(--text-dim)}.seg.on.live.svelte-10p19bf.svelte-10p19bf{background:var(--live)}.seg.on.fault.svelte-10p19bf.svelte-10p19bf{background:var(--fault)}.actions.svelte-10p19bf.svelte-10p19bf{display:flex;align-items:center;gap:8px;padding:0 12px;margin-left:auto;flex-shrink:0}.connect-view.svelte-1br9j49.svelte-1br9j49{display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:32px 24px}.connect-form.svelte-1br9j49.svelte-1br9j49,.connected-info.svelte-1br9j49.svelte-1br9j49{width:min(360px,100%);display:flex;flex-direction:column;gap:16px;animation:svelte-1br9j49-connectIn .45s cubic-bezier(.16,1,.3,1) both}@keyframes svelte-1br9j49-connectIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.brand-block.svelte-1br9j49.svelte-1br9j49{text-align:center;margin-bottom:12px}.brand.svelte-1br9j49.svelte-1br9j49{font-family:var(--font-display);font-size:clamp(36px,7vw,48px);font-weight:400;letter-spacing:.02em;text-transform:uppercase;color:var(--text-bright);line-height:1.05}.tagline.svelte-1br9j49.svelte-1br9j49{margin-top:10px;font-size:var(--font-size-sm);color:var(--text-secondary);text-transform:none;letter-spacing:.02em;line-height:1.45}.field.svelte-1br9j49.svelte-1br9j49{display:flex;flex-direction:column;gap:7px}label.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--text-secondary);text-transform:uppercase;letter-spacing:.08em}input.svelte-1br9j49.svelte-1br9j49{background:#111111d9;border:1px solid var(--border-light);color:var(--text-bright);padding:13px 14px;min-height:44px;font-family:var(--font-mono);font-size:var(--font-size);transition:border-color .15s ease,background .15s ease}input.svelte-1br9j49.svelte-1br9j49::placeholder{color:var(--text-dim)}input.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){border-color:var(--border-hover)}input.svelte-1br9j49.svelte-1br9j49:focus{outline:none;border-color:var(--text-bright);background:#141414f2}input.invalid.svelte-1br9j49.svelte-1br9j49{border-color:var(--error)}.field-error.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--error)}.connect-btn.svelte-1br9j49.svelte-1br9j49,.disconnect-btn.svelte-1br9j49.svelte-1br9j49,.secondary-btn.svelte-1br9j49.svelte-1br9j49{border:none;padding:15px 18px;min-height:48px;font-family:var(--font-mono);font-size:var(--font-size);cursor:pointer;text-transform:uppercase;letter-spacing:.12em;font-weight:600;transition:background .15s ease,border-color .15s ease,transform .12s ease}.connect-btn.svelte-1br9j49.svelte-1br9j49{margin-top:4px;background:var(--accent);color:var(--accent-ink)}.connect-btn.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){background:var(--accent-hover);transform:translateY(-1px)}.connect-btn.svelte-1br9j49.svelte-1br9j49:active:not(:disabled){transform:translateY(0)}.connect-btn.svelte-1br9j49.svelte-1br9j49:disabled{opacity:.45;cursor:not-allowed;transform:none}.secondary-btn.svelte-1br9j49.svelte-1br9j49{background:transparent;color:var(--text-primary);border:1px solid var(--border-light);font-weight:500;letter-spacing:.08em}.secondary-btn.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){border-color:var(--accent);color:var(--text-bright)}.disconnect-btn.svelte-1br9j49.svelte-1br9j49{background:transparent;color:var(--error);border:1px solid var(--error);font-weight:500}.phase.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--text-muted);text-align:center;letter-spacing:.04em}.invite-disclosure.svelte-1br9j49.svelte-1br9j49{margin-top:8px;border-top:1px solid var(--border);padding-top:14px}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49{cursor:pointer;color:var(--text-muted);font-size:var(--font-size-sm);list-style:none;min-height:36px;display:flex;align-items:center;justify-content:center;transition:color .15s ease}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49:hover{color:var(--text-secondary)}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49::-webkit-details-marker{display:none}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49:before{content:"+";margin-right:8px;color:var(--text-dim)}.invite-disclosure[open].svelte-1br9j49 summary.svelte-1br9j49:before{content:"−"}.invite-block.svelte-1br9j49.svelte-1br9j49{display:flex;flex-direction:column;gap:10px;margin-top:12px}.connected-line.svelte-1br9j49.svelte-1br9j49{display:flex;align-items:center;gap:8px;color:var(--text-bright)}.info-line.svelte-1br9j49.svelte-1br9j49{display:flex;justify-content:space-between;font-size:var(--font-size-sm);color:var(--text-secondary)}.info-value.svelte-1br9j49.svelte-1br9j49{color:var(--text-primary);font-family:var(--font-mono)}.sq.svelte-1br9j49.svelte-1br9j49{width:8px;height:8px;display:inline-block}.sq-green.svelte-1br9j49.svelte-1br9j49{background:var(--success)}.sq-warn.svelte-1br9j49.svelte-1br9j49{background:var(--warning)}.sr-only.svelte-1br9j49.svelte-1br9j49{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.light-theme input.svelte-1br9j49.svelte-1br9j49{background:#ffffffa6}.light-theme input.svelte-1br9j49.svelte-1br9j49:focus{background:#ffffffe6}.chat-view.svelte-19atdil.svelte-19atdil{display:flex;flex-direction:column;height:100%;flex:1;padding:12px 14px;min-height:0;background:var(--surface)}.section-head.svelte-19atdil.svelte-19atdil{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:8px;padding-bottom:10px;border-bottom:1px solid var(--border);flex-shrink:0}.section-head.svelte-19atdil h2.svelte-19atdil{font-size:1.05rem;font-weight:600;letter-spacing:-.03em;color:var(--text);margin:0}.chat-messages.svelte-19atdil.svelte-19atdil{flex:1;overflow-y:auto;min-height:0;padding:4px 0 12px;font-family:var(--font-mono);font-size:12px}.empty.svelte-19atdil.svelte-19atdil{padding:12px 2px;color:var(--muted);font-family:var(--font-sans)}.empty-label.svelte-19atdil.svelte-19atdil{color:var(--text);margin-bottom:4px;font-weight:500}.empty-desc.svelte-19atdil.svelte-19atdil{font-family:var(--font-serif);font-style:italic;font-size:13px;margin:0}.date-sep.svelte-19atdil.svelte-19atdil{text-align:left;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:14px 0 8px;font-family:var(--font-sans)}.system-line.svelte-19atdil.svelte-19atdil{font-size:12px;color:var(--muted);margin:6px 0}.msg.svelte-19atdil.svelte-19atdil{margin-bottom:8px}.msg-meta.svelte-19atdil.svelte-19atdil{font-size:11px;color:var(--muted);margin-bottom:2px;display:flex;gap:8px;align-items:baseline}.msg.self.svelte-19atdil .msg-nick.svelte-19atdil{color:var(--text)}.msg-time.svelte-19atdil.svelte-19atdil{color:var(--text-dim)}.msg-nick.svelte-19atdil.svelte-19atdil{color:var(--text);font-weight:500}.msg-body.svelte-19atdil.svelte-19atdil{color:var(--text);white-space:pre-wrap;word-break:break-word;line-height:1.45}.msg.failed.svelte-19atdil .msg-body.svelte-19atdil{color:var(--fault)}.retry-btn.svelte-19atdil.svelte-19atdil{margin-left:auto;background:transparent;border:none;color:var(--fault);cursor:pointer;font-size:11px;font-family:inherit}.composer.svelte-19atdil.svelte-19atdil{display:flex;gap:8px;padding:10px 0 0;border-top:1px solid var(--border);flex-shrink:0;align-items:flex-end}textarea.svelte-19atdil.svelte-19atdil{flex:1;resize:none;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:8px 10px;font-family:var(--font-sans);font-size:14px;min-height:40px;line-height:1.4}textarea.svelte-19atdil.svelte-19atdil:disabled{opacity:.5}.settings-view.svelte-zmj6k4.svelte-zmj6k4{display:flex;flex-direction:column;height:100%;padding:12px 16px 0;min-height:0}.section-head.svelte-zmj6k4.svelte-zmj6k4{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;flex-shrink:0}.section-head.svelte-zmj6k4 h2.svelte-zmj6k4{font-size:1.05rem;font-weight:600;letter-spacing:-.03em;color:var(--text);margin:0}.settings-content.svelte-zmj6k4.svelte-zmj6k4{flex:1;overflow-y:auto;min-height:0;display:flex;flex-direction:column;gap:8px;padding-bottom:12px}.reconnect-banner.svelte-zmj6k4.svelte-zmj6k4{background:var(--surface-2);border:1px solid var(--hairline);color:var(--text);padding:10px 12px;border-radius:var(--radius);font-size:13px}.toggle-row.svelte-zmj6k4.svelte-zmj6k4{display:flex;align-items:flex-start;gap:10px;margin:10px 0;cursor:pointer}.toggle-title.svelte-zmj6k4.svelte-zmj6k4{display:block;font-size:13px;font-weight:600;color:var(--text)}.toggle-label.svelte-zmj6k4.svelte-zmj6k4{display:block;color:var(--muted);font-size:12px;margin-top:2px}input[type=checkbox].svelte-zmj6k4.svelte-zmj6k4{width:16px;height:16px;margin-top:2px;accent-color:var(--cta)}.scale-row.svelte-zmj6k4.svelte-zmj6k4{display:flex;align-items:center;gap:12px}.scale-row.svelte-zmj6k4 input[type=range].svelte-zmj6k4{flex:1;accent-color:var(--cta)}.scale-value.svelte-zmj6k4.svelte-zmj6k4{min-width:44px;text-align:right;color:var(--muted);font-family:var(--font-mono);font-size:13px}.theme-row.svelte-zmj6k4.svelte-zmj6k4{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:12px 0 16px}.update-row.svelte-zmj6k4.svelte-zmj6k4{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:12px 0}.ver.svelte-zmj6k4.svelte-zmj6k4{color:var(--muted);font-size:12px;font-family:var(--font-mono)}.commit-plate.svelte-zmj6k4.svelte-zmj6k4{display:flex;justify-content:flex-end;gap:8px;padding:12px 0 16px;flex-shrink:0;border-top:1px solid var(--border);background:var(--bg)}.shell.svelte-8tj0mu{display:flex;flex-direction:column;height:100%;width:100%;position:relative;z-index:1;min-height:0}.connect-shell.svelte-8tj0mu{z-index:1}.layout.svelte-8tj0mu{display:flex;flex:1;min-height:0;position:relative}.fullscreen-connect.svelte-8tj0mu{display:flex;flex:1;width:100%;align-items:center;justify-content:center;min-height:0}.sidebar-wrapper.svelte-8tj0mu{position:relative;flex-shrink:0;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;min-height:0}.drag-handle.svelte-8tj0mu{position:absolute;top:0;right:-3px;width:6px;height:100%;cursor:col-resize;z-index:10;background:transparent;transition:background var(--dur-mid, .28s) var(--ease-out, ease)}.drag-handle.svelte-8tj0mu:hover,.drag-handle.active.svelte-8tj0mu,.drag-handle.svelte-8tj0mu:focus-visible{background:var(--border-hover)}.main-area.svelte-8tj0mu{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--bg);min-width:0;min-height:0}.workspace.svelte-8tj0mu{flex:1;display:flex;min-height:0;min-width:0}.peer-pane.svelte-8tj0mu,.settings-pane.svelte-8tj0mu{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}.peer-pane[hidden].svelte-8tj0mu,.chat-pane[hidden].svelte-8tj0mu,.settings-pane[hidden].svelte-8tj0mu{display:none!important}.chat-pane.svelte-8tj0mu{flex:0 0 42%;max-width:480px;min-width:280px;min-height:0;display:flex;flex-direction:column;border-left:1px solid var(--border)}.error-strip.svelte-8tj0mu{position:fixed;left:16px;right:16px;bottom:16px;z-index:1100;display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--surface-2, var(--bg-raised, #111));border:1px solid var(--fault, var(--error, #c41e1e));border-radius:var(--radius, 2px);color:var(--fault, var(--error, #c41e1e));font-size:var(--font-size-sm, 13px)}.error-strip-msg.svelte-8tj0mu{flex:1;min-width:0;word-break:break-word}.error-strip-dismiss.svelte-8tj0mu{flex-shrink:0;min-height:32px;padding:4px 10px;border:1px solid var(--fault, var(--error, #c41e1e));border-radius:var(--radius, 2px);background:transparent;color:var(--fault, var(--error, #c41e1e));cursor:pointer;font-family:inherit;font-size:13px;text-transform:none}.error-strip-dismiss.svelte-8tj0mu:hover{background:color-mix(in srgb,var(--fault, #c41e1e) 12%,transparent)}.notification.svelte-8tj0mu{position:fixed;top:16px;right:16px;padding:12px 16px;font-size:13px;font-weight:500;z-index:1000;background:var(--surface-2, var(--bg-raised, #111));border:1px solid var(--border, #2a2a2a);border-radius:var(--radius, 2px);color:var(--text, var(--text-bright, #f5f5f5));max-width:min(360px,calc(100vw - 2rem))}.notification.error.svelte-8tj0mu{border-color:var(--fault, var(--error, #c41e1e));color:var(--fault, var(--error, #c41e1e))}.space-bg,.space-bg:before,.space-bg:after,.space-dust,.space-dust:before{position:fixed;pointer-events:none;z-index:0}.space-bg{inset:0;background:radial-gradient(ellipse 85% 65% at 50% 36%,rgba(255,255,255,.045) 0%,transparent 55%),radial-gradient(ellipse 120% 80% at 50% 100%,rgba(255,255,255,.03) 0%,transparent 50%),radial-gradient(ellipse 100% 100% at 50% 50%,#0a0a0a,#050505 70%,#000)}.space-bg:before{content:"";position:absolute;inset:0;opacity:.8;background:radial-gradient(1.2px 1.2px at 5% 10%,rgba(255,255,255,.55) 0%,transparent 100%),radial-gradient(1px 1px at 12% 32%,rgba(255,255,255,.35) 0%,transparent 100%),radial-gradient(1px 1px at 18% 68%,rgba(255,255,255,.32) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 25% 45%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 32% 18%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 38% 78%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 45% 15%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 52% 58%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 58% 28%,rgba(255,255,255,.26) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 65% 10%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 72% 72%,rgba(255,255,255,.35) 0%,transparent 100%),radial-gradient(1px 1px at 78% 40%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 85% 65%,rgba(255,255,255,.48) 0%,transparent 100%),radial-gradient(1px 1px at 90% 22%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 94% 88%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1px 1px at 8% 88%,rgba(255,255,255,.26) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 30% 92%,rgba(255,255,255,.4) 0%,transparent 100%),radial-gradient(1px 1px at 48% 42%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1px 1px at 62% 88%,rgba(255,255,255,.32) 0%,transparent 100%),radial-gradient(1px 1px at 88% 52%,rgba(255,255,255,.26) 0%,transparent 100%)}.space-bg:after{content:"";position:absolute;inset:0;background-image:radial-gradient(1.5px 1.5px at 8% 18%,rgba(255,255,255,.7) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 48% 38%,rgba(255,255,255,.6) 0%,transparent 100%),radial-gradient(1.5px 1.5px at 88% 28%,rgba(255,255,255,.65) 0%,transparent 100%),radial-gradient(1px 1px at 22% 55%,rgba(255,255,255,.45) 0%,transparent 100%),radial-gradient(1px 1px at 70% 60%,rgba(255,255,255,.4) 0%,transparent 100%);animation:twinkle 9s ease-in-out infinite alternate;opacity:.5}@keyframes twinkle{0%{opacity:.2}45%{opacity:.5}to{opacity:.25}}.space-dust{inset:0;background:radial-gradient(ellipse 80% 70% at 50% 45%,transparent 42%,rgba(0,0,0,.62) 100%);opacity:1}.space-dust:before{content:"";position:absolute;inset:0;opacity:.32;background:radial-gradient(.5px .5px at 20% 30%,rgba(255,255,255,.4) 0%,transparent 100%),radial-gradient(.5px .5px at 40% 60%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(.5px .5px at 60% 25%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(.5px .5px at 75% 70%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(.5px .5px at 88% 40%,rgba(255,255,255,.3) 0%,transparent 100%)}.light-theme .space-bg,.light-theme .space-bg:before,.light-theme .space-bg:after,.light-theme .space-dust,.light-theme .space-dust:before{display:none!important}@font-face{font-family:Bricolage Grotesque;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/bricolage-latin-400-A6LyuA6R.woff2) format("woff2")}@font-face{font-family:Bricolage Grotesque;font-style:normal;font-weight:500;font-display:swap;src:url(/assets/bricolage-latin-500-DEDBoLFO.woff2) format("woff2")}@font-face{font-family:Bricolage Grotesque;font-style:normal;font-weight:600;font-display:swap;src:url(/assets/bricolage-latin-600-nxTgbNFE.woff2) format("woff2")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/ibm-plex-mono-latin-400-Dm_PoFIZ.woff2) format("woff2")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:500;font-display:swap;src:url(/assets/ibm-plex-mono-latin-500-C_OblDzq.woff2) format("woff2")}@font-face{font-family:"IBM Plex Serif";font-style:italic;font-weight:400;font-display:swap;src:url(/assets/ibm-plex-serif-latin-400-italic-yImJBWyJ.woff2) format("woff2")}html[data-app=connected]{color-scheme:dark;--bg: #0a0a0a;--surface: #141414;--surface-2: #1a1a1a;--border: #2a2a2a;--hairline: #3a3a3a;--muted: #8a8a8a;--text: #f5f5f5;--cta: #ffffff;--cta-fill: #ffffff;--cta-ink: #0a0a0a;--caution: #a3a3a3;--danger: #f5f5f5;--live: #2fbf62;--fault: #e23d3d;--radius: 2px;--graticule: #262626;--font-sans: "Bricolage Grotesque", "Segoe UI", sans-serif;--font-serif: "IBM Plex Serif", Georgia, serif;--font-mono: "IBM Plex Mono", Consolas, monospace;--switch-track: #1a1a1a;--switch-thumb: #f5f5f5;--switch-icon-on: #0a0a0a;--ease-out: cubic-bezier(.16, 1, .3, 1);--ease-inout: cubic-bezier(.4, 0, .2, 1);--dur-fast: .15s;--dur-mid: .28s;--dur-slow: .42s;--bg-deep: var(--bg);--bg-primary: var(--bg);--bg-surface: var(--surface);--bg-raised: var(--surface-2);--bg-hover: #222;--bg-active: #262626;--border-deep: var(--border);--border-light: var(--hairline);--border-hover: var(--hairline);--text-bright: var(--text);--text-primary: var(--text);--text-secondary: var(--muted);--text-muted: var(--muted);--text-dim: #5c5c5c;--accent: var(--cta-fill);--accent-hover: #e8e8e8;--accent-ink: var(--cta-ink);--success: var(--live);--warning: var(--caution);--error: var(--fault);--focus-ring: var(--cta);--font-size: 14px;--font-size-sm: 13px;--font-size-xs: 12px;--line-height: 1.5}html[data-app=connected][data-theme=light]{color-scheme:light;--bg: #ffffff;--surface: #f7f7f7;--surface-2: #eeeeee;--border: #d4d4d4;--hairline: #b8b8b8;--muted: #5c5c5c;--text: #111111;--cta: #111111;--cta-fill: #111111;--cta-ink: #ffffff;--caution: #5c5c5c;--danger: #111111;--live: #1a8f45;--fault: #c41e1e;--switch-track: #e6e6e6;--switch-thumb: #111111;--switch-icon-on: #ffffff;--bg-hover: #ececec;--bg-active: #e4e4e4;--text-dim: #9a9a9a;--accent-hover: #2a2a2a;--error: var(--fault);--graticule: #d8d8d8}@media (prefers-reduced-motion: reduce){html[data-app=connected]{--dur-fast: .01ms;--dur-mid: .01ms;--dur-slow: .01ms}}html[data-app=connected],html[data-app=connected] body,html[data-app=connected] #app{font-family:var(--font-sans);font-size:var(--font-size);color:var(--text);background:var(--bg)}html[data-app=connected] input,html[data-app=connected] textarea,html[data-app=connected] button,html[data-app=connected] select{font-family:inherit;font-size:inherit}html[data-app=connected] ::selection{background:color-mix(in srgb,var(--text) 18%,transparent);color:var(--text)}html[data-app=connected] :focus-visible{outline:2px solid var(--text);outline-offset:2px}html[data-app=connected] .dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;background:var(--text-dim)}html[data-app=connected] .dot-live{background:var(--live)}html[data-app=connected] .dot-fault{background:var(--fault)}html[data-app=connected] .dot-idle{background:var(--text-dim)}html[data-app=connected] .btn{border:1px solid var(--border);background:transparent;color:var(--text);padding:8px 14px;min-height:36px;border-radius:var(--radius);cursor:pointer;font-weight:500;font-size:13px;letter-spacing:0;text-transform:none;display:inline-flex;align-items:center;justify-content:center;font-family:var(--font-sans);transition:background var(--dur-mid) var(--ease-out),border-color var(--dur-mid) var(--ease-out),color var(--dur-mid) var(--ease-out),opacity var(--dur-mid) var(--ease-out)}html[data-app=connected] .btn:hover:not(:disabled){border-color:var(--hairline);background:var(--surface-2)}html[data-app=connected] .btn:disabled{opacity:.45;cursor:not-allowed}html[data-app=connected] .btn-ghost{border-color:var(--border);color:var(--muted)}html[data-app=connected] .btn-ghost:hover:not(:disabled){color:var(--text)}html[data-app=connected] .btn-quiet{border-color:transparent;color:var(--muted)}html[data-app=connected] .btn-quiet:hover:not(:disabled){border-color:var(--border);color:var(--text);background:transparent}html[data-app=connected] .btn-cta,html[data-app=connected] .btn.primary,html[data-app=connected] .btn-save{background:var(--cta-fill);color:var(--cta-ink);border-color:var(--cta-fill);font-weight:600}html[data-app=connected] .btn-cta:hover:not(:disabled),html[data-app=connected] .btn.primary:hover:not(:disabled),html[data-app=connected] .btn-save:hover:not(:disabled){background:color-mix(in srgb,var(--cta-fill) 88%,var(--cta-ink))}html[data-app=connected] .btn.danger{color:var(--fault);border-color:color-mix(in srgb,var(--fault) 45%,var(--border))}html[data-app=connected] .card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}html[data-app=connected] .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:48px;padding:10px 14px;background:var(--surface-2)}html[data-app=connected] .card-disclosure{flex:1;min-width:0;display:flex;align-items:center;gap:10px;background:transparent;border:none;padding:0;cursor:pointer;text-align:left;color:inherit;font:inherit;min-height:28px}html[data-app=connected] .card-title{font-size:13px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--text)}html[data-app=connected] .card-body{display:grid;grid-template-rows:1fr;border-top:1px solid var(--border);transition:grid-template-rows var(--dur-slow) var(--ease-out),border-color var(--dur-mid) var(--ease-out)}html[data-app=connected] .card-body.collapsed{grid-template-rows:0fr;border-top-color:transparent}html[data-app=connected] .card-body-inner{overflow:hidden;min-height:0}html[data-app=connected] .card-body-pad{padding:14px 16px 16px}html[data-app=connected] .collapse-chevron{color:var(--muted);width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;transition:transform var(--dur-mid) var(--ease-out);flex-shrink:0}html[data-app=connected] .collapse-chevron svg{width:12px;height:12px;display:block}html[data-app=connected] .collapse-chevron.collapsed{transform:rotate(-90deg)}html[data-app=connected] .field{margin-bottom:12px}html[data-app=connected] .field label,html[data-app=connected] .setting-label{display:block;font-size:12px;font-weight:600;letter-spacing:0;text-transform:none;color:var(--muted);margin-bottom:4px}html[data-app=connected] .field input,html[data-app=connected] .field select,html[data-app=connected] .field textarea,html[data-app=connected] .settings-view input[type=text],html[data-app=connected] .settings-view input[type=password],html[data-app=connected] .settings-view input[type=number],html[data-app=connected] .settings-view select,html[data-app=connected] .modal input{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:8px 10px;min-height:36px;font-size:13px;font-family:var(--font-mono)}html[data-app=connected] .hint{color:var(--muted);font-size:13px;margin:0 0 12px;max-width:65ch;line-height:1.45}html[data-app=connected] .theme-switch{--ts-thumb: 28px;--ts-inset: 3px;--ts-travel: 36px;position:relative;width:calc(var(--ts-thumb) + var(--ts-inset) * 2 + var(--ts-travel));height:calc(var(--ts-thumb) + var(--ts-inset) * 2);padding:0;border:1px solid var(--border);border-radius:999px;background:transparent;cursor:pointer;flex-shrink:0;box-sizing:border-box;min-width:62px;min-height:34px}html[data-app=connected] .theme-switch:hover{border-color:var(--hairline)}html[data-app=connected] .theme-switch-track{position:absolute;inset:0;border-radius:inherit;overflow:hidden}html[data-app=connected] .theme-switch-thumb{position:absolute;top:50%;left:var(--ts-inset);width:var(--ts-thumb);height:var(--ts-thumb);border-radius:50%;background:var(--switch-thumb);border:1px solid var(--border);transform:translate3d(0,-50%,0);z-index:2;pointer-events:none}html[data-app=connected] .theme-switch.is-dark .theme-switch-thumb{transform:translate3d(var(--ts-travel),-50%,0)}html[data-app=connected] .theme-switch-icon{position:absolute;top:50%;width:var(--ts-thumb);height:var(--ts-thumb);display:flex;align-items:center;justify-content:center;color:var(--muted);opacity:.4;transform:translateY(-50%) scale(.92);z-index:3;pointer-events:none}html[data-app=connected] .theme-switch-icon svg{display:block;width:16px;height:16px}html[data-app=connected] .theme-switch-sun{left:var(--ts-inset)}html[data-app=connected] .theme-switch-moon{left:calc(var(--ts-inset) + var(--ts-travel))}html[data-app=connected] .theme-switch:not(.is-dark) .theme-switch-sun,html[data-app=connected] .theme-switch.is-dark .theme-switch-moon{color:var(--switch-icon-on);opacity:1;transform:translateY(-50%) scale(1)}html[data-app=connected] .label-track{display:block;font-size:10px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);line-height:1.2}html[data-app=connected] .led{width:7px;height:7px;border-radius:50%;flex-shrink:0;background:var(--text-dim);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--text) 18%,transparent)}html[data-app=connected] .led.on.live{background:var(--live)}html[data-app=connected] .led.on.fault{background:var(--fault)}html[data-app=connected] .led.on.idle{background:var(--text)}html[data-app=connected] .bezel{border:1px solid var(--border);background:transparent;color:var(--text);min-height:32px;padding:4px 10px;border-radius:var(--radius);font-size:11px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;font-family:var(--font-sans)}html[data-app=connected] .bezel:hover:not(:disabled){border-color:var(--hairline);background:var(--surface-2)}html[data-app=connected] .bezel.active{background:var(--text);color:var(--bg);border-color:var(--text)}@font-face{font-family:Bungee;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/bungee-latin-Dfnurram.woff2) format("woff2")}:root{--bg-deep: #000000;--bg-primary: #050505;--bg-surface: #0a0a0a;--bg-raised: #111111;--bg-hover: #181818;--bg-active: #1f1f1f;--border-deep: #111111;--border: #1c1c1c;--border-light: #2a2a2a;--border-hover: #3d3d3d;--text-bright: #f5f5f5;--text-primary: #c8c8c8;--text-secondary: #8a8a8a;--text-muted: #5c5c5c;--text-dim: #3a3a3a;--accent: #f0f0f0;--accent-hover: #ffffff;--accent-ink: #000000;--success: #9a9a9a;--warning: #c8c8c8;--error: #c45c5c;--focus-ring: #f0f0f0;--sidebar-width: 220px;--font-display: "Bungee", ui-sans-serif, system-ui, sans-serif;--font-mono: ui-monospace, "Cascadia Code", "SF Mono", Consolas, "Liberation Mono", monospace;--font-size: 12px;--font-size-sm: 11px;--font-size-xs: 10px;--line-height: 1.6}.light-theme{--bg-deep: #f5f5f5;--bg-primary: #f0f0f0;--bg-surface: #e8e8e8;--bg-raised: #e0e0e0;--bg-hover: #d8d8d8;--bg-active: #cfcfcf;--border-deep: #d0d0d0;--border: #cccccc;--border-light: #bbbbbb;--border-hover: #999999;--text-bright: #111111;--text-primary: #222222;--text-secondary: #555555;--text-muted: #888888;--text-dim: #aaaaaa;--accent: #000000;--accent-hover: #222222;--accent-ink: #f5f5f5;--success: #555555;--warning: #333333;--error: #b33a3a;--focus-ring: #000000}*,*:before,*:after{margin:0;padding:0;box-sizing:border-box}html,body,#app{height:100%;width:100%;overflow:hidden;color:var(--text-primary);font-family:var(--font-mono);font-size:var(--font-size);line-height:var(--line-height);-webkit-font-smoothing:antialiased;user-select:auto}html,body{background:var(--bg-deep)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border)}::-webkit-scrollbar-thumb:hover{background:var(--border-hover)}::-webkit-scrollbar-corner{background:transparent}::selection{background:#ffffff1a;color:var(--text-bright)}input,textarea,button,select{font-family:var(--font-mono);font-size:var(--font-size)}input:focus-visible,textarea:focus-visible,select:focus-visible,button:focus-visible,[tabindex]:focus-visible,.network-item:focus-visible,.clickable:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}button{cursor:pointer;border:none;background:none;color:inherit}a{color:var(--text-bright);text-decoration:none}a:hover{text-decoration:underline}.btn{padding:8px 12px;min-height:36px;border:1px solid var(--border);background:var(--bg-raised);color:var(--text-primary);cursor:pointer;font-size:var(--font-size-sm);text-align:center;font-family:var(--font-mono);transition:background .1s ease,opacity .1s ease}.btn:hover:not(:disabled){background:var(--bg-hover)}.btn.primary,.btn-save{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:500}.btn.primary:hover,.btn-save:hover{background:var(--accent-hover)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn.danger{color:var(--error);border-color:var(--error)}.empty-state{padding:16px;text-align:center;color:var(--text-muted);font-size:var(--font-size-sm)} diff --git a/client/frontend/dist/assets/index-coA7a2P7.css b/client/frontend/dist/assets/index-coA7a2P7.css deleted file mode 100644 index 43570b8..0000000 --- a/client/frontend/dist/assets/index-coA7a2P7.css +++ /dev/null @@ -1 +0,0 @@ -.sidebar.svelte-1oaanap.svelte-1oaanap{width:100%;height:100%;background:var(--bg-surface);display:flex;flex-direction:column;flex-shrink:0}.sidebar-header.svelte-1oaanap.svelte-1oaanap{padding:12px;border-bottom:1px solid var(--border)}.sidebar-header.svelte-1oaanap h2.svelte-1oaanap{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:var(--text-secondary);font-weight:600}.sidebar-meta.svelte-1oaanap.svelte-1oaanap{font-size:var(--font-size-xs);color:var(--text-muted);margin-top:4px}.sidebar-actions.svelte-1oaanap.svelte-1oaanap{padding:8px;display:flex;flex-direction:column;gap:6px;border-bottom:1px solid var(--border)}.sidebar-actions.svelte-1oaanap .btn.svelte-1oaanap{min-height:36px;padding:8px 10px}.network-list.svelte-1oaanap.svelte-1oaanap{flex:1;overflow-y:auto;overflow-x:hidden;padding:4px 0}.network-item.svelte-1oaanap.svelte-1oaanap{display:flex;align-items:center;padding:10px 12px;min-height:44px;cursor:pointer;gap:8px;border-bottom:1px solid var(--border-deep);transition:background .12s ease,opacity .12s ease;position:relative}.network-item.svelte-1oaanap.svelte-1oaanap:hover,.network-item.svelte-1oaanap.svelte-1oaanap:focus-visible{background:var(--bg-hover)}.network-item.active.svelte-1oaanap.svelte-1oaanap{background:var(--bg-active)}.network-item.disabled.svelte-1oaanap.svelte-1oaanap{opacity:.55}.network-indicator.svelte-1oaanap.svelte-1oaanap{width:4px;height:12px;background:var(--text-muted);flex-shrink:0}.network-indicator.active-dot.svelte-1oaanap.svelte-1oaanap{background:var(--accent)}.network-info.svelte-1oaanap.svelte-1oaanap{flex:1;min-width:0}.network-name.svelte-1oaanap.svelte-1oaanap{font-size:var(--font-size);color:var(--text-bright);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.network-meta.svelte-1oaanap.svelte-1oaanap{font-size:var(--font-size-xs);color:var(--text-secondary);line-height:1.4}.row-actions.svelte-1oaanap.svelte-1oaanap{position:relative;flex-shrink:0}.menu-btn.svelte-1oaanap.svelte-1oaanap{background:none;border:1px solid transparent;color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);font-size:16px;line-height:1;width:32px;height:32px;display:flex;align-items:center;justify-content:center}.menu-btn.svelte-1oaanap.svelte-1oaanap:hover,.menu-btn[aria-expanded=true].svelte-1oaanap.svelte-1oaanap{color:var(--text-bright);border-color:var(--border);background:var(--bg-raised)}.overflow-menu.svelte-1oaanap.svelte-1oaanap{position:absolute;right:0;top:100%;z-index:20;min-width:160px;background:var(--bg-raised);border:1px solid var(--border);display:flex;flex-direction:column;box-shadow:0 4px 12px #00000059}.menu-item.svelte-1oaanap.svelte-1oaanap{background:none;border:none;text-align:left;padding:10px 12px;min-height:36px;color:var(--text-primary);font-family:var(--font-mono);font-size:var(--font-size-sm);cursor:pointer}.menu-item.svelte-1oaanap.svelte-1oaanap:hover{background:var(--bg-hover);color:var(--text-bright)}.menu-item.danger.svelte-1oaanap.svelte-1oaanap{color:var(--error)}.menu-item.danger.svelte-1oaanap.svelte-1oaanap:hover{background:#c45c5c1f}.empty-desc.svelte-1oaanap.svelte-1oaanap{margin-top:6px;font-size:var(--font-size-xs);color:var(--text-dim)}.empty-state.svelte-1oaanap.svelte-1oaanap{padding:16px;text-align:center;color:var(--text-muted);font-size:var(--font-size-sm)}.sidebar-footer.svelte-1oaanap.svelte-1oaanap{padding:8px;border-top:1px solid var(--border)}.settings-btn.svelte-1oaanap.svelte-1oaanap{width:100%;min-height:36px}.modal-overlay.svelte-1oaanap.svelte-1oaanap{position:fixed;inset:0;background:#000000b3;display:flex;align-items:center;justify-content:center;z-index:100;animation:svelte-1oaanap-fadeIn .12s ease-out}@keyframes svelte-1oaanap-fadeIn{0%{opacity:0}to{opacity:1}}.modal.svelte-1oaanap.svelte-1oaanap{background:var(--bg-surface);border:1px solid var(--border);padding:20px;width:320px;max-height:80vh;overflow-y:auto;display:flex;flex-direction:column;gap:10px}.modal.svelte-1oaanap h3.svelte-1oaanap{font-size:var(--font-size);color:var(--text-bright);font-weight:500;text-transform:uppercase;letter-spacing:.5px}.modal.svelte-1oaanap input.svelte-1oaanap{padding:10px;min-height:40px;border:1px solid var(--border);background:var(--bg-primary);color:var(--text-primary);font-size:var(--font-size);font-family:var(--font-mono)}.modal.svelte-1oaanap input.invalid.svelte-1oaanap{border-color:var(--error)}.field-error.svelte-1oaanap.svelte-1oaanap,.modal-error.svelte-1oaanap.svelte-1oaanap{font-size:var(--font-size-xs);color:var(--error)}.modal-error.svelte-1oaanap.svelte-1oaanap{padding:8px;border:1px solid var(--error);background:#c45c5c14}.modal-actions.svelte-1oaanap.svelte-1oaanap{display:flex;gap:6px;justify-content:flex-end}.modal-actions.svelte-1oaanap .btn.svelte-1oaanap{min-height:36px;padding:8px 12px}.sr-only.svelte-1oaanap.svelte-1oaanap{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.peer-view.svelte-mpjrp9.svelte-mpjrp9{display:flex;flex-direction:column;height:100%}.peer-header.svelte-mpjrp9.svelte-mpjrp9{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;background:var(--bg-surface);flex-wrap:wrap}.peer-header.svelte-mpjrp9 h2.svelte-mpjrp9{font-size:var(--font-size);font-weight:500;color:var(--text-bright);text-transform:uppercase;letter-spacing:.5px}.ip-badge.svelte-mpjrp9.svelte-mpjrp9{padding:4px 8px;min-height:28px;background:var(--bg-raised);font-size:var(--font-size-xs);font-family:var(--font-mono);color:var(--text-secondary);border:1px solid var(--border);cursor:pointer}.ip-badge.svelte-mpjrp9.svelte-mpjrp9:hover{border-color:var(--border-hover);color:var(--text-bright)}.room-chat-btn.svelte-mpjrp9.svelte-mpjrp9{margin-left:auto;background:transparent;border:1px solid var(--border);color:var(--text-secondary);font-family:var(--font-mono);font-size:var(--font-size-xs);padding:6px 10px;min-height:32px;cursor:pointer;position:relative}.room-chat-btn.svelte-mpjrp9.svelte-mpjrp9:hover{color:var(--text-bright);border-color:var(--border-hover)}.relay-hint.svelte-mpjrp9.svelte-mpjrp9{font-size:var(--font-size-xs);color:var(--text-muted)}.badge.svelte-mpjrp9.svelte-mpjrp9{display:inline-block;min-width:14px;padding:0 4px;margin-left:4px;background:var(--accent);color:var(--accent-ink);font-size:10px;line-height:14px;text-align:center}.peer-table.svelte-mpjrp9.svelte-mpjrp9{flex:1;overflow:auto;min-height:0;min-width:0}.table-row.svelte-mpjrp9.svelte-mpjrp9{display:grid;grid-template-columns:minmax(56px,72px) minmax(72px,1fr) minmax(88px,110px) minmax(48px,70px) minmax(52px,72px) minmax(64px,80px);gap:8px;align-items:center;padding:10px 16px;min-height:40px;border-bottom:1px solid var(--border-deep);font-size:var(--font-size-sm);min-width:520px}.table-header-row.svelte-mpjrp9.svelte-mpjrp9{color:var(--text-muted);text-transform:uppercase;font-size:var(--font-size-xs);letter-spacing:.4px}.self-row.svelte-mpjrp9.svelte-mpjrp9{background:var(--bg-surface)}.nickname.svelte-mpjrp9.svelte-mpjrp9{color:var(--text-bright);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.self-tag.svelte-mpjrp9.svelte-mpjrp9{margin-left:6px;font-size:var(--font-size-xs);color:var(--text-muted)}.col-ip.svelte-mpjrp9.svelte-mpjrp9,.col-ping.svelte-mpjrp9.svelte-mpjrp9,.col-path.svelte-mpjrp9.svelte-mpjrp9{font-family:var(--font-mono);color:var(--text-secondary)}.col-ping.probing.svelte-mpjrp9.svelte-mpjrp9{color:var(--text-bright)}.clickable.svelte-mpjrp9.svelte-mpjrp9{cursor:pointer}.clickable.svelte-mpjrp9.svelte-mpjrp9:hover{color:var(--text-bright)}.status-cell.svelte-mpjrp9.svelte-mpjrp9{display:flex;align-items:center;gap:6px}.status-text.svelte-mpjrp9.svelte-mpjrp9{font-size:var(--font-size-xs);color:var(--text-secondary)}.sq.svelte-mpjrp9.svelte-mpjrp9{width:8px;height:8px;display:inline-block;flex-shrink:0}.sq-online.svelte-mpjrp9.svelte-mpjrp9{background:var(--success)}.sq-offline.svelte-mpjrp9.svelte-mpjrp9{background:var(--text-dim)}.col-action.svelte-mpjrp9.svelte-mpjrp9{display:flex;gap:2px;justify-content:flex-end}.row-btn.svelte-mpjrp9.svelte-mpjrp9{background:transparent;border:none;color:var(--text-muted);cursor:pointer;font-family:var(--font-mono);min-width:32px;min-height:32px;padding:0}.row-btn.svelte-mpjrp9.svelte-mpjrp9:hover:not(:disabled){color:var(--text-bright)}.row-btn.svelte-mpjrp9.svelte-mpjrp9:disabled{opacity:.35;cursor:not-allowed}.empty-peers.svelte-mpjrp9.svelte-mpjrp9{padding:48px 24px;text-align:center;color:var(--text-muted)}.empty-label.svelte-mpjrp9.svelte-mpjrp9{color:var(--text-secondary);margin:8px 0 4px}.empty-desc.svelte-mpjrp9.svelte-mpjrp9{font-size:var(--font-size-xs)}.statusbar.svelte-1pzfkl{min-height:var(--statusbar-height);background:var(--bg-deep);border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:12px;padding:4px 12px;font-size:var(--font-size-xs);color:var(--text-secondary);flex-shrink:0;font-family:var(--font-mono);z-index:1;position:relative;min-width:0}.status-left.svelte-1pzfkl,.status-right.svelte-1pzfkl{display:flex;align-items:center;gap:8px 14px;min-width:0;flex-wrap:wrap}.status-left.svelte-1pzfkl{flex:0 1 auto}.status-right.svelte-1pzfkl{flex:1 1 auto;justify-content:flex-end;opacity:.85}.status-item.svelte-1pzfkl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:36ch}.status-server.svelte-1pzfkl{max-width:28ch}.status-room.svelte-1pzfkl{max-width:22ch}.status-ver.svelte-1pzfkl{flex-shrink:0;max-width:none}.degraded.svelte-1pzfkl{color:var(--warning)}.sq.svelte-1pzfkl{display:inline-block;width:7px;height:7px;background:var(--error);vertical-align:middle;flex-shrink:0}.sq.sq-connected.svelte-1pzfkl{background:var(--success)}.sq.sq-warn.svelte-1pzfkl{background:var(--warning)}.connect-view.svelte-1br9j49.svelte-1br9j49{display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:32px 24px}.connect-form.svelte-1br9j49.svelte-1br9j49,.connected-info.svelte-1br9j49.svelte-1br9j49{width:min(360px,100%);display:flex;flex-direction:column;gap:16px;animation:svelte-1br9j49-connectIn .45s cubic-bezier(.16,1,.3,1) both}@keyframes svelte-1br9j49-connectIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.brand-block.svelte-1br9j49.svelte-1br9j49{text-align:center;margin-bottom:12px}.brand.svelte-1br9j49.svelte-1br9j49{font-family:var(--font-display);font-size:clamp(36px,7vw,48px);font-weight:400;letter-spacing:.02em;text-transform:uppercase;color:var(--text-bright);line-height:1.05}.tagline.svelte-1br9j49.svelte-1br9j49{margin-top:10px;font-size:var(--font-size-sm);color:var(--text-secondary);text-transform:none;letter-spacing:.02em;line-height:1.45}.field.svelte-1br9j49.svelte-1br9j49{display:flex;flex-direction:column;gap:7px}label.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--text-secondary);text-transform:uppercase;letter-spacing:.08em}input.svelte-1br9j49.svelte-1br9j49{background:#111111d9;border:1px solid var(--border-light);color:var(--text-bright);padding:13px 14px;min-height:44px;font-family:var(--font-mono);font-size:var(--font-size);transition:border-color .15s ease,background .15s ease}input.svelte-1br9j49.svelte-1br9j49::placeholder{color:var(--text-dim)}input.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){border-color:var(--border-hover)}input.svelte-1br9j49.svelte-1br9j49:focus{outline:none;border-color:var(--text-bright);background:#141414f2}input.invalid.svelte-1br9j49.svelte-1br9j49{border-color:var(--error)}.field-error.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--error)}.connect-btn.svelte-1br9j49.svelte-1br9j49,.disconnect-btn.svelte-1br9j49.svelte-1br9j49,.secondary-btn.svelte-1br9j49.svelte-1br9j49{border:none;padding:15px 18px;min-height:48px;font-family:var(--font-mono);font-size:var(--font-size);cursor:pointer;text-transform:uppercase;letter-spacing:.12em;font-weight:600;transition:background .15s ease,border-color .15s ease,transform .12s ease}.connect-btn.svelte-1br9j49.svelte-1br9j49{margin-top:4px;background:var(--accent);color:var(--accent-ink)}.connect-btn.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){background:var(--accent-hover);transform:translateY(-1px)}.connect-btn.svelte-1br9j49.svelte-1br9j49:active:not(:disabled){transform:translateY(0)}.connect-btn.svelte-1br9j49.svelte-1br9j49:disabled{opacity:.45;cursor:not-allowed;transform:none}.secondary-btn.svelte-1br9j49.svelte-1br9j49{background:transparent;color:var(--text-primary);border:1px solid var(--border-light);font-weight:500;letter-spacing:.08em}.secondary-btn.svelte-1br9j49.svelte-1br9j49:hover:not(:disabled){border-color:var(--accent);color:var(--text-bright)}.disconnect-btn.svelte-1br9j49.svelte-1br9j49{background:transparent;color:var(--error);border:1px solid var(--error);font-weight:500}.phase.svelte-1br9j49.svelte-1br9j49{font-size:var(--font-size-xs);color:var(--text-muted);text-align:center;letter-spacing:.04em}.invite-disclosure.svelte-1br9j49.svelte-1br9j49{margin-top:8px;border-top:1px solid var(--border);padding-top:14px}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49{cursor:pointer;color:var(--text-muted);font-size:var(--font-size-sm);list-style:none;min-height:36px;display:flex;align-items:center;justify-content:center;transition:color .15s ease}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49:hover{color:var(--text-secondary)}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49::-webkit-details-marker{display:none}.invite-disclosure.svelte-1br9j49 summary.svelte-1br9j49:before{content:"+";margin-right:8px;color:var(--text-dim)}.invite-disclosure[open].svelte-1br9j49 summary.svelte-1br9j49:before{content:"−"}.invite-block.svelte-1br9j49.svelte-1br9j49{display:flex;flex-direction:column;gap:10px;margin-top:12px}.connected-line.svelte-1br9j49.svelte-1br9j49{display:flex;align-items:center;gap:8px;color:var(--text-bright)}.info-line.svelte-1br9j49.svelte-1br9j49{display:flex;justify-content:space-between;font-size:var(--font-size-sm);color:var(--text-secondary)}.info-value.svelte-1br9j49.svelte-1br9j49{color:var(--text-primary);font-family:var(--font-mono)}.sq.svelte-1br9j49.svelte-1br9j49{width:8px;height:8px;display:inline-block}.sq-green.svelte-1br9j49.svelte-1br9j49{background:var(--success)}.sq-warn.svelte-1br9j49.svelte-1br9j49{background:var(--warning)}.sr-only.svelte-1br9j49.svelte-1br9j49{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.light-theme input.svelte-1br9j49.svelte-1br9j49{background:#ffffffa6}.light-theme input.svelte-1br9j49.svelte-1br9j49:focus{background:#ffffffe6}.chat-view.svelte-11c1d7e.svelte-11c1d7e{display:flex;flex-direction:column;height:100%}.chat-header.svelte-11c1d7e.svelte-11c1d7e{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--border);background:var(--bg-surface)}.back-btn.svelte-11c1d7e.svelte-11c1d7e{width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:transparent;border:1px solid var(--border);color:var(--text-secondary);cursor:pointer;font-family:var(--font-mono)}.chat-title.svelte-11c1d7e.svelte-11c1d7e{color:var(--text-bright);font-size:var(--font-size)}.chat-messages.svelte-11c1d7e.svelte-11c1d7e{flex:1;overflow-y:auto;padding:16px;min-height:0}.empty-chat.svelte-11c1d7e.svelte-11c1d7e{text-align:center;padding:40px 16px;color:var(--text-muted)}.empty-chat-label.svelte-11c1d7e.svelte-11c1d7e{color:var(--text-secondary);margin:8px 0 4px}.empty-chat-desc.svelte-11c1d7e.svelte-11c1d7e{font-size:var(--font-size-xs)}.date-sep.svelte-11c1d7e.svelte-11c1d7e{text-align:center;font-size:var(--font-size-xs);color:var(--text-dim);margin:12px 0}.system-line.svelte-11c1d7e.svelte-11c1d7e{text-align:center;font-size:var(--font-size-xs);color:var(--text-muted);margin:8px 0}.msg.svelte-11c1d7e.svelte-11c1d7e{margin-bottom:10px;max-width:80%}.msg.self.svelte-11c1d7e.svelte-11c1d7e{margin-left:auto;text-align:right}.msg-meta.svelte-11c1d7e.svelte-11c1d7e{font-size:var(--font-size-xs);color:var(--text-muted);margin-bottom:2px}.msg-body.svelte-11c1d7e.svelte-11c1d7e{display:inline-block;padding:8px 10px;background:var(--bg-raised);border:1px solid var(--border);color:var(--text-primary);white-space:pre-wrap;word-break:break-word;text-align:left}.msg.self.svelte-11c1d7e .msg-body.svelte-11c1d7e{background:var(--bg-active)}.retry-btn.svelte-11c1d7e.svelte-11c1d7e{margin-left:6px;background:transparent;border:none;color:var(--error);cursor:pointer;font-size:var(--font-size-xs)}.chat-input-area.svelte-11c1d7e.svelte-11c1d7e{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--border);background:var(--bg-surface)}textarea.svelte-11c1d7e.svelte-11c1d7e{flex:1;resize:none;background:var(--bg-raised);border:1px solid var(--border);color:var(--text-primary);padding:8px 10px;font-family:var(--font-mono);font-size:var(--font-size);min-height:36px}textarea.svelte-11c1d7e.svelte-11c1d7e:disabled{opacity:.5}.send-btn.svelte-11c1d7e.svelte-11c1d7e{background:var(--accent);color:var(--accent-ink);border:none;padding:0 16px;cursor:pointer;font-family:var(--font-mono);text-transform:uppercase}.send-btn.svelte-11c1d7e.svelte-11c1d7e:disabled{opacity:.4;cursor:not-allowed}.settings-view.svelte-1pfl9w0.svelte-1pfl9w0{display:flex;flex-direction:column;height:100%;padding:24px 32px}.settings-content.svelte-1pfl9w0.svelte-1pfl9w0{flex:1;overflow-y:auto;min-height:0;height:0}.settings-title.svelte-1pfl9w0.svelte-1pfl9w0{font-size:var(--font-size);color:var(--text-bright);text-transform:uppercase;letter-spacing:1px;font-weight:500;margin-bottom:20px}.reconnect-banner.svelte-1pfl9w0.svelte-1pfl9w0{background:var(--bg-raised);border:1px solid var(--border-hover);color:var(--text-bright);padding:10px 12px;margin-bottom:16px;font-size:var(--font-size-sm)}.section.svelte-1pfl9w0.svelte-1pfl9w0{margin-bottom:24px}.section-title.svelte-1pfl9w0.svelte-1pfl9w0{font-size:var(--font-size-xs);color:var(--text-muted);letter-spacing:1px}.hint.svelte-1pfl9w0.svelte-1pfl9w0{font-size:var(--font-size-xs);color:var(--text-muted);margin:6px 0 12px;line-height:1.4}.reconnect-hint.svelte-1pfl9w0.svelte-1pfl9w0{color:var(--text-secondary)}.setting-row.svelte-1pfl9w0.svelte-1pfl9w0{display:flex;align-items:center;gap:12px;margin-bottom:10px;min-height:32px}.setting-label.svelte-1pfl9w0.svelte-1pfl9w0{min-width:140px;color:var(--text-secondary);font-size:var(--font-size-sm)}.toggle-label.svelte-1pfl9w0.svelte-1pfl9w0{color:var(--text-muted);font-size:var(--font-size-xs)}input[type=text].svelte-1pfl9w0.svelte-1pfl9w0,input[type=password].svelte-1pfl9w0.svelte-1pfl9w0,input[type=number].svelte-1pfl9w0.svelte-1pfl9w0,select.svelte-1pfl9w0.svelte-1pfl9w0{flex:1;background:var(--bg-raised);border:1px solid var(--border);color:var(--text-primary);padding:8px 10px;min-height:36px;font-family:var(--font-mono);font-size:var(--font-size-sm)}input[type=checkbox].svelte-1pfl9w0.svelte-1pfl9w0{width:18px;height:18px}.radio-group.svelte-1pfl9w0.svelte-1pfl9w0{display:flex;gap:12px}.radio-option.svelte-1pfl9w0.svelte-1pfl9w0{display:flex;align-items:center;gap:6px;color:var(--text-secondary);font-size:var(--font-size-sm);min-height:32px}.scale-row.svelte-1pfl9w0 input[type=range].svelte-1pfl9w0{flex:1;min-width:120px;accent-color:var(--accent)}.scale-value.svelte-1pfl9w0.svelte-1pfl9w0{min-width:44px;text-align:right;color:var(--text-secondary);font-size:var(--font-size-sm);font-family:var(--font-mono)}.actions-section.svelte-1pfl9w0.svelte-1pfl9w0{display:flex;gap:8px;align-items:center}.actions-section.svelte-1pfl9w0 .btn.svelte-1pfl9w0{min-height:36px;padding:8px 14px}.update-row.svelte-1pfl9w0.svelte-1pfl9w0{flex-wrap:wrap}.update-row.svelte-1pfl9w0 .btn.svelte-1pfl9w0{min-height:36px;padding:8px 14px}.btn-check.svelte-1pfl9w0.svelte-1pfl9w0,.btn-update.svelte-1pfl9w0.svelte-1pfl9w0{background:var(--bg-raised);border:1px solid var(--border);color:var(--text-primary);font-family:var(--font-mono);font-size:var(--font-size-sm);cursor:pointer}.btn-check.svelte-1pfl9w0.svelte-1pfl9w0:hover:not(:disabled),.btn-update.svelte-1pfl9w0.svelte-1pfl9w0:hover:not(:disabled){border-color:var(--border-hover);color:var(--text-bright)}.btn-check.svelte-1pfl9w0.svelte-1pfl9w0:disabled,.btn-update.svelte-1pfl9w0.svelte-1pfl9w0:disabled{opacity:.5;cursor:default}.ver.svelte-1pfl9w0.svelte-1pfl9w0{margin-left:auto;color:var(--text-dim);font-size:var(--font-size-xs)}.shell.svelte-1hi7jen{display:flex;flex-direction:column;height:100%;width:100%;position:relative;z-index:1;min-height:0}.connect-shell.svelte-1hi7jen{z-index:1}.layout.svelte-1hi7jen{display:flex;flex:1;min-height:0;position:relative}.fullscreen-connect.svelte-1hi7jen{display:flex;flex:1;width:100%;align-items:center;justify-content:center;min-height:0}.sidebar-wrapper.svelte-1hi7jen{position:relative;flex-shrink:0;background:var(--bg-surface);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;min-height:0}.drag-handle.svelte-1hi7jen{position:absolute;top:0;right:-3px;width:6px;height:100%;cursor:col-resize;z-index:10;background:transparent;transition:background .1s}.drag-handle.svelte-1hi7jen:hover,.drag-handle.active.svelte-1hi7jen,.drag-handle.svelte-1hi7jen:focus-visible{background:var(--border-hover)}.main-area.svelte-1hi7jen{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--bg-primary);min-width:0;min-height:0}.view-fade.svelte-1hi7jen{flex:1;display:flex;flex-direction:column;min-height:0;min-width:0;animation:svelte-1hi7jen-viewIn .2s ease-out}@keyframes svelte-1hi7jen-viewIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.unknown-view.svelte-1hi7jen{padding:40px;color:var(--text-muted)}.error-strip.svelte-1hi7jen{position:fixed;left:12px;right:12px;bottom:calc(var(--statusbar-height) + 12px);z-index:1100;display:flex;align-items:center;gap:12px;padding:10px 14px;background:var(--bg-raised);border:1px solid var(--error);color:var(--error);font-size:var(--font-size-sm)}.error-strip-msg.svelte-1hi7jen{flex:1;min-width:0;word-break:break-word}.error-strip-dismiss.svelte-1hi7jen{flex-shrink:0;min-height:32px;padding:4px 10px;border:1px solid var(--error);background:transparent;color:var(--error);cursor:pointer;font-family:var(--font-mono);font-size:var(--font-size-xs);text-transform:uppercase}.error-strip-dismiss.svelte-1hi7jen:hover{background:#c45c5c1f}.notification.svelte-1hi7jen{position:fixed;top:12px;right:12px;padding:6px 12px;font-size:var(--font-size-sm);z-index:1000;background:var(--bg-raised);border:1px solid var(--border);color:var(--text-primary);animation:svelte-1hi7jen-slideIn .15s ease-out}.notification.error.svelte-1hi7jen{border-color:var(--error);color:var(--error)}.notification.info.svelte-1hi7jen{border-color:var(--border)}.notif-icon.svelte-1hi7jen{margin-right:6px;font-weight:700}@keyframes svelte-1hi7jen-slideIn{0%{transform:translate(100%);opacity:0}to{transform:translate(0);opacity:1}}.space-bg,.space-bg:before,.space-bg:after,.space-dust,.space-dust:before{position:fixed;pointer-events:none;z-index:0}.space-bg{inset:0;background:radial-gradient(ellipse 85% 65% at 50% 36%,rgba(255,255,255,.045) 0%,transparent 55%),radial-gradient(ellipse 120% 80% at 50% 100%,rgba(255,255,255,.03) 0%,transparent 50%),radial-gradient(ellipse 100% 100% at 50% 50%,#0a0a0a,#050505 70%,#000)}.space-bg:before{content:"";position:absolute;inset:0;opacity:.8;background:radial-gradient(1.2px 1.2px at 5% 10%,rgba(255,255,255,.55) 0%,transparent 100%),radial-gradient(1px 1px at 12% 32%,rgba(255,255,255,.35) 0%,transparent 100%),radial-gradient(1px 1px at 18% 68%,rgba(255,255,255,.32) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 25% 45%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 32% 18%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 38% 78%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 45% 15%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 52% 58%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 58% 28%,rgba(255,255,255,.26) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 65% 10%,rgba(255,255,255,.5) 0%,transparent 100%),radial-gradient(1px 1px at 72% 72%,rgba(255,255,255,.35) 0%,transparent 100%),radial-gradient(1px 1px at 78% 40%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 85% 65%,rgba(255,255,255,.48) 0%,transparent 100%),radial-gradient(1px 1px at 90% 22%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(1px 1px at 94% 88%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1px 1px at 8% 88%,rgba(255,255,255,.26) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 30% 92%,rgba(255,255,255,.4) 0%,transparent 100%),radial-gradient(1px 1px at 48% 42%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(1px 1px at 62% 88%,rgba(255,255,255,.32) 0%,transparent 100%),radial-gradient(1px 1px at 88% 52%,rgba(255,255,255,.26) 0%,transparent 100%)}.space-bg:after{content:"";position:absolute;inset:0;background-image:radial-gradient(1.5px 1.5px at 8% 18%,rgba(255,255,255,.7) 0%,transparent 100%),radial-gradient(1.2px 1.2px at 48% 38%,rgba(255,255,255,.6) 0%,transparent 100%),radial-gradient(1.5px 1.5px at 88% 28%,rgba(255,255,255,.65) 0%,transparent 100%),radial-gradient(1px 1px at 22% 55%,rgba(255,255,255,.45) 0%,transparent 100%),radial-gradient(1px 1px at 70% 60%,rgba(255,255,255,.4) 0%,transparent 100%);animation:twinkle 9s ease-in-out infinite alternate;opacity:.5}@keyframes twinkle{0%{opacity:.2}45%{opacity:.5}to{opacity:.25}}.space-dust{inset:0;background:radial-gradient(ellipse 80% 70% at 50% 45%,transparent 42%,rgba(0,0,0,.62) 100%);opacity:1}.space-dust:before{content:"";position:absolute;inset:0;opacity:.32;background:radial-gradient(.5px .5px at 20% 30%,rgba(255,255,255,.4) 0%,transparent 100%),radial-gradient(.5px .5px at 40% 60%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(.5px .5px at 60% 25%,rgba(255,255,255,.28) 0%,transparent 100%),radial-gradient(.5px .5px at 75% 70%,rgba(255,255,255,.3) 0%,transparent 100%),radial-gradient(.5px .5px at 88% 40%,rgba(255,255,255,.3) 0%,transparent 100%)}.light-theme .space-bg,.light-theme .space-bg:before,.light-theme .space-bg:after,.light-theme .space-dust,.light-theme .space-dust:before{display:none!important}@font-face{font-family:Bungee;font-style:normal;font-weight:400;font-display:swap;src:url(/assets/bungee-latin-Dfnurram.woff2) format("woff2")}:root{--bg-deep: #000000;--bg-primary: #050505;--bg-surface: #0a0a0a;--bg-raised: #111111;--bg-hover: #181818;--bg-active: #1f1f1f;--border-deep: #111111;--border: #1c1c1c;--border-light: #2a2a2a;--border-hover: #3d3d3d;--text-bright: #f5f5f5;--text-primary: #c8c8c8;--text-secondary: #8a8a8a;--text-muted: #5c5c5c;--text-dim: #3a3a3a;--accent: #f0f0f0;--accent-hover: #ffffff;--accent-ink: #000000;--success: #9a9a9a;--warning: #c8c8c8;--error: #c45c5c;--focus-ring: #f0f0f0;--sidebar-width: 220px;--statusbar-height: 28px;--font-display: "Bungee", ui-sans-serif, system-ui, sans-serif;--font-mono: ui-monospace, "Cascadia Code", "SF Mono", Consolas, "Liberation Mono", monospace;--font-size: 12px;--font-size-sm: 11px;--font-size-xs: 10px;--line-height: 1.6}.light-theme{--bg-deep: #f5f5f5;--bg-primary: #f0f0f0;--bg-surface: #e8e8e8;--bg-raised: #e0e0e0;--bg-hover: #d8d8d8;--bg-active: #cfcfcf;--border-deep: #d0d0d0;--border: #cccccc;--border-light: #bbbbbb;--border-hover: #999999;--text-bright: #111111;--text-primary: #222222;--text-secondary: #555555;--text-muted: #888888;--text-dim: #aaaaaa;--accent: #000000;--accent-hover: #222222;--accent-ink: #f5f5f5;--success: #555555;--warning: #333333;--error: #b33a3a;--focus-ring: #000000}*,*:before,*:after{margin:0;padding:0;box-sizing:border-box}html,body,#app{height:100%;width:100%;overflow:hidden;color:var(--text-primary);font-family:var(--font-mono);font-size:var(--font-size);line-height:var(--line-height);-webkit-font-smoothing:antialiased;user-select:auto}html,body{background:var(--bg-deep)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border)}::-webkit-scrollbar-thumb:hover{background:var(--border-hover)}::-webkit-scrollbar-corner{background:transparent}::selection{background:#ffffff1a;color:var(--text-bright)}input,textarea,button,select{font-family:var(--font-mono);font-size:var(--font-size)}input:focus-visible,textarea:focus-visible,select:focus-visible,button:focus-visible,[tabindex]:focus-visible,.network-item:focus-visible,.clickable:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}button{cursor:pointer;border:none;background:none;color:inherit}a{color:var(--text-bright);text-decoration:none}a:hover{text-decoration:underline}.btn{padding:8px 12px;min-height:36px;border:1px solid var(--border);background:var(--bg-raised);color:var(--text-primary);cursor:pointer;font-size:var(--font-size-sm);text-align:center;font-family:var(--font-mono);transition:background .1s ease,opacity .1s ease}.btn:hover:not(:disabled){background:var(--bg-hover)}.btn.primary,.btn-save{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:500}.btn.primary:hover,.btn-save:hover{background:var(--accent-hover)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn.danger{color:var(--error);border-color:var(--error)}.empty-state{padding:16px;text-align:center;color:var(--text-muted);font-size:var(--font-size-sm)} diff --git a/client/frontend/dist/index.html b/client/frontend/dist/index.html index da5e5ee..83f23ed 100644 --- a/client/frontend/dist/index.html +++ b/client/frontend/dist/index.html @@ -7,8 +7,8 @@ Entangled VPN - - + +
diff --git a/client/frontend/src/App.svelte b/client/frontend/src/App.svelte index 5c46983..82ae039 100644 --- a/client/frontend/src/App.svelte +++ b/client/frontend/src/App.svelte @@ -1,10 +1,10 @@ - - - {#if $view === 'connect'} + +
{:else} -
+
+ +
-
- {#if $view === 'network'} - - {:else if $view === 'chat'} +
+ + +
- -
{/if} @@ -179,7 +196,6 @@ {#each $notifications as notif (notif.id)}
- {notif.type === 'error' ? '!' : '>'} {notif.msg}
{/each} @@ -214,7 +230,7 @@ .sidebar-wrapper { position: relative; flex-shrink: 0; - background: var(--bg-surface); + background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; @@ -229,7 +245,7 @@ cursor: col-resize; z-index: 10; background: transparent; - transition: background 0.1s; + transition: background var(--dur-mid, 280ms) var(--ease-out, ease); } .drag-handle:hover, .drag-handle.active, .drag-handle:focus-visible { background: var(--border-hover); @@ -239,40 +255,50 @@ display: flex; flex-direction: column; overflow: hidden; - background: var(--bg-primary); + background: var(--bg); min-width: 0; min-height: 0; } - .view-fade { + .workspace { flex: 1; display: flex; - flex-direction: column; min-height: 0; min-width: 0; - animation: viewIn 0.2s ease-out; } - @keyframes viewIn { - 0% { opacity: 0; transform: translateY(8px); } - 100% { opacity: 1; transform: translateY(0); } + .peer-pane, .settings-pane { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; } - .unknown-view { - padding: 40px; - color: var(--text-muted); + .peer-pane[hidden], .chat-pane[hidden], .settings-pane[hidden] { + display: none !important; + } + .chat-pane { + flex: 0 0 42%; + max-width: 480px; + min-width: 280px; + min-height: 0; + display: flex; + flex-direction: column; + border-left: 1px solid var(--border); } .error-strip { position: fixed; - left: 12px; - right: 12px; - bottom: calc(var(--statusbar-height) + 12px); + left: 16px; + right: 16px; + bottom: 16px; z-index: 1100; display: flex; align-items: center; gap: 12px; - padding: 10px 14px; - background: var(--bg-raised); - border: 1px solid var(--error); - color: var(--error); - font-size: var(--font-size-sm); + padding: 12px 14px; + background: var(--surface-2, var(--bg-raised, #111)); + border: 1px solid var(--fault, var(--error, #c41e1e)); + border-radius: var(--radius, 2px); + color: var(--fault, var(--error, #c41e1e)); + font-size: var(--font-size-sm, 13px); } .error-strip-msg { flex: 1; @@ -283,42 +309,34 @@ flex-shrink: 0; min-height: 32px; padding: 4px 10px; - border: 1px solid var(--error); + border: 1px solid var(--fault, var(--error, #c41e1e)); + border-radius: var(--radius, 2px); background: transparent; - color: var(--error); + color: var(--fault, var(--error, #c41e1e)); cursor: pointer; - font-family: var(--font-mono); - font-size: var(--font-size-xs); - text-transform: uppercase; + font-family: inherit; + font-size: 13px; + text-transform: none; } .error-strip-dismiss:hover { - background: rgba(196, 92, 92, 0.12); + background: color-mix(in srgb, var(--fault, #c41e1e) 12%, transparent); } .notification { position: fixed; - top: 12px; - right: 12px; - padding: 6px 12px; - font-size: var(--font-size-sm); + top: 16px; + right: 16px; + padding: 12px 16px; + font-size: 13px; + font-weight: 500; z-index: 1000; - background: var(--bg-raised); - border: 1px solid var(--border); - color: var(--text-primary); - animation: slideIn 0.15s ease-out; + background: var(--surface-2, var(--bg-raised, #111)); + border: 1px solid var(--border, #2a2a2a); + border-radius: var(--radius, 2px); + color: var(--text, var(--text-bright, #f5f5f5)); + max-width: min(360px, calc(100vw - 2rem)); } .notification.error { - border-color: var(--error); - color: var(--error); - } - .notification.info { - border-color: var(--border); - } - .notif-icon { - margin-right: 6px; - font-weight: 700; - } - @keyframes slideIn { - from { transform: translateX(100%); opacity: 0; } - to { transform: translateX(0); opacity: 1; } + border-color: var(--fault, var(--error, #c41e1e)); + color: var(--fault, var(--error, #c41e1e)); } diff --git a/client/frontend/src/app.css b/client/frontend/src/app.css index 8e245a1..b334195 100644 --- a/client/frontend/src/app.css +++ b/client/frontend/src/app.css @@ -1,4 +1,5 @@ @import './space.css'; +@import './connected.css'; @font-face { font-family: 'Bungee'; @@ -32,7 +33,6 @@ --error: #c45c5c; --focus-ring: #f0f0f0; --sidebar-width: 220px; - --statusbar-height: 28px; --font-display: 'Bungee', ui-sans-serif, system-ui, sans-serif; --font-mono: ui-monospace, 'Cascadia Code', 'SF Mono', Consolas, 'Liberation Mono', monospace; --font-size: 12px; diff --git a/client/frontend/src/assets/fonts/bricolage-latin-400.woff2 b/client/frontend/src/assets/fonts/bricolage-latin-400.woff2 new file mode 100644 index 0000000..ccc73da Binary files /dev/null and b/client/frontend/src/assets/fonts/bricolage-latin-400.woff2 differ diff --git a/client/frontend/src/assets/fonts/bricolage-latin-500.woff2 b/client/frontend/src/assets/fonts/bricolage-latin-500.woff2 new file mode 100644 index 0000000..ed9b2a2 Binary files /dev/null and b/client/frontend/src/assets/fonts/bricolage-latin-500.woff2 differ diff --git a/client/frontend/src/assets/fonts/bricolage-latin-600.woff2 b/client/frontend/src/assets/fonts/bricolage-latin-600.woff2 new file mode 100644 index 0000000..e9794de Binary files /dev/null and b/client/frontend/src/assets/fonts/bricolage-latin-600.woff2 differ diff --git a/client/frontend/src/assets/fonts/ibm-plex-mono-latin-400.woff2 b/client/frontend/src/assets/fonts/ibm-plex-mono-latin-400.woff2 new file mode 100644 index 0000000..a6c77d6 Binary files /dev/null and b/client/frontend/src/assets/fonts/ibm-plex-mono-latin-400.woff2 differ diff --git a/client/frontend/src/assets/fonts/ibm-plex-mono-latin-500.woff2 b/client/frontend/src/assets/fonts/ibm-plex-mono-latin-500.woff2 new file mode 100644 index 0000000..99c2610 Binary files /dev/null and b/client/frontend/src/assets/fonts/ibm-plex-mono-latin-500.woff2 differ diff --git a/client/frontend/src/assets/fonts/ibm-plex-serif-latin-400-italic.woff2 b/client/frontend/src/assets/fonts/ibm-plex-serif-latin-400-italic.woff2 new file mode 100644 index 0000000..5645b4d Binary files /dev/null and b/client/frontend/src/assets/fonts/ibm-plex-serif-latin-400-italic.woff2 differ diff --git a/client/frontend/src/connected.css b/client/frontend/src/connected.css new file mode 100644 index 0000000..e2bf14c --- /dev/null +++ b/client/frontend/src/connected.css @@ -0,0 +1,465 @@ +@font-face { + font-family: 'Bricolage Grotesque'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('./assets/fonts/bricolage-latin-400.woff2') format('woff2'); +} +@font-face { + font-family: 'Bricolage Grotesque'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('./assets/fonts/bricolage-latin-500.woff2') format('woff2'); +} +@font-face { + font-family: 'Bricolage Grotesque'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('./assets/fonts/bricolage-latin-600.woff2') format('woff2'); +} +@font-face { + font-family: 'IBM Plex Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('./assets/fonts/ibm-plex-mono-latin-400.woff2') format('woff2'); +} +@font-face { + font-family: 'IBM Plex Mono'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('./assets/fonts/ibm-plex-mono-latin-500.woff2') format('woff2'); +} +@font-face { + font-family: 'IBM Plex Serif'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('./assets/fonts/ibm-plex-serif-latin-400-italic.woff2') format('woff2'); +} + +html[data-app="connected"] { + color-scheme: dark; + --bg: #0a0a0a; + --surface: #141414; + --surface-2: #1a1a1a; + --border: #2a2a2a; + --hairline: #3a3a3a; + --muted: #8a8a8a; + --text: #f5f5f5; + --cta: #ffffff; + --cta-fill: #ffffff; + --cta-ink: #0a0a0a; + --caution: #a3a3a3; + --danger: #f5f5f5; + --live: #2fbf62; + --fault: #e23d3d; + --radius: 2px; + --graticule: #262626; + --font-sans: 'Bricolage Grotesque', 'Segoe UI', sans-serif; + --font-serif: 'IBM Plex Serif', Georgia, serif; + --font-mono: 'IBM Plex Mono', Consolas, monospace; + --switch-track: #1a1a1a; + --switch-thumb: #f5f5f5; + --switch-icon-on: #0a0a0a; + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-inout: cubic-bezier(0.4, 0, 0.2, 1); + --dur-fast: 150ms; + --dur-mid: 280ms; + --dur-slow: 420ms; + --bg-deep: var(--bg); + --bg-primary: var(--bg); + --bg-surface: var(--surface); + --bg-raised: var(--surface-2); + --bg-hover: #222; + --bg-active: #262626; + --border-deep: var(--border); + --border-light: var(--hairline); + --border-hover: var(--hairline); + --text-bright: var(--text); + --text-primary: var(--text); + --text-secondary: var(--muted); + --text-muted: var(--muted); + --text-dim: #5c5c5c; + --accent: var(--cta-fill); + --accent-hover: #e8e8e8; + --accent-ink: var(--cta-ink); + --success: var(--live); + --warning: var(--caution); + --error: var(--fault); + --focus-ring: var(--cta); + --font-size: 14px; + --font-size-sm: 13px; + --font-size-xs: 12px; + --line-height: 1.5; +} + +html[data-app="connected"][data-theme="light"] { + color-scheme: light; + --bg: #ffffff; + --surface: #f7f7f7; + --surface-2: #eeeeee; + --border: #d4d4d4; + --hairline: #b8b8b8; + --muted: #5c5c5c; + --text: #111111; + --cta: #111111; + --cta-fill: #111111; + --cta-ink: #ffffff; + --caution: #5c5c5c; + --danger: #111111; + --live: #1a8f45; + --fault: #c41e1e; + --switch-track: #e6e6e6; + --switch-thumb: #111111; + --switch-icon-on: #ffffff; + --bg-hover: #ececec; + --bg-active: #e4e4e4; + --text-dim: #9a9a9a; + --accent-hover: #2a2a2a; + --error: var(--fault); + --graticule: #d8d8d8; +} + +@media (prefers-reduced-motion: reduce) { + html[data-app="connected"] { + --dur-fast: 0.01ms; + --dur-mid: 0.01ms; + --dur-slow: 0.01ms; + } +} + +html[data-app="connected"], +html[data-app="connected"] body, +html[data-app="connected"] #app { + font-family: var(--font-sans); + font-size: var(--font-size); + color: var(--text); + background: var(--bg); +} + +html[data-app="connected"] input, +html[data-app="connected"] textarea, +html[data-app="connected"] button, +html[data-app="connected"] select { + font-family: inherit; + font-size: inherit; +} + +html[data-app="connected"] ::selection { + background: color-mix(in srgb, var(--text) 18%, transparent); + color: var(--text); +} + +html[data-app="connected"] :focus-visible { + outline: 2px solid var(--text); + outline-offset: 2px; +} + +html[data-app="connected"] .dot { + width: 7px; + height: 7px; + border-radius: 50%; + flex-shrink: 0; + background: var(--text-dim); +} +html[data-app="connected"] .dot-live { background: var(--live); } +html[data-app="connected"] .dot-fault { background: var(--fault); } +html[data-app="connected"] .dot-idle { background: var(--text-dim); } + +html[data-app="connected"] .btn { + border: 1px solid var(--border); + background: transparent; + color: var(--text); + padding: 8px 14px; + min-height: 36px; + border-radius: var(--radius); + cursor: pointer; + font-weight: 500; + font-size: 13px; + letter-spacing: 0; + text-transform: none; + display: inline-flex; + align-items: center; + justify-content: center; + font-family: var(--font-sans); + transition: + background var(--dur-mid) var(--ease-out), + border-color var(--dur-mid) var(--ease-out), + color var(--dur-mid) var(--ease-out), + opacity var(--dur-mid) var(--ease-out); +} +html[data-app="connected"] .btn:hover:not(:disabled) { + border-color: var(--hairline); + background: var(--surface-2); +} +html[data-app="connected"] .btn:disabled { + opacity: 0.45; + cursor: not-allowed; +} +html[data-app="connected"] .btn-ghost { + border-color: var(--border); + color: var(--muted); +} +html[data-app="connected"] .btn-ghost:hover:not(:disabled) { + color: var(--text); +} +html[data-app="connected"] .btn-quiet { + border-color: transparent; + color: var(--muted); +} +html[data-app="connected"] .btn-quiet:hover:not(:disabled) { + border-color: var(--border); + color: var(--text); + background: transparent; +} +html[data-app="connected"] .btn-cta, +html[data-app="connected"] .btn.primary, +html[data-app="connected"] .btn-save { + background: var(--cta-fill); + color: var(--cta-ink); + border-color: var(--cta-fill); + font-weight: 600; +} +html[data-app="connected"] .btn-cta:hover:not(:disabled), +html[data-app="connected"] .btn.primary:hover:not(:disabled), +html[data-app="connected"] .btn-save:hover:not(:disabled) { + background: color-mix(in srgb, var(--cta-fill) 88%, var(--cta-ink)); +} +html[data-app="connected"] .btn.danger { + color: var(--fault); + border-color: color-mix(in srgb, var(--fault) 45%, var(--border)); +} + +html[data-app="connected"] .card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} +html[data-app="connected"] .card-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + min-height: 48px; + padding: 10px 14px; + background: var(--surface-2); +} +html[data-app="connected"] .card-disclosure { + flex: 1; + min-width: 0; + display: flex; + align-items: center; + gap: 10px; + background: transparent; + border: none; + padding: 0; + cursor: pointer; + text-align: left; + color: inherit; + font: inherit; + min-height: 28px; +} +html[data-app="connected"] .card-title { + font-size: 13px; + font-weight: 600; + letter-spacing: 0; + text-transform: none; + color: var(--text); +} +html[data-app="connected"] .card-body { + display: grid; + grid-template-rows: 1fr; + border-top: 1px solid var(--border); + transition: + grid-template-rows var(--dur-slow) var(--ease-out), + border-color var(--dur-mid) var(--ease-out); +} +html[data-app="connected"] .card-body.collapsed { + grid-template-rows: 0fr; + border-top-color: transparent; +} +html[data-app="connected"] .card-body-inner { + overflow: hidden; + min-height: 0; +} +html[data-app="connected"] .card-body-pad { + padding: 14px 16px 16px; +} +html[data-app="connected"] .collapse-chevron { + color: var(--muted); + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + transition: transform var(--dur-mid) var(--ease-out); + flex-shrink: 0; +} +html[data-app="connected"] .collapse-chevron svg { + width: 12px; + height: 12px; + display: block; +} +html[data-app="connected"] .collapse-chevron.collapsed { + transform: rotate(-90deg); +} + +html[data-app="connected"] .field { + margin-bottom: 12px; +} +html[data-app="connected"] .field label, +html[data-app="connected"] .setting-label { + display: block; + font-size: 12px; + font-weight: 600; + letter-spacing: 0; + text-transform: none; + color: var(--muted); + margin-bottom: 4px; +} +html[data-app="connected"] .field input, +html[data-app="connected"] .field select, +html[data-app="connected"] .field textarea, +html[data-app="connected"] .settings-view input[type="text"], +html[data-app="connected"] .settings-view input[type="password"], +html[data-app="connected"] .settings-view input[type="number"], +html[data-app="connected"] .settings-view select, +html[data-app="connected"] .modal input { + width: 100%; + background: var(--bg); + border: 1px solid var(--border); + border-radius: var(--radius); + color: var(--text); + padding: 8px 10px; + min-height: 36px; + font-size: 13px; + font-family: var(--font-mono); +} +html[data-app="connected"] .hint { + color: var(--muted); + font-size: 13px; + margin: 0 0 12px; + max-width: 65ch; + line-height: 1.45; +} + +html[data-app="connected"] .theme-switch { + --ts-thumb: 28px; + --ts-inset: 3px; + --ts-travel: 36px; + position: relative; + width: calc(var(--ts-thumb) + var(--ts-inset) * 2 + var(--ts-travel)); + height: calc(var(--ts-thumb) + var(--ts-inset) * 2); + padding: 0; + border: 1px solid var(--border); + border-radius: 999px; + background: transparent; + cursor: pointer; + flex-shrink: 0; + box-sizing: border-box; + min-width: 62px; + min-height: 34px; +} +html[data-app="connected"] .theme-switch:hover { border-color: var(--hairline); } +html[data-app="connected"] .theme-switch-track { + position: absolute; + inset: 0; + border-radius: inherit; + overflow: hidden; +} +html[data-app="connected"] .theme-switch-thumb { + position: absolute; + top: 50%; + left: var(--ts-inset); + width: var(--ts-thumb); + height: var(--ts-thumb); + border-radius: 50%; + background: var(--switch-thumb); + border: 1px solid var(--border); + transform: translate3d(0, -50%, 0); + z-index: 2; + pointer-events: none; +} +html[data-app="connected"] .theme-switch.is-dark .theme-switch-thumb { + transform: translate3d(var(--ts-travel), -50%, 0); +} +html[data-app="connected"] .theme-switch-icon { + position: absolute; + top: 50%; + width: var(--ts-thumb); + height: var(--ts-thumb); + display: flex; + align-items: center; + justify-content: center; + color: var(--muted); + opacity: 0.4; + transform: translateY(-50%) scale(0.92); + z-index: 3; + pointer-events: none; +} +html[data-app="connected"] .theme-switch-icon svg { + display: block; + width: 16px; + height: 16px; +} +html[data-app="connected"] .theme-switch-sun { + left: var(--ts-inset); +} +html[data-app="connected"] .theme-switch-moon { + left: calc(var(--ts-inset) + var(--ts-travel)); +} +html[data-app="connected"] .theme-switch:not(.is-dark) .theme-switch-sun, +html[data-app="connected"] .theme-switch.is-dark .theme-switch-moon { + color: var(--switch-icon-on); + opacity: 1; + transform: translateY(-50%) scale(1); +} + +html[data-app="connected"] .label-track { + display: block; + font-size: 10px; + font-weight: 500; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--muted); + line-height: 1.2; +} +html[data-app="connected"] .led { + width: 7px; + height: 7px; + border-radius: 50%; + flex-shrink: 0; + background: var(--text-dim); + box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 18%, transparent); +} +html[data-app="connected"] .led.on.live { background: var(--live); } +html[data-app="connected"] .led.on.fault { background: var(--fault); } +html[data-app="connected"] .led.on.idle { background: var(--text); } +html[data-app="connected"] .bezel { + border: 1px solid var(--border); + background: transparent; + color: var(--text); + min-height: 32px; + padding: 4px 10px; + border-radius: var(--radius); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + cursor: pointer; + font-family: var(--font-sans); +} +html[data-app="connected"] .bezel:hover:not(:disabled) { + border-color: var(--hairline); + background: var(--surface-2); +} +html[data-app="connected"] .bezel.active { + background: var(--text); + color: var(--bg); + border-color: var(--text); +} diff --git a/client/frontend/src/lib/components/ChatView.svelte b/client/frontend/src/lib/components/ChatView.svelte index e2a6055..1f8204c 100644 --- a/client/frontend/src/lib/components/ChatView.svelte +++ b/client/frontend/src/lib/components/ChatView.svelte @@ -1,7 +1,7 @@
-
- - {title} +
+

{title}

+
{#if $chatMessages.length === 0} -
-
[ . . . ]
-
{$t.no_messages}
-
{$t.no_messages_desc}
+
+
{$t.no_messages}
+

{$t.no_messages_desc}

{/if} {#each $chatMessages as msg, i (msg.id)} @@ -147,8 +145,9 @@ {#if msg.system}
{msg.message}
{:else} -
+
+ {new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })} {msg.isSelf ? ($settings.nickname || $t.you_self) : msg.nickname} {#if msg.status === 'pending'}{/if} {#if msg.status === 'failed'} @@ -161,17 +160,18 @@ {/each}
-
+
- +
@@ -180,114 +180,112 @@ display: flex; flex-direction: column; height: 100%; + flex: 1; + padding: 12px 14px 12px; + min-height: 0; + background: var(--surface); } - .chat-header { + .section-head { display: flex; align-items: center; - gap: 10px; - padding: 12px 16px; + justify-content: space-between; + gap: 12px; + margin-bottom: 8px; + padding-bottom: 10px; border-bottom: 1px solid var(--border); - background: var(--bg-surface); + flex-shrink: 0; } - .back-btn { - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: 1px solid var(--border); - color: var(--text-secondary); - cursor: pointer; - font-family: var(--font-mono); - } - .chat-title { - color: var(--text-bright); - font-size: var(--font-size); + .section-head h2 { + font-size: 1.05rem; + font-weight: 600; + letter-spacing: -0.03em; + color: var(--text); + margin: 0; } .chat-messages { flex: 1; overflow-y: auto; - padding: 16px; min-height: 0; + padding: 4px 0 12px; + font-family: var(--font-mono); + font-size: 12px; } - .empty-chat { - text-align: center; - padding: 40px 16px; - color: var(--text-muted); + .empty { + padding: 12px 2px; + color: var(--muted); + font-family: var(--font-sans); + } + .empty-label { color: var(--text); margin-bottom: 4px; font-weight: 500; } + .empty-desc { + font-family: var(--font-serif); + font-style: italic; + font-size: 13px; + margin: 0; } - .empty-chat-label { color: var(--text-secondary); margin: 8px 0 4px; } - .empty-chat-desc { font-size: var(--font-size-xs); } .date-sep { - text-align: center; - font-size: var(--font-size-xs); - color: var(--text-dim); - margin: 12px 0; + text-align: left; + font-size: 10px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + margin: 14px 0 8px; + font-family: var(--font-sans); } .system-line { - text-align: center; - font-size: var(--font-size-xs); - color: var(--text-muted); - margin: 8px 0; + font-size: 12px; + color: var(--muted); + margin: 6px 0; } .msg { - margin-bottom: 10px; - max-width: 80%; + margin-bottom: 8px; } - .msg.self { margin-left: auto; text-align: right; } .msg-meta { - font-size: var(--font-size-xs); - color: var(--text-muted); + font-size: 11px; + color: var(--muted); margin-bottom: 2px; + display: flex; + gap: 8px; + align-items: baseline; } + .msg.self .msg-nick { color: var(--text); } + .msg-time { color: var(--text-dim); } + .msg-nick { color: var(--text); font-weight: 500; } .msg-body { - display: inline-block; - padding: 8px 10px; - background: var(--bg-raised); - border: 1px solid var(--border); - color: var(--text-primary); + color: var(--text); white-space: pre-wrap; word-break: break-word; - text-align: left; - } - .msg.self .msg-body { - background: var(--bg-active); + line-height: 1.45; } + .msg.failed .msg-body { color: var(--fault); } .retry-btn { - margin-left: 6px; + margin-left: auto; background: transparent; border: none; - color: var(--error); + color: var(--fault); cursor: pointer; - font-size: var(--font-size-xs); + font-size: 11px; + font-family: inherit; } - .chat-input-area { + .composer { display: flex; gap: 8px; - padding: 12px 16px; + padding: 10px 0 0; border-top: 1px solid var(--border); - background: var(--bg-surface); + flex-shrink: 0; + align-items: flex-end; } textarea { flex: 1; resize: none; - background: var(--bg-raised); + background: var(--bg); border: 1px solid var(--border); - color: var(--text-primary); + border-radius: var(--radius); + color: var(--text); padding: 8px 10px; - font-family: var(--font-mono); - font-size: var(--font-size); - min-height: 36px; + font-family: var(--font-sans); + font-size: 14px; + min-height: 40px; + line-height: 1.4; } textarea:disabled { opacity: 0.5; } - .send-btn { - background: var(--accent); - color: var(--accent-ink); - border: none; - padding: 0 16px; - cursor: pointer; - font-family: var(--font-mono); - text-transform: uppercase; - } - .send-btn:disabled { opacity: 0.4; cursor: not-allowed; } diff --git a/client/frontend/src/lib/components/ConfirmDialog.svelte b/client/frontend/src/lib/components/ConfirmDialog.svelte new file mode 100644 index 0000000..d96db05 --- /dev/null +++ b/client/frontend/src/lib/components/ConfirmDialog.svelte @@ -0,0 +1,82 @@ + + +{#if open} + +{/if} + + diff --git a/client/frontend/src/lib/components/ConnectView.svelte b/client/frontend/src/lib/components/ConnectView.svelte index b8ec40f..9c003ba 100644 --- a/client/frontend/src/lib/components/ConnectView.svelte +++ b/client/frontend/src/lib/components/ConnectView.svelte @@ -1,6 +1,6 @@
-
-

{$status.room || $t.network}

- +
+

{$t.channels} {$status.room || $t.peers_list}

{#if $status.room} - {/if} - {#if anyRelay} - {$t.status_relay_degraded} - {/if}
+ {#if pathAggregate === 'relay'} +

{$t.status_relay_degraded}

+ {/if} -
-
- - {$t.col_nickname} - {$t.col_ip} - {$t.col_ping} - {$t.col_path} - -
- -
- - - {$t.status_online} - - - {$settings.nickname || $t.you} - {$t.me} - - copyIP($status.virtualIP)} - on:keydown={(e) => onIpKey(e, $status.virtualIP)} - >{$status.virtualIP || $t.na} - - - - - +
+ +
+
+ {chIndex(0)} +
+ {$settings.nickname || $t.you} + {$t.me} +
+ + {$t.path_unknown} + + +
- {#each sortedPeers as peer (peer.id)} -
- - - {peer.connected ? $t.status_online : $t.status_offline} - - - {peer.nickname || peer.id} - {#if peerUnread(peer.id) > 0}{peerUnread(peer.id)}{/if} - - copyIP(peer.virtualIP)} - on:keydown={(e) => onIpKey(e, peer.virtualIP)} - >{peer.virtualIP || $t.na} - - {#if !peer.connected} - - - {:else if peer.ping >= 0 && !pinging[peer.id]} - {peer.ping}ms - {:else} - ... - {/if} - - {peer.path ? pathLabel(peer.path) : $t.path_unknown} - + {#each sortedPeers as peer, i (peer.id)} + {@const kind = peerDot(peer)} +
+ {chIndex(i + 1)} +
+ {peer.nickname || peer.id} + {#if peerUnread(peer.id) > 0}{peerUnread(peer.id)}{/if} +
- - -
- {:else} -
-
[ . . . ]
-
{$t.no_peers}
-
{$t.no_peers_desc}
-
- {/each} + type="button" + class="peer-ip" + on:click={() => copyIP(peer.virtualIP)} + on:keydown={(e) => onIpKey(e, peer.virtualIP)} + >{peer.virtualIP || $t.na} + + + {peer.connected && peer.path ? pathLabel(peer.path) : $t.path_unknown} + + + {#if !peer.connected} + — + {:else if peer.ping >= 0 && !pinging[peer.id]} + {peer.ping}ms + {:else} + … + {/if} + +
+ + +
+ + {:else} +
+
{$t.no_peers}
+

{$t.no_peers_desc}

+
+ {/each} +
@@ -181,144 +191,176 @@ display: flex; flex-direction: column; height: 100%; + padding: 12px 16px 20px; + overflow: auto; + min-height: 0; } - .peer-header { - padding: 12px 16px; - border-bottom: 1px solid var(--border); + .section-head { display: flex; align-items: center; - gap: 10px; - background: var(--bg-surface); - flex-wrap: wrap; + justify-content: space-between; + gap: 12px; + margin-bottom: 10px; } - .peer-header h2 { - font-size: var(--font-size); - font-weight: 500; - color: var(--text-bright); - text-transform: uppercase; - letter-spacing: 0.5px; + .section-head h2 { + font-size: 1.05rem; + font-weight: 600; + letter-spacing: -0.03em; + color: var(--text); + margin: 0; } - .ip-badge { - padding: 4px 8px; - min-height: 28px; - background: var(--bg-raised); - font-size: var(--font-size-xs); - font-family: var(--font-mono); - color: var(--text-secondary); - border: 1px solid var(--border); - cursor: pointer; + .section-hint { + color: var(--muted); + font-size: 13px; + margin: 0 0 10px; } - .ip-badge:hover { border-color: var(--border-hover); color: var(--text-bright); } - .room-chat-btn { - margin-left: auto; - background: transparent; - border: 1px solid var(--border); - color: var(--text-secondary); - font-family: var(--font-mono); - font-size: var(--font-size-xs); - padding: 6px 10px; - min-height: 32px; - cursor: pointer; + .section-hint.fault { color: var(--fault); } + .scope-face { position: relative; - } - .room-chat-btn:hover { color: var(--text-bright); border-color: var(--border-hover); } - .relay-hint { - font-size: var(--font-size-xs); - color: var(--text-muted); - } - .badge { - display: inline-block; - min-width: 14px; - padding: 0 4px; - margin-left: 4px; - background: var(--accent); - color: var(--accent-ink); - font-size: 10px; - line-height: 14px; - text-align: center; - } - .peer-table { flex: 1; - overflow: auto; min-height: 0; - min-width: 0; + border: 1px solid var(--border); + background-color: var(--bg); + background-image: + repeating-linear-gradient( + to right, + var(--graticule) 0 1px, + transparent 1px 48px + ), + repeating-linear-gradient( + to bottom, + var(--graticule) 0 1px, + transparent 1px 28px + ); } - .table-row { + .channel-head, + .peer-row { display: grid; - grid-template-columns: minmax(56px, 72px) minmax(72px, 1fr) minmax(88px, 110px) minmax(48px, 70px) minmax(52px, 72px) minmax(64px, 80px); - gap: 8px; + grid-template-columns: 36px minmax(0, 1.3fr) 118px 128px 56px auto; + gap: 10px; align-items: center; - padding: 10px 16px; - min-height: 40px; - border-bottom: 1px solid var(--border-deep); - font-size: var(--font-size-sm); - min-width: 520px; - } - .table-header-row { - color: var(--text-muted); + padding: 0 12px; + } + .channel-head { + min-height: 28px; + border-bottom: 1px solid var(--border); + background: color-mix(in srgb, var(--surface) 82%, transparent); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.12em; text-transform: uppercase; - font-size: var(--font-size-xs); - letter-spacing: 0.4px; + color: var(--muted); + } + .stack { + display: flex; + flex-direction: column; + } + .peer-row { + min-height: 44px; + border-bottom: 1px solid var(--border); + background: color-mix(in srgb, var(--bg) 55%, transparent); + } + .self-row { opacity: 0.88; } + .ch { + font-family: var(--font-mono); + font-size: 11px; + color: var(--muted); + } + .nickname { + color: var(--text); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } - .self-row { background: var(--bg-surface); } - .nickname { color: var(--text-bright); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .self-tag { margin-left: 6px; - font-size: var(--font-size-xs); - color: var(--text-muted); + font-size: 11px; + color: var(--muted); + font-weight: 400; + letter-spacing: 0.06em; + text-transform: uppercase; } - .col-ip, .col-ping, .col-path { + .peer-ip { font-family: var(--font-mono); - color: var(--text-secondary); + font-size: 12px; + color: var(--muted); + background: none; + border: 0; + padding: 0; + cursor: pointer; + text-align: left; } - .col-ping.probing { color: var(--text-bright); } - .clickable { cursor: pointer; } - .clickable:hover { color: var(--text-bright); } - .status-cell { - display: flex; + .peer-ip:hover { color: var(--text); } + .col-ping { + font-family: var(--font-mono); + font-size: 12px; + color: var(--muted); + } + .col-ping.probing { color: var(--text); } + .col-path { + display: inline-flex; align-items: center; gap: 6px; + font-size: 12px; + font-weight: 500; + font-family: var(--font-mono); + color: var(--muted); + min-width: 0; } - .status-text { - font-size: var(--font-size-xs); - color: var(--text-secondary); - } - .sq { - width: 8px; - height: 8px; - display: inline-block; + .col-path.live { color: var(--live); } + .col-path.fault { color: var(--fault); } + .col-path.idle { color: var(--muted); } + .trace { + width: 36px; + height: 10px; flex-shrink: 0; } - .sq-online { background: var(--success); } - .sq-offline { background: var(--text-dim); } - .col-action { + .trace line, + .trace circle { + fill: currentColor; + stroke: currentColor; + stroke-width: 1; + } + .peer-actions { display: flex; - gap: 2px; + gap: 6px; justify-content: flex-end; } - .row-btn { - background: transparent; - border: none; - color: var(--text-muted); - cursor: pointer; - font-family: var(--font-mono); - min-width: 32px; - min-height: 32px; - padding: 0; + .badge { + display: inline-block; + min-width: 16px; + padding: 0 5px; + margin-left: 6px; + background: var(--fault); + color: #fff; + font-size: 10px; + line-height: 16px; + text-align: center; + border-radius: var(--radius); } - .row-btn:hover:not(:disabled) { color: var(--text-bright); } - .row-btn:disabled { - opacity: 0.35; - cursor: not-allowed; + .empty { + padding: 20px 12px; + color: var(--muted); + background: color-mix(in srgb, var(--bg) 70%, transparent); } - .empty-peers { - padding: 48px 24px; - text-align: center; - color: var(--text-muted); + .empty-label { color: var(--text); margin-bottom: 4px; font-weight: 500; } + .empty-desc { + font-family: var(--font-serif); + font-style: italic; + font-size: 13px; + margin: 0; + } + .btn-sm { + min-height: 28px; + padding: 4px 10px; + font-size: 12px; } - .empty-label { - color: var(--text-secondary); - margin: 8px 0 4px; + @media (max-width: 720px) { + .channel-head, .peer-row { grid-template-columns: 28px minmax(0, 1fr) auto; } + .channel-head span:nth-child(3), + .channel-head span:nth-child(4), + .channel-head span:nth-child(5), + .peer-ip, .col-path, .col-ping { display: none; } } - .empty-desc { font-size: var(--font-size-xs); } diff --git a/client/frontend/src/lib/components/SettingsView.svelte b/client/frontend/src/lib/components/SettingsView.svelte index 16909ea..393cbf0 100644 --- a/client/frontend/src/lib/components/SettingsView.svelte +++ b/client/frontend/src/lib/components/SettingsView.svelte @@ -2,6 +2,8 @@ import { onMount } from 'svelte' import { settings, addNotification } from '../stores/app.js' import { t, setLang, currentLang, fmt } from '../locales/index.js' + import ThemeToggle from './ThemeToggle.svelte' + import ConfirmDialog from './ConfirmDialog.svelte' let cfg = { ...$settings } let version = '1.0.0' @@ -10,6 +12,7 @@ let updateStatus = '' // '', 'checking', 'latest', 'available', 'downloading', 'error' let updateLatest = '' let updateError = '' + let pending = null async function checkForUpdate() { updateBusy = true @@ -34,18 +37,23 @@ } async function applyUpdate() { - if (!confirm($t.confirm_update)) return - updateBusy = true - updateStatus = 'downloading' - updateError = '' - try { - await window.go.main.App.ApplyUpdate() - addNotification($t.notif_updating, 'info') - } catch (e) { - updateStatus = 'error' - updateError = String(e) - updateBusy = false - addNotification($t.error_update + e, 'error') + pending = { + message: $t.confirm_update, + confirmLabel: $t.apply_update, + run: async () => { + updateBusy = true + updateStatus = 'downloading' + updateError = '' + try { + await window.go.main.App.ApplyUpdate() + addNotification($t.notif_updating, 'info') + } catch (e) { + updateStatus = 'error' + updateError = String(e) + updateBusy = false + addNotification($t.error_update + e, 'error') + } + }, } } @@ -64,15 +72,21 @@ } async function resetDefaults() { - if (!confirm($t.confirm_reset)) return - try { - cfg = await window.go.main.App.ResetSettings() - $settings = { ...cfg } - needsReconnect = false - if (cfg.lang) setLang(cfg.lang) - addNotification($t.notif_settings_saved, 'info') - } catch (e) { - addNotification($t.error_save + e, 'error') + pending = { + message: $t.confirm_reset, + confirmLabel: $t.reset, + danger: true, + run: async () => { + try { + cfg = await window.go.main.App.ResetSettings() + $settings = { ...cfg } + needsReconnect = false + if (cfg.lang) setLang(cfg.lang) + addNotification($t.notif_settings_saved, 'info') + } catch (e) { + addNotification($t.error_save + e, 'error') + } + }, } } @@ -87,17 +101,24 @@ setLang(e.target.value) } - function onThemeChange() { - $settings = { ...$settings, theme: cfg.theme } + $: if ($settings.theme && cfg.theme !== $settings.theme) { + cfg.theme = $settings.theme + } + + let open = { connection: true, network: false, system: false, appearance: true } + + function toggle(key) { + open = { ...open, [key]: !open[key] } } onMount(async () => { try { const saved = await window.go.main.App.GetSettings() if (saved) { - cfg = { ...cfg, ...saved } + const theme = $settings.theme || saved.theme + cfg = { ...cfg, ...saved, theme } if (!cfg.connectionMode) cfg.connectionMode = 'direct' - $settings = cfg + $settings = { ...cfg, theme } if (cfg.lang) setLang(cfg.lang) } if (window.go.main.App.GetVersion) { @@ -108,272 +129,328 @@
-

{$t.settings_title}

+
+

{$t.settings_title}

+ v{version} +
{#if needsReconnect}

{$t.notif_reconnect_required}

{/if} -
-
─── {$t.section_connection} ───
- -
- - -
-
- - -
- -
- - +
+
+
-

{$t.reconnect_apply}

- - - - -
- -
-
─── {$t.section_network} ───
-

{$t.advanced_warn}

- -
- - +
+
+
+
+ + +
+
+ + +
+
+ + +
+

{$t.reconnect_apply}

+ + +
+
-

{$t.connection_mode_desc}

+ -
- - +
+
+
-

{$t.mtu_desc}

- -
- - +
+
+
+

{$t.advanced_warn}

+
+ + +
+

{$t.connection_mode_desc}

+
+ + +
+

{$t.mtu_desc}

+
+ + +
+

{$t.dns_desc}

+
+ + +
+

{$t.socks5_desc}

+
+ + +
+

{$t.stun_desc}

+
+
-

{$t.dns_desc}

+
-
- - +
+
+
-

{$t.socks5_desc}

- -
- - +
+
+
+ +
+ {$t.updates} + + {#if updateStatus === 'available'} + + {/if} +
+ {#if updateStatus === 'checking'} +

{$t.update_checking}

+ {:else if updateStatus === 'latest'} +

{$t.update_latest}

+ {:else if updateStatus === 'available'} +

{fmt($t.update_available, { v: updateLatest })}

+ {:else if updateStatus === 'downloading'} +

{$t.update_downloading}

+ {:else if updateStatus === 'error' && updateError} +

{$t.update_failed}: {updateError}

+ {/if} +
+
-

{$t.stun_desc}

-
- -
-
─── {$t.section_system} ───
- - +
-
- {$t.updates} - - {#if updateStatus === 'available'} - - {/if}
- {#if updateStatus === 'checking'} -

{$t.update_checking}

- {:else if updateStatus === 'latest'} -

{$t.update_latest}

- {:else if updateStatus === 'available'} -

{fmt($t.update_available, { v: updateLatest })}

- {:else if updateStatus === 'downloading'} -

{$t.update_downloading}

- {:else if updateStatus === 'error' && updateError} -

{$t.update_failed}: {updateError}

- {/if} -
- -
-
─── {$t.section_appearance} ───
- -
- - - {cfg.uiScale ?? 100}% -
-

{$t.ui_scale_desc}

- -
- {$t.theme} -
- - +
+
+
+
+ +
+ + {cfg.uiScale ?? 100}% +
+
+

{$t.ui_scale_desc}

+
+ {$t.theme} + +
+
+ + +
+
- -
- - -
+
-
- - -
v{version}
+
+ +
-
+ + { + const run = pending?.run + pending = null + if (run) await run() + }} + onCancel={() => (pending = null)} +/> diff --git a/client/frontend/src/lib/components/Sidebar.svelte b/client/frontend/src/lib/components/Sidebar.svelte index 8eec7ea..456a700 100644 --- a/client/frontend/src/lib/components/Sidebar.svelte +++ b/client/frontend/src/lib/components/Sidebar.svelte @@ -1,7 +1,8 @@ - -
-
- - {statusLabel} - {#if anyRelay && $status.connected} - {$t.status_relay_degraded} - {/if} -
-
- {#if $status.server} - {fmt($t.status_server, { s: $status.server })} - {/if} - {#if $status.virtualIP} - {fmt($t.status_ip, { ip: $status.virtualIP })} - {/if} - {#if $status.room} - {fmt($t.status_network, { room: $status.room })} - {/if} - {#if $status.peerCount !== undefined} - {fmt($t.status_peers, { n: $status.peerCount })} - {/if} - {fmt($t.version, { v: version })} -
-
- - diff --git a/client/frontend/src/lib/components/ThemeToggle.svelte b/client/frontend/src/lib/components/ThemeToggle.svelte new file mode 100644 index 0000000..6b767ef --- /dev/null +++ b/client/frontend/src/lib/components/ThemeToggle.svelte @@ -0,0 +1,45 @@ + + + diff --git a/client/frontend/src/lib/components/Topbar.svelte b/client/frontend/src/lib/components/Topbar.svelte new file mode 100644 index 0000000..1fbed70 --- /dev/null +++ b/client/frontend/src/lib/components/Topbar.svelte @@ -0,0 +1,254 @@ + + +
+
+
+ {$t.brand} +
+ + +
+ {$t.col_path} + + + {pathLabel} + + +
+
+ {$t.peers_list} + {$status.peerCount} +
+
+ {$t.path_relay} + {relayCount} +
+
+ + + +
+
+
+ + (askDisconnect = false)} +/> + + diff --git a/client/frontend/src/lib/locales/en.js b/client/frontend/src/lib/locales/en.js index 21406d4..4793802 100644 --- a/client/frontend/src/lib/locales/en.js +++ b/client/frontend/src/lib/locales/en.js @@ -43,6 +43,9 @@ export default { phase_error: "Error", room_chat: "Room Chat", chat: "Chat", + close_chat: "Close", + channels: "Channels", + col_ch: "Ch", col_nickname: "Nickname", col_ip: "Virtual IP", col_ping: "Ping", @@ -64,6 +67,7 @@ export default { me: "me", no_peers: "No peers connected", no_peers_desc: "Create or join a network, then wait for friends", + peers_list: "Peers", network: "Network", chat_with: "Chat with {name}", room_chat_title: "Room Chat — {room}", @@ -82,16 +86,17 @@ export default { status_disconnected: "Disconnected", status_reconnecting: "Reconnecting…", status_relay_degraded: "Some peers on relay", + status_direct: "Direct", status_server: "Server: {s}", status_ip: "IP: {ip}", status_network: "Network: {room}", status_peers: "Peers: {n}", version: "Entangled v{v}", settings_title: "Settings", - section_connection: "CONNECTION", - section_network: "ADVANCED NETWORK", - section_system: "SYSTEM", - section_appearance: "APPEARANCE", + section_connection: "Connection", + section_network: "Advanced network", + section_system: "System", + section_appearance: "Appearance", advanced_warn: "Only change these if you know what they do.", reconnect_apply: "Reconnect to apply", auto_connect: "Auto Connect", @@ -130,8 +135,8 @@ export default { theme_dark: "Dark", theme_light: "Light", language: "Language", - save: "SAVE", - reset: "RESET TO DEFAULTS", + save: "Save", + reset: "Reset to defaults", language_en: "English", language_ru: "Russian", language_zh: "Chinese", diff --git a/client/frontend/src/lib/locales/ru.js b/client/frontend/src/lib/locales/ru.js index c065042..a57083d 100644 --- a/client/frontend/src/lib/locales/ru.js +++ b/client/frontend/src/lib/locales/ru.js @@ -43,6 +43,9 @@ export default { phase_error: "Ошибка", room_chat: "Чат комнаты", chat: "Чат", + close_chat: "Закрыть", + channels: "Каналы", + col_ch: "Кн", col_nickname: "Псевдоним", col_ip: "Виртуальный IP", col_ping: "Пинг", @@ -64,6 +67,7 @@ export default { me: "я", no_peers: "Нет подключённых участников", no_peers_desc: "Создайте или войдите в сеть и дождитесь друзей", + peers_list: "Участники", network: "Сеть", chat_with: "Чат с {name}", room_chat_title: "Чат комнаты — {room}", @@ -82,16 +86,17 @@ export default { status_disconnected: "Отключено", status_reconnecting: "Переподключение…", status_relay_degraded: "Часть пиров через релей", + status_direct: "Прямое", status_server: "Сервер: {s}", status_ip: "IP: {ip}", status_network: "Сеть: {room}", status_peers: "Участники: {n}", version: "Entangled v{v}", settings_title: "Настройки", - section_connection: "ПОДКЛЮЧЕНИЕ", - section_network: "ДОП. СЕТЬ", - section_system: "СИСТЕМА", - section_appearance: "ВНЕШНИЙ ВИД", + section_connection: "Подключение", + section_network: "Доп. сеть", + section_system: "Система", + section_appearance: "Внешний вид", advanced_warn: "Меняйте только если понимаете, что делаете.", reconnect_apply: "Переподключитесь, чтобы применить", auto_connect: "Автоподключение", @@ -130,8 +135,8 @@ export default { theme_dark: "Тёмная", theme_light: "Светлая", language: "Язык", - save: "СОХРАНИТЬ", - reset: "СБРОСИТЬ", + save: "Сохранить", + reset: "Сбросить", language_en: "Английский", language_ru: "Русский", language_zh: "Китайский", diff --git a/client/frontend/src/lib/locales/zh.js b/client/frontend/src/lib/locales/zh.js index fb52d3e..7930513 100644 --- a/client/frontend/src/lib/locales/zh.js +++ b/client/frontend/src/lib/locales/zh.js @@ -43,6 +43,9 @@ export default { phase_error: "错误", room_chat: "房间聊天", chat: "聊天", + close_chat: "关闭", + channels: "通道", + col_ch: "通", col_nickname: "昵称", col_ip: "虚拟 IP", col_ping: "延迟", @@ -64,6 +67,7 @@ export default { me: "我", no_peers: "暂无连接的节点", no_peers_desc: "创建或加入网络,然后等待好友", + peers_list: "节点", network: "网络", chat_with: "与 {name} 聊天", room_chat_title: "房间聊天 — {room}", @@ -82,6 +86,7 @@ export default { status_disconnected: "已断开", status_reconnecting: "重新连接…", status_relay_degraded: "部分节点经中继", + status_direct: "直连", status_server: "服务器: {s}", status_ip: "IP: {ip}", status_network: "网络: {room}", diff --git a/client/frontend/src/lib/sim.js b/client/frontend/src/lib/sim.js new file mode 100644 index 0000000..839431b --- /dev/null +++ b/client/frontend/src/lib/sim.js @@ -0,0 +1,171 @@ +import { status, view, peers, settings, addChatMessage, chatOpen } from './stores/app.js' + +const SIM = import.meta.env.DEV && !window.go +const jumpConnected = SIM && new URLSearchParams(location.search).has('connected') + +const cfg = { + serverAddr: 'sim.local:8080', + nickname: 'Ada', + autoConnect: false, + autoJoinLastRoom: false, + startWithWindows: false, + connectionMode: 'direct', + mtu: 1500, + dnsServer: '', + socks5Proxy: '', + stunServer: 'stun.l.google.com:19302', + uiScale: 100, + theme: 'dark', + lang: 'en', + serverToken: '', +} + +let rooms = [ + { name: 'nightwatch', password: '', server: 'sim.local:8080', locked: false }, + { name: 'lan-party', password: 'secret', server: 'sim.local:8080', locked: true }, +] + +const simPeers = [ + { + id: 'peer-bob', + nickname: 'Bob', + virtualIP: '10.242.0.3', + connected: true, + ping: 18, + path: 'p2p', + }, + { + id: 'peer-cy', + nickname: 'Cy', + virtualIP: '10.242.0.4', + connected: true, + ping: 84, + path: 'relay', + }, +] + +function connectedStatus(room = 'nightwatch') { + return { + connected: true, + reconnecting: false, + server: 'sim.local:8080', + room, + virtualIP: '10.242.0.2', + peerCount: simPeers.filter((p) => p.connected).length, + isOwner: room === 'nightwatch', + phase: 'ready', + } +} + +function enterConnected(room) { + status.set(connectedStatus(room)) + peers.set(simPeers.map((p) => ({ ...p }))) + view.set('network') + chatOpen.set(true) +} + +if (SIM) { + window.go = { + main: { + App: { + LoadConfig: async () => ({ ...cfg }), + GetSettings: async () => ({ ...cfg }), + SaveSettings: async (next) => { + Object.assign(cfg, next) + return false + }, + ResetSettings: async () => { + cfg.theme = 'dark' + cfg.uiScale = 100 + cfg.lang = 'en' + return { ...cfg } + }, + GetVersion: async () => '1.3.1-sim', + GetSavedRooms: async () => rooms.slice(), + CreateRoom: async (name, password) => { + rooms = [...rooms, { name, password: password || '', server: cfg.serverAddr, locked: !!password }] + enterConnected(name) + }, + JoinRoom: async (name) => { + enterConnected(name) + }, + LeaveRoom: async () => { + status.set({ ...connectedStatus(''), room: '', peerCount: 0, isOwner: false, connected: true }) + }, + RemoveSavedRoom: async (name) => { + rooms = rooms.filter((r) => r.name !== name) + }, + DeleteRoom: async (name) => { + rooms = rooms.filter((r) => r.name !== name) + status.set({ ...connectedStatus(''), room: '', peerCount: 0, isOwner: false, connected: true }) + }, + FormatInvite: async (name, password) => `sim.local:8080|${name}|${password || ''}`, + ParseInvite: async (raw) => { + const [server, room, password] = String(raw).split('|') + return { server, room, password } + }, + CopyText: async (text) => { + try { + await navigator.clipboard.writeText(String(text)) + } catch (_) {} + }, + Connect: async (serverAddr, nickname) => { + cfg.serverAddr = serverAddr + cfg.nickname = nickname + const s = connectedStatus('nightwatch') + s.server = serverAddr + peers.set(simPeers.map((p) => ({ ...p }))) + return s + }, + Disconnect: async () => { + status.set({ + connected: false, + reconnecting: false, + server: '', + room: '', + virtualIP: '', + peerCount: 0, + isOwner: false, + phase: 'idle', + }) + peers.set([]) + chatOpen.set(false) + }, + SendChat: async (peerId, message) => { + const peer = simPeers.find((p) => p.id === peerId) + if (peer) { + addChatMessage(peer.id, peer.nickname, `ack: ${message}`, false, 'sent', false, true) + } + }, + BroadcastChat: async (message) => { + const peer = simPeers[0] + if (peer) { + addChatMessage(peer.id, peer.nickname, `heard: ${message}`, false, 'sent', false, false) + } + }, + PingPeer: async (peerId) => { + const i = simPeers.findIndex((p) => p.id === peerId) + if (i >= 0) { + simPeers[i] = { ...simPeers[i], ping: 12 + Math.floor(Math.random() * 40) } + peers.set(simPeers.map((p) => ({ ...p }))) + } + }, + CheckForUpdate: async () => ({ available: false, latest: '1.3.1-sim' }), + ApplyUpdate: async () => {}, + }, + }, + } + window.runtime = { + EventsOn() {}, + EventsOff() {}, + WindowIsFullscreen: async () => false, + WindowFullscreen() {}, + WindowUnfullscreen() {}, + } + + settings.set({ ...cfg }) + if (jumpConnected) { + enterConnected() + addChatMessage('peer-bob', 'Bob', 'Sim is live — tweak away.', false, 'sent') + } +} diff --git a/client/frontend/src/lib/stores/app.js b/client/frontend/src/lib/stores/app.js index c3ab8f6..1f735e7 100644 --- a/client/frontend/src/lib/stores/app.js +++ b/client/frontend/src/lib/stores/app.js @@ -24,6 +24,7 @@ export const status = writable({ export const peers = writable([]) export const view = writable('connect') +export const chatOpen = writable(false) export const notifications = writable([]) export const durableError = writable(null) export const chatThreads = writable({}) @@ -97,9 +98,8 @@ export function addChatMessage(fromID, nickname, message, isSelf = false, delive } }) - const viewing = get(view) const active = activeThreadKey() - if (!isSelf && !(viewing === 'chat' && storeKey === active)) { + if (!isSelf && !(get(chatOpen) && storeKey === active)) { unread.update(u => ({ ...u, [storeKey]: (u[storeKey] || 0) + 1 })) } } diff --git a/client/frontend/src/main.js b/client/frontend/src/main.js index 5bb8247..9b81b96 100644 --- a/client/frontend/src/main.js +++ b/client/frontend/src/main.js @@ -1,3 +1,4 @@ +import './lib/sim.js' import App from './App.svelte' import './app.css'