Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires up the last batch of inbound row + general actions that were
toasting "coming soon": export-inbound-links, export-subs (per-inbound
and global), export-all-links, import-inbound, and the clipboard JSON
peek. Two small shared components back them — both can be reused by
the xray page later.
- TextModal.vue (shared): read-only multi-line viewer with a copy
button and an optional download button when fileName is set.
Replaces the legacy txtModal which the inbounds page used for every
link export.
- PromptModal.vue (shared): generic title + input/textarea + confirm
callback, with the legacy keybindings (Enter submits in single-line
mode; Ctrl+S submits in textarea mode). Used here for import-inbound
but also by xray-config edits in Phase 6.
- InboundsPage.vue: drops the toast stubs for `import`/`export`/`subs`
on the general-actions menu and `export`/`subs`/`clipboard` on the
per-row menu, routing each through openText / openPrompt + the
appropriate model helper (genInboundLinks, etc.). The copyClients
cross-inbound modal stays toast-stubbed — that's its own dedicated
legacy modal worth its own commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rewrites InboundFormModal to look like the legacy panel: structured
forms for the common case, with a compact "Advanced (JSON)" fallback
for the rare bits we don't yet have UI for.
Tabs:
• Basics — enable/remark/protocol/listen/port/total/trafficReset/expiry
• Protocol — protocol-aware:
VMess/VLess/Trojan/SS-multi/Hysteria in add mode embed an inline
first-client form (email + ID/password/auth, security, flow,
subId, comment, total GB, expiry);
edit mode shows a clients-count summary table;
VLess: decryption/encryption inputs;
SS: method dropdown that re-randomizes password and propagates
method change to the multi-user array (matches legacy
SSMethodChange);
HTTP/Mixed: accounts table with add/remove rows + Mixed
auth/udp/ip toggles;
Tunnel: address/port/network/followRedirect;
WireGuard: secretKey/pubKey (regen via Wireguard.generateKeypair)
+ per-peer fields with PSK regen + allowedIPs add/remove +
keepAlive.
• Stream — only when canEnableStream(); transport selector with
structured forms for TCP (proxy-protocol, http camouflage),
WS (host/path/heartbeat/headers), gRPC (serviceName, multiMode),
HTTPUpgrade (host/path). KCP/XHTTP fall back to the Advanced tab
with an alert banner. Security selector with TLS (sni/alpn/
fingerprint) and Reality (target/serverNames/keypair-gen via
/panel/api/server/getNewX25519Cert / shortIds / fingerprint).
• Sniffing — enabled/destOverride/metadataOnly/routeOnly/
ipsExcluded/domainsExcluded as structured fields.
• Advanced (JSON) — raw streamSettings + sniffing JSON for users
reaching KCP/XHTTP/sockopt/finalmask/full TLS cert arrays. The
stream JSON is auto-synced from the live model whenever the
structured fields change.
State source of truth is a deeply-reactive Inbound + DBInbound pair
cloned on open; submit serializes via inbound.settings.toString() +
inbound.stream.toString() so the wire shape matches the legacy panel
byte-for-byte. streamNetworkChange semantics (clear flow when
TLS/Reality unavailable, reset finalmask.udp when not KCP) are
preserved.
Vision Seed for VLess + finer-grained TCP HTTP camouflage + the full
TLS cert/ECH editor will land in 5f-iii-c.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Each multi-user inbound row in the list now expands to show its
client roster, mirroring the legacy aClientTable component.
- ClientRowTable.vue: inner a-table with full desktop column set
(action icons / enable / online / client-with-status-dot / traffic
with progress bar / all-time / expiry with reset cycle) and a
collapsed mobile variant (single dropdown menu + popover info).
Self-contained: stats are looked up via a per-inbound email->stats
Map; per-client confirms (reset/delete) live on the row.
- The component emits typed events (edit/qrcode/info/reset-traffic/
delete/toggle-enable) — InboundsPage routes them back to the
existing client and info modals (with `findClientIndex` so the
modal opens focused on the right client).
- InboundList.vue: hooks ClientRowTable into the a-table's
expandedRowRender slot; row-class-name `hide-expand-icon` and a
scoped CSS rule hide the chevron for non-multi-user inbounds
(HTTP/Mixed/Tunnel/WireGuard/SS-single) so they keep looking flat.
- toggle-enable-client routes through updateClient with the same
`{id, settings: '{"clients": [...]}'}` shape as the other modals,
so backend parsing stays single-pathed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires the row "info" and "qrcode" actions and ports the legacy
inbound_info_modal end-to-end. The info modal handles every protocol
the legacy panel did:
• multi-user (VMess/VLess/Trojan/SS-multi/Hysteria) — per-client
table + share links + per-link QR;
• SS single-user — share link + QR;
• WireGuard — full peer table with downloadable peer-N.conf and a
wg:// share link per peer;
• Mixed/HTTP/Tunnel — connection-detail tables.
- QrPanel.vue: shared link card (header tag, copy button, optional
download button, optional QR canvas, monospace footer with the
raw value). Per-instance QRious instances are repainted on
value/size change.
- InboundInfoModal.vue: full info modal. Subscription URL block keys
off subSettings.subURI/subJsonURI; IP-log lazy-loads on open and
surfaces refresh + clear; tg-id, last-online, depleted/enabled tags
all match legacy.
- QrCodeModal.vue: lighter modal used for the row "qrcode" action on
SS-single and WireGuard inbounds (just the QRs, no info table).
- InboundsPage.vue: wires both flows. checkFallback() reproduces the
legacy logic — when an inbound listens on a unix-socket fallback
(`@<name>`), the link generator is pointed at the root inbound that
owns the listen address so QRs/links carry the public host:port +
the right TLS state. Multi-client navigation (focusing a specific
client's links) is deferred to 5f-vi where the per-inbound expand-
row table will pass the email through.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires per-inbound client management. Both flows go through the same
addClient/updateClient endpoints as legacy; the modals just funnel
the form state into the right shape (`{id, settings: '{"clients": [...]}'}`).
- ClientFormModal.vue: protocol-aware single-client editor — email/
password/id/auth/security/flow/subId/tgId/comment/ipLimit/totalGB/
expiry/renewal fields are shown/hidden per protocol like legacy.
Edit mode displays the per-client traffic stats with a reset
button; IP-limit log is read on click and clearable. Random
helpers (sync icon next to each label) regenerate UUID/email/
password/sub-id values.
- ClientBulkModal.vue: 1–500 clients in one POST, with the legacy
five email-generation modes (Random / +Prefix / +Num / +Postfix /
Pure-Prefix-Num-Postfix). Builds clients via the protocol-aware
factory and concatenates their toString() output into a single
settings.clients JSON array.
- InboundsPage.vue: opens both modals from the row action menu
(`addClient` / `addBulkClient`). They both refresh the inbound list
on success.
- Outstanding row actions still toast as "coming soon": qrcode,
showInfo, copyClients, clipboard. Those land in 5f-v / 5f-vi.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires up the inbound CRUD flows. The protocol-specific and transport-
specific forms are still ahead in 5f-iii-b — for now the modal exposes
those as JSON textareas so users can both edit existing inbounds without
losing settings and create new ones from default templates.
- InboundFormModal.vue: tabbed modal with a full Basics tab (enable,
remark, protocol, listen, port, total GB, traffic reset, expiry
date) and three JSON-edit tabs (Settings, Stream, Sniffing). Add
mode stamps a fresh template per protocol via
Inbound.Settings.getSettings(protocol); changing the protocol in
add mode restamps the JSON. Edit mode pretty-prints the existing
JSON so the user sees the same fields they save back.
- POST /panel/api/inbounds/add or /panel/api/inbounds/update/:id on
submit; on success the parent refreshes the list and the modal
closes. Malformed JSON in any of the three textareas surfaces a
message.error and aborts the save without losing user input.
- InboundsPage.vue: wires the row action menu to real handlers —
edit (opens the modal in edit mode), delete, reset-traffic,
clone, reset-clients, del-depleted-clients all go through
Modal.confirm and refresh on success. General actions menu wires
reset-inbounds / reset-clients / del-depleted-clients the same way.
Remaining actions (qrcode/info/import/export/copyClients) still
toast as "coming soon" — those land in 5f-iv and 5f-v.
- Adds dayjs ^1.11.20 dep for the a-date-picker v-model interop.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Vue's template compiler warned that <tr> can't be a direct child of
<table> per the HTML spec; the browser silently inserts a <tbody>
wrapper but Vue's SSR/hydration path doesn't, which can cause
hydration mismatches. Add explicit <tbody> in both popover tables
(traffic + mobile-info).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fleshes out the inbound list with the full column set, search & filter
toolbar, row enable toggle wired to /panel/api/inbounds/setEnable/:id,
and a per-row action dropdown that emits events the parent will route
to modals as those land in 5f-iii through 5f-vii.
- InboundList.vue (new): toolbar (Add inbound + General actions
dropdown + Refresh + auto-refresh popover), search-or-filter switch
with the legacy radio buttons (Active/Disabled/Depleted/Depleting/
Online), and a a-table with desktop and mobile column variants.
Cells use AD-Vue 4's #bodyCell slot — protocol/clients/traffic/
allTime/expiry/info cells render the same popovers and tags as
legacy. Row enable switch is optimistic with rollback on POST
failure.
- visibleInbounds computed mirrors the legacy search and filter
projection: deep search through dbInbound + clients, or filter
reduces inbound.settings.clients to the selected bucket so the
table only shows matching client rows.
- Auto-refresh interval is read/written to localStorage with the
same keys (`isRefreshEnabled`, `refreshInterval`) as the legacy
panel. WebSocket delta updates are still deferred.
- Action menu emits event payloads {key, dbInbound}; the parent
currently shows a "coming in later 5f subphase" toast for each.
Modals (edit/qr/clone/delete/reset/info/clients) land in
5f-iii through 5f-vii.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the inbounds entry as a fourth Vite multi-page input and wires
/panel/inbounds through the dev proxy bypass. Lays down the page
chrome (sidebar, summary statistics card, refresh button) and the
fetch lifecycle composable so 5f-ii onward can drop in the table
columns and the modals without re-implementing it.
- inbounds.html + src/inbounds.js: fourth Vite entry; mounts InboundsPage.
- InboundsPage.vue: sidebar + summary card (totals over up/down,
all-time, inbound count, client tags) + a basic table with enable/
remark/port/protocol/traffic/expiry columns. Row actions, popovers,
search/filter, auto-refresh, and the WebSocket delta path are all
deferred to subsequent 5f subphases.
- useInbounds.js composable: GET /panel/api/inbounds/list +
POST /panel/api/inbounds/onlines + POST /panel/api/inbounds/lastOnline +
POST /panel/setting/defaultSettings, then computes the
per-inbound clientCount roll-ups (active/deactive/depleted/expiring/
online/comments) the table popovers consume.
- models/dbinbound.js + models/inbound.js: switched the legacy-utils
import to the @/utils alias for consistency with the rest of the
app. Semantics unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>