diff --git a/luci-app-daede/Makefile b/luci-app-daede/Makefile index 7ce7bd64..8d8f3d33 100644 --- a/luci-app-daede/Makefile +++ b/luci-app-daede/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-daede -PKG_VERSION:=1.10 -PKG_RELEASE:=3 +PKG_VERSION:=1.10.3 +PKG_RELEASE:=4 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/config.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/config.js index debdf36d..7f3cca45 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/config.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/config.js @@ -19,6 +19,16 @@ return view.extend({ }, render: function(ctx) { + /* themes signal dark differently — BootstrapDark sets data-darkmode, + Argon just loads a dark stylesheet with no flag. Detect dark from the + page background luminance and set data-darkmode so our dark rules + (keyed on it) fire uniformly across every theme's dark mode. */ + try { + const bg = getComputedStyle(document.body).backgroundColor.match(/\d+/g); + if (bg && (0.299 * bg[0] + 0.587 * bg[1] + 0.114 * bg[2]) < 128) + document.documentElement.setAttribute('data-darkmode', 'true'); + } catch (e) {} + const listenAddr = uci.get('daed', 'config', 'listen_addr') || backend.BACKENDS.daed.defaultListen; const children = [ E('style', {}, styles.CSS), diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js index de5e01bc..0808f830 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js @@ -110,7 +110,9 @@ const GEN = '/usr/share/luci-app-daede/gen-dae-config.sh'; stays visible in the edit dialog and on hover. */ function ellipsisCell(section_id) { const v = this.cfgvalue(section_id) || ''; - const short = v.length > 52 ? v.slice(0, 52) + '…' : v; + /* middle-ellipsis keeps the host head + tail visible (the meaningful parts + of a sub link) while staying short enough for one line on a phone */ + const short = v.length > 30 ? v.slice(0, 18) + '…' + v.slice(-9) : v; return E('span', { 'title': v, 'style': 'font-family:ui-monospace,Menlo,monospace;font-size:12px' }, short); } @@ -224,7 +226,16 @@ function renderDaeForms(ctx) { s.anonymous = true; o = s.option(form.Value, 'name', _('Name')); o.rmempty = false; + o.default = 'proxy'; /* real default value, editable — a bright placeholder looked filled and tripped the required check */ o.placeholder = 'proxy'; + o.validate = function(sid, v) { + if (!v) return _('Name is required'); + /* dae fatals on duplicate outbound names — keep group names unique */ + const dup = (uci.sections('dae', 'group') || []).some(function(g) { + return g['.name'] !== sid && g.name === v; + }); + return dup ? _('Group name must be unique') : true; + }; o = s.option(form.ListValue, 'policy', _('Policy')); o.value('min_moving_avg', _('Min moving average latency')); o.value('min', _('Min last latency')); @@ -256,6 +267,7 @@ function renderDaeForms(ctx) { o.default = '0'; o = s.option(form.Value, 'fallback', _('Fallback group'), _('Default outbound for everything else.')); + o.default = 'proxy'; /* matches the default proxy group so the box works out of the box */ if (groupNames.length) groupNames.forEach(function(n) { o.value(n, n); }); else @@ -476,7 +488,10 @@ function renderDaeImportBanner() { if (hasForms) return Promise.resolve(null); - return fs.read_direct(backend.BACKENDS.dae.config, 'text').then(function(content) { + /* stat first (ubus): avoid a 404 when config.dae doesn't exist yet */ + return fs.stat(backend.BACKENDS.dae.config).then(function() { + return fs.read_direct(backend.BACKENDS.dae.config, 'text'); + }).then(function(content) { if (!content || !/\b(subscription|node)\s*\{/.test(content)) return null; @@ -650,7 +665,11 @@ function renderDaeEditor() { textarea.addEventListener('input', refreshPlaceholders); function loadConfig() { - return fs.read_direct(backend.BACKENDS.dae.config, 'text').then(function(content) { + /* stat first (ubus, no HTTP): a fresh install has no config.dae yet, and + reading a missing file via fs.read_direct logs a noisy 404 */ + return fs.stat(backend.BACKENDS.dae.config).then(function() { + return fs.read_direct(backend.BACKENDS.dae.config, 'text'); + }).then(function(content) { textarea.value = content || ''; }).catch(function() { textarea.value = ''; diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js index cd1d00c0..304b8104 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js @@ -69,9 +69,11 @@ const CSS = [ as an off-colour band on the white card — keep it transparent */ '.dd-settings-card .cbi-section-table-titles,.dd-settings-card .cbi-section-table-titles .cbi-section-table-cell{background:transparent !important;background-image:none !important}', '.dd-settings-card .cbi-section-table-cell:nth-child(1){width:14% !important}', - '.dd-settings-card .cbi-section-table-cell:nth-child(2){width:50% !important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}', - '.dd-settings-card .cbi-section-table-cell:nth-child(3){width:12% !important}', - '.dd-settings-card .cbi-section-table-cell:nth-child(4){width:24% !important;min-width:210px}', + '.dd-settings-card .cbi-section-table-cell:nth-child(2){width:62% !important;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}', + '.dd-settings-card .cbi-section-table-cell:nth-child(3){width:10% !important}', + '.dd-settings-card .cbi-section-table-cell:nth-child(4){width:auto !important;min-width:0 !important;white-space:nowrap;text-align:right}', + /* icon row-action buttons stay narrow, never stretch to fill the cell */ + '.dd-settings-card .cbi-section-actions .cbi-button-edit,.dd-settings-card .cbi-section-actions .cbi-button-remove{width:auto !important;min-width:0 !important;display:inline-block !important;flex:0 0 auto !important}', /* proxy-test result coloring */ '.dd-meta.dd-ok{color:#3da66a;font-weight:600}', '.dd-meta.dd-err{color:#d96d6d;font-weight:600}', @@ -89,9 +91,9 @@ const CSS = [ real editor (caret, selection, insert-at-cursor, jump-to-line all native) */ '.dd-edit-wrap{position:relative}', '.dd-edit-wrap .dd-editor,.dd-edit-wrap .dd-hl{margin:0;padding:10px 12px;border-width:1px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono",monospace;font-size:12px;line-height:1.5;letter-spacing:0;tab-size:4;white-space:pre-wrap;word-break:break-word;box-sizing:border-box}', - '.dd-edit-wrap .dd-hl{position:absolute;inset:0;margin:0;overflow:hidden;border:1px solid transparent;border-radius:6px 6px 0 0;pointer-events:none;background:transparent;color:#3b4252;z-index:1}', + '.dd-edit-wrap .dd-hl{position:absolute;inset:0;margin:0;overflow:hidden;border:1px solid transparent;border-radius:6px 6px 0 0;pointer-events:none;background:#f6f8fa;color:#3b4252;z-index:1}', '.dd-edit-wrap .dd-hl code{font:inherit;white-space:inherit;word-break:inherit;display:block}', - '.dd-edit-wrap .dd-editor-hl{position:relative;z-index:2;color:transparent;background:transparent;caret-color:#2f7288;border:1px solid rgba(128,128,128,.28)}', + '.dd-edit-wrap .dd-editor-hl{position:relative;z-index:2;color:transparent !important;background:transparent !important;caret-color:#2f7288;border:1px solid rgba(128,128,128,.28)}', '.dd-edit-wrap .dd-editor-hl::placeholder{color:rgba(128,128,128,.55)}', '.dd-edit-wrap .dd-editor-hl::selection{background:rgba(56,134,161,.25)}', '.dh-c{color:#8a919a;font-style:italic}', @@ -100,7 +102,7 @@ const CSS = [ '.dh-f{color:#2f7288}', '.dh-o{color:#c2772a}', '.dh-g{color:#b0226a}', - 'html[data-darkmode="true"] .dd-edit-wrap .dd-hl,body.dark .dd-edit-wrap .dd-hl,html[data-bs-theme="dark"] .dd-edit-wrap .dd-hl{color:#c8cdd6}', + 'html[data-darkmode="true"] .dd-edit-wrap .dd-hl,body.dark .dd-edit-wrap .dd-hl,html[data-bs-theme="dark"] .dd-edit-wrap .dd-hl{color:#c8cdd6;background:#1e2228}', 'html[data-darkmode="true"] .dd-edit-wrap .dd-editor-hl,body.dark .dd-edit-wrap .dd-editor-hl,html[data-bs-theme="dark"] .dd-edit-wrap .dd-editor-hl{caret-color:#7fd0e8}', 'html[data-darkmode="true"] .dh-c,body.dark .dh-c{color:#6b7280}', 'html[data-darkmode="true"] .dh-s,body.dark .dh-s{color:#7ec699}', @@ -150,15 +152,32 @@ const CSS = [ 'body.dark .dd-card,html[data-theme="dark"] .dd-card,html[data-bs-theme="dark"] .dd-card,html[data-darkmode="true"] .dd-card{border-color:rgba(255,255,255,.08);background:rgba(255,255,255,.02)}', 'body.dark .dd-adv-bar,html[data-theme="dark"] .dd-adv-bar,html[data-bs-theme="dark"] .dd-adv-bar,html[data-darkmode="true"] .dd-adv-bar{background:rgba(255,255,255,.04);border-color:rgba(255,255,255,.10)}', 'body.dark .dd-settings-card .cbi-value-field input,body.dark .dd-settings-card .cbi-value-field select,body.dark .dd-settings-card .cbi-value-field textarea,html[data-theme="dark"] .dd-settings-card .cbi-value-field input,html[data-theme="dark"] .dd-settings-card .cbi-value-field select,html[data-theme="dark"] .dd-settings-card .cbi-value-field textarea,html[data-bs-theme="dark"] .dd-settings-card .cbi-value-field input,html[data-bs-theme="dark"] .dd-settings-card .cbi-value-field select,html[data-bs-theme="dark"] .dd-settings-card .cbi-value-field textarea,html[data-darkmode="true"] .dd-settings-card .cbi-value-field input,html[data-darkmode="true"] .dd-settings-card .cbi-value-field select,html[data-darkmode="true"] .dd-settings-card .cbi-value-field textarea{border-color:rgba(255,255,255,.18)}', - /* phone: render each subscription/node row as a self-contained card instead - of a cramped table (LuCI already block-stacks the cells; we add the card - frame, drop the desktop column widths, and shrink the action buttons) */ + /* status card line-2 meta (backend · pid), below the badge+toggle line */ + '.dd-status-meta{display:flex;flex-wrap:wrap;gap:10px;margin-top:6px}', + /* compact icon-only row actions — the 编辑/删除 text buttons are too wide + (esp. Bootstrap); show a pencil / trash glyph instead, both themes */ + '.dd-settings-card .cbi-section-actions .cbi-button-edit,.dd-settings-card .cbi-section-actions .cbi-button-remove{font-size:0 !important;padding:4px 7px !important;min-width:0 !important}', + '.dd-settings-card .cbi-section-actions .cbi-button-edit::before{content:"\\270E";font-size:13px;line-height:1}', + '.dd-settings-card .cbi-section-actions .cbi-button-remove::before{content:"\\1F5D1\\FE0E";font-size:13px;line-height:1}', + /* phone: keep each subscription/node row on ONE line; the URL cell + middle-ellipsizes (via ellipsisCell) and flex-shrinks to fit the screen */ '@media (max-width:600px){', - '.dd-settings-card .cbi-section-table-cell:nth-child(1),.dd-settings-card .cbi-section-table-cell:nth-child(2),.dd-settings-card .cbi-section-table-cell:nth-child(3),.dd-settings-card .cbi-section-table-cell:nth-child(4){width:auto !important;min-width:0 !important;white-space:normal !important;overflow:visible !important;text-overflow:clip !important}', - '.dd-settings-card .cbi-section-table-row{display:block;border:1px solid rgba(128,128,128,.2);border-radius:8px;padding:6px 10px;margin:0 0 8px;background:rgba(128,128,128,.03)}', - '.dd-settings-card .cbi-section-table-cell{display:block;padding:3px 0 !important}', - '.dd-settings-card .cbi-section-actions{display:flex;flex-wrap:wrap;gap:6px;padding-top:6px !important}', - '.dd-settings-card .cbi-section-actions .cbi-button{width:auto;margin:0;flex:0 0 auto}', + '.dd-settings-card .cbi-section-table-titles{display:none !important}', + /* drop table layout so flex rows are constrained to the card width and the + URL cell can actually shrink (a flex