Dvesign improvemments
This commit is contained in:
+3595
-201
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -577,12 +577,12 @@
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 26px;
|
font-size: 24px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
.page-copy,
|
.page-copy,
|
||||||
.card-copy {
|
.card-copy {
|
||||||
@@ -617,11 +617,10 @@
|
|||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
.builder-card {
|
.builder-card {
|
||||||
background: var(--surface);
|
background: #0f1116;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 18px;
|
border-radius: 12px;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.16);
|
|
||||||
}
|
}
|
||||||
.hero-card {
|
.hero-card {
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
@@ -671,9 +670,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@@ -703,16 +702,16 @@
|
|||||||
.target-option,
|
.target-option,
|
||||||
.preview-item {
|
.preview-item {
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
background: var(--bg-secondary);
|
background: #111419;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
}
|
}
|
||||||
.result-card {
|
.result-card {
|
||||||
all: unset;
|
all: unset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
background: var(--bg-secondary);
|
background: #111419;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
transition: background 0.12s ease, border-color 0.12s ease;
|
transition: background 0.12s ease, border-color 0.12s ease;
|
||||||
}
|
}
|
||||||
@@ -762,8 +761,8 @@
|
|||||||
}
|
}
|
||||||
.empty-note {
|
.empty-note {
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
background: var(--bg-secondary);
|
background: #111419;
|
||||||
border: 1px dashed var(--border);
|
border: 1px dashed var(--border);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -782,9 +781,9 @@
|
|||||||
}
|
}
|
||||||
.status {
|
.status {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background: var(--bg-secondary);
|
background: #111419;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
@@ -801,7 +800,7 @@
|
|||||||
all: unset;
|
all: unset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 9px 16px;
|
padding: 9px 16px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@@ -809,13 +808,14 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.btn.ghost {
|
.btn.ghost {
|
||||||
color: #d9e7f8;
|
color: var(--text);
|
||||||
border-color: var(--border);
|
border-color: var(--border);
|
||||||
background: var(--surface);
|
background: #15181e;
|
||||||
}
|
}
|
||||||
.btn.accent {
|
.btn.accent {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
border-color: rgba(42, 215, 239, 0.35);
|
||||||
|
color: #031014;
|
||||||
}
|
}
|
||||||
.btn:disabled {
|
.btn:disabled {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
@@ -827,9 +827,9 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 34px;
|
width: 34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
background: var(--surface-active);
|
background: rgba(40, 193, 220, 0.1);
|
||||||
color: #a9d6ff;
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
|
|||||||
+35
-36
@@ -495,21 +495,18 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.section-card {
|
.section-card {
|
||||||
background: var(--surface);
|
background: #101318;
|
||||||
border-radius: 20px;
|
border-radius: 12px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.18);
|
transition: border-color 0.15s ease, background 0.15s ease;
|
||||||
transition: border-color 0.15s, transform 0.15s ease, box-shadow 0.15s ease;
|
|
||||||
}
|
}
|
||||||
.section-card:hover {
|
.section-card:hover {
|
||||||
transform: translateY(-1px);
|
background: #12161d;
|
||||||
box-shadow: 0 22px 42px rgba(0, 0, 0, 0.22);
|
|
||||||
}
|
}
|
||||||
.section-card.expanded {
|
.section-card.expanded {
|
||||||
border-color: var(--border-strong);
|
border-color: var(--border-strong);
|
||||||
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.26);
|
|
||||||
}
|
}
|
||||||
.section-header {
|
.section-header {
|
||||||
all: unset;
|
all: unset;
|
||||||
@@ -534,9 +531,9 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
opacity: 0.6;
|
opacity: 0.7;
|
||||||
padding: 3px 4px;
|
padding: 3px 4px;
|
||||||
color: var(--text);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
.move-btn:disabled {
|
.move-btn:disabled {
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
@@ -556,12 +553,12 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 34px;
|
width: 30px;
|
||||||
height: 34px;
|
height: 30px;
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
background: var(--bg-secondary);
|
background: rgba(40, 193, 220, 0.1);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
color: #a9d6ff;
|
color: var(--accent);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.section-info {
|
.section-info {
|
||||||
@@ -570,7 +567,7 @@
|
|||||||
}
|
}
|
||||||
.section-name {
|
.section-name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -588,10 +585,10 @@
|
|||||||
all: unset;
|
all: unset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
font-size: 22px;
|
font-size: 20px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
opacity: 0.5;
|
opacity: 0.55;
|
||||||
}
|
}
|
||||||
.remove-btn:hover {
|
.remove-btn:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -604,8 +601,9 @@
|
|||||||
|
|
||||||
/* Editor panel */
|
/* Editor panel */
|
||||||
.section-editor {
|
.section-editor {
|
||||||
padding: 8px 16px 18px;
|
padding: 10px 16px 18px;
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
|
background: #0d1015;
|
||||||
}
|
}
|
||||||
.field-grid {
|
.field-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -629,10 +627,10 @@
|
|||||||
}
|
}
|
||||||
.field-label {
|
.field-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.08em;
|
||||||
}
|
}
|
||||||
.hint-inline {
|
.hint-inline {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -644,9 +642,9 @@
|
|||||||
.field input[type='text'],
|
.field input[type='text'],
|
||||||
.field select {
|
.field select {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@@ -668,10 +666,10 @@
|
|||||||
}
|
}
|
||||||
.lookup-btn {
|
.lookup-btn {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--surface);
|
background: #15181e;
|
||||||
}
|
}
|
||||||
.lookup-btn:disabled {
|
.lookup-btn:disabled {
|
||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
@@ -690,9 +688,9 @@
|
|||||||
.inline-status {
|
.inline-status {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--surface);
|
background: #12151b;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
@@ -714,6 +712,7 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
background: #12151b;
|
||||||
transition: all 0.12s;
|
transition: all 0.12s;
|
||||||
}
|
}
|
||||||
.chip:hover {
|
.chip:hover {
|
||||||
@@ -722,8 +721,8 @@
|
|||||||
}
|
}
|
||||||
.chip.active {
|
.chip.active {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
border-color: transparent;
|
border-color: rgba(42, 215, 239, 0.3);
|
||||||
color: #fff;
|
color: #031014;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-inline {
|
.field-inline {
|
||||||
@@ -740,9 +739,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
@@ -762,7 +761,7 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 3px;
|
gap: 3px;
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 4px 8px 4px 10px;
|
padding: 4px 8px 4px 10px;
|
||||||
@@ -784,7 +783,7 @@
|
|||||||
.badge {
|
.badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
color: #031014;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -803,8 +802,8 @@
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
.text-muted {
|
.text-muted {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
@@ -821,8 +820,8 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
background: var(--bg-secondary);
|
background: #12151b;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.lookup-result:hover {
|
.lookup-result:hover {
|
||||||
|
|||||||
@@ -215,19 +215,19 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.panel {
|
.panel {
|
||||||
padding: 0 4px;
|
padding: 0;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
margin: 0 0 18px;
|
margin: 0 0 16px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.08em;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
}
|
}
|
||||||
@@ -235,9 +235,8 @@
|
|||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 18px;
|
border-radius: 12px;
|
||||||
background: var(--surface);
|
background: #111419;
|
||||||
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.16);
|
|
||||||
}
|
}
|
||||||
section:last-of-type {
|
section:last-of-type {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
@@ -254,9 +253,9 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
input {
|
input {
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
@@ -276,7 +275,7 @@
|
|||||||
}
|
}
|
||||||
code {
|
code {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
background: #0b0f14;
|
background: #0c0e12;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
@@ -289,24 +288,25 @@
|
|||||||
all: unset;
|
all: unset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 9px 14px;
|
padding: 9px 14px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
transition: all 0.12s;
|
transition: all 0.12s;
|
||||||
}
|
}
|
||||||
.btn.ghost {
|
.btn.ghost {
|
||||||
color: #d9e7f8;
|
color: var(--text);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--bg-secondary);
|
background: #15181e;
|
||||||
}
|
}
|
||||||
.btn.ghost:hover:not(:disabled) {
|
.btn.ghost:hover:not(:disabled) {
|
||||||
background: var(--surface-hover);
|
background: var(--surface-hover);
|
||||||
color: var(--text);
|
border-color: var(--border-strong);
|
||||||
}
|
}
|
||||||
.btn.accent {
|
.btn.accent {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
color: #031014;
|
||||||
|
border: 1px solid rgba(42, 215, 239, 0.35);
|
||||||
}
|
}
|
||||||
.btn.accent:hover:not(:disabled) {
|
.btn.accent:hover:not(:disabled) {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
@@ -318,18 +318,18 @@
|
|||||||
.status {
|
.status {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 14px;
|
border-radius: 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
background: var(--surface);
|
background: #111419;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
border-left: 3px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.status.ok {
|
.status.ok {
|
||||||
border-left-color: #22c55e;
|
border-color: rgba(34, 197, 94, 0.3);
|
||||||
color: #86efac;
|
color: #86efac;
|
||||||
}
|
}
|
||||||
.status.error {
|
.status.error {
|
||||||
border-left-color: var(--danger);
|
border-color: rgba(239, 68, 68, 0.3);
|
||||||
color: #fca5a5;
|
color: #fca5a5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+12
-9
@@ -54,7 +54,7 @@
|
|||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(0, 0, 0, 0.72);
|
background: rgba(0, 0, 0, 0.82);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -62,14 +62,15 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.modal {
|
.modal {
|
||||||
background: var(--surface-strong);
|
background: #0f1116;
|
||||||
|
border: 1px solid var(--border);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 720px;
|
max-width: 720px;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.42);
|
||||||
}
|
}
|
||||||
.modal-header {
|
.modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -103,9 +104,9 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: var(--surface);
|
background: #12151b;
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.modal-actions {
|
.modal-actions {
|
||||||
@@ -119,7 +120,7 @@
|
|||||||
all: unset;
|
all: unset;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 8px 18px;
|
padding: 8px 18px;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@@ -127,15 +128,17 @@
|
|||||||
.action-btn.secondary {
|
.action-btn.secondary {
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
|
background: #15181e;
|
||||||
}
|
}
|
||||||
.action-btn.secondary:hover {
|
.action-btn.secondary:hover {
|
||||||
background: var(--surface);
|
background: var(--surface-hover);
|
||||||
}
|
}
|
||||||
.action-btn.primary {
|
.action-btn.primary {
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
border: 1px solid rgba(42, 215, 239, 0.35);
|
||||||
|
color: #031014;
|
||||||
}
|
}
|
||||||
.action-btn.primary:hover {
|
.action-btn.primary:hover {
|
||||||
opacity: 0.9;
|
background: #35d2ea;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+19
-16
@@ -158,8 +158,8 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
margin: 0 0 18px;
|
margin: 0 0 18px;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
@@ -168,10 +168,10 @@
|
|||||||
}
|
}
|
||||||
.step-label {
|
.step-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 700;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.08em;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
@@ -184,9 +184,9 @@
|
|||||||
.select-input {
|
.select-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: #0b0f14;
|
background: #12151b;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@@ -202,9 +202,9 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 16px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--surface);
|
background: #111419;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.12s;
|
transition: all 0.12s;
|
||||||
}
|
}
|
||||||
@@ -237,7 +237,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
|
border: 1px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
@@ -246,6 +247,7 @@
|
|||||||
}
|
}
|
||||||
.section-pick.selected {
|
.section-pick.selected {
|
||||||
background: var(--surface-active);
|
background: var(--surface-active);
|
||||||
|
border: 1px solid var(--border-strong);
|
||||||
}
|
}
|
||||||
.pick-name {
|
.pick-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -266,7 +268,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: 12px;
|
border-radius: 10px;
|
||||||
|
border: 1px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
@@ -275,6 +278,7 @@
|
|||||||
}
|
}
|
||||||
.target-option.selected {
|
.target-option.selected {
|
||||||
background: var(--surface-active);
|
background: var(--surface-active);
|
||||||
|
border: 1px solid var(--border-strong);
|
||||||
}
|
}
|
||||||
.target-name {
|
.target-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -302,19 +306,18 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 16px;
|
border-radius: 10px;
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: #fff;
|
border: 1px solid rgba(42, 215, 239, 0.35);
|
||||||
|
color: #031014;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2);
|
transition: opacity 0.12s, background 0.12s ease;
|
||||||
transition: opacity 0.12s, transform 0.12s ease;
|
|
||||||
}
|
}
|
||||||
.sync-btn:hover:not(:disabled) {
|
.sync-btn:hover:not(:disabled) {
|
||||||
opacity: 0.95;
|
background: #35d2ea;
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
}
|
||||||
.sync-btn:disabled {
|
.sync-btn:disabled {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
|||||||
+83
-2
@@ -90,7 +90,7 @@ export function createEmptySection(userId) {
|
|||||||
SectionType: 'items',
|
SectionType: 'items',
|
||||||
ImageType: 'Thumb',
|
ImageType: 'Thumb',
|
||||||
CollectionType: 'movies',
|
CollectionType: 'movies',
|
||||||
SortBy: 'DateLastContentAdded,SortName',
|
SortBy: 'Random',
|
||||||
SortOrder: 'Descending',
|
SortOrder: 'Descending',
|
||||||
Monitor: [],
|
Monitor: [],
|
||||||
ItemTypes: ['Movie'],
|
ItemTypes: ['Movie'],
|
||||||
@@ -142,7 +142,7 @@ export function createBoxSetSection(userId, collectionName, collectionId) {
|
|||||||
SectionType: 'boxset',
|
SectionType: 'boxset',
|
||||||
ImageType: 'Thumb',
|
ImageType: 'Thumb',
|
||||||
ItemTypes: [],
|
ItemTypes: [],
|
||||||
SortBy: 'default',
|
SortBy: 'Random',
|
||||||
SortOrder: 'Descending',
|
SortOrder: 'Descending',
|
||||||
Monitor: [],
|
Monitor: [],
|
||||||
ExcludedFolders: [],
|
ExcludedFolders: [],
|
||||||
@@ -186,6 +186,40 @@ export function isWatchlistSection(section) {
|
|||||||
return !!section.Query?.IsFavorite || name.includes('watchlist') || name.includes('watch list');
|
return !!section.Query?.IsFavorite || name.includes('watchlist') || name.includes('watch list');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isUpNextSection(section) {
|
||||||
|
if (!section) return false;
|
||||||
|
if (section.SectionType === 'resume') return true;
|
||||||
|
|
||||||
|
const name = `${section.CustomName || ''} ${section.Name || ''}`.trim().toLowerCase();
|
||||||
|
return name === 'up next' || name === 'next up' || name === 'resume / up next';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNewToEmbySection(section) {
|
||||||
|
if (!section) return false;
|
||||||
|
const name = `${section.CustomName || ''} ${section.Name || ''}`.toLowerCase();
|
||||||
|
return name.includes('new to emby');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRecentlyWatchedSection(section) {
|
||||||
|
if (!section) return false;
|
||||||
|
const name = `${section.CustomName || ''} ${section.Name || ''}`.toLowerCase();
|
||||||
|
return section.Query?.IsPlayed === true || name.includes('recently watched');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFixedOrderSection(section) {
|
||||||
|
if (!section) return false;
|
||||||
|
return (
|
||||||
|
isUpNextSection(section) ||
|
||||||
|
isWatchlistSection(section) ||
|
||||||
|
isNewToEmbySection(section) ||
|
||||||
|
isRecentlyWatchedSection(section) ||
|
||||||
|
section.SectionType === 'latestepisodereleases' ||
|
||||||
|
section.SectionType === 'latestmoviereleases' ||
|
||||||
|
section.SectionType === 'latestmediablock' ||
|
||||||
|
section.SectionType === 'userviews'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renameWatchlistLabel(label, targetName) {
|
function renameWatchlistLabel(label, targetName) {
|
||||||
if (!label || !targetName) return label;
|
if (!label || !targetName) return label;
|
||||||
if (/^\s*watch\s+list\s*$/i.test(label)) return 'Watch List';
|
if (/^\s*watch\s+list\s*$/i.test(label)) return 'Watch List';
|
||||||
@@ -259,6 +293,53 @@ export function getWatchlistLabelsForTarget(sourceSection, targetUser) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applySectionStandards(sourceSection, targetUser) {
|
||||||
|
const section = JSON.parse(JSON.stringify(sourceSection || {}));
|
||||||
|
|
||||||
|
if (isUpNextSection(section)) {
|
||||||
|
section.Name = 'Up Next';
|
||||||
|
section.CustomName = 'Up Next';
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isWatchlistSection(section)) {
|
||||||
|
const labels = getWatchlistLabelsForTarget(section, targetUser);
|
||||||
|
if (labels.Name) section.Name = labels.Name;
|
||||||
|
if (labels.CustomName) section.CustomName = labels.CustomName;
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewToEmbySection(section)) {
|
||||||
|
section.Name = 'New to Emby';
|
||||||
|
section.CustomName = 'New to Emby';
|
||||||
|
section.SortBy = 'DateLastContentAdded,SortName';
|
||||||
|
section.SortOrder = 'Descending';
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRecentlyWatchedSection(section)) {
|
||||||
|
const targetName = getPreferredUserName(targetUser);
|
||||||
|
const label = `Recently Watched${targetName ? ` - ${targetName}` : ''}`;
|
||||||
|
section.Name = label;
|
||||||
|
section.CustomName = label;
|
||||||
|
section.SortBy = 'DatePlayed';
|
||||||
|
section.SortOrder = 'Descending';
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFixedOrderSection(section) && ['items', 'collections', 'boxset'].includes(section.SectionType)) {
|
||||||
|
section.SortBy = 'Random';
|
||||||
|
section.SortOrder = 'Descending';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section.SectionType === 'userviews') {
|
||||||
|
section.Name = 'Libraries';
|
||||||
|
section.CustomName = 'Libraries';
|
||||||
|
}
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build SQL UPDATE statements from modified user data.
|
* Build SQL UPDATE statements from modified user data.
|
||||||
* Each user's entire homescreensettings JSON is replaced.
|
* Each user's entire homescreensettings JSON is replaced.
|
||||||
|
|||||||
+966
-647
File diff suppressed because it is too large
Load Diff
+104
-1
@@ -2,7 +2,12 @@ import assert from 'node:assert/strict';
|
|||||||
import { mkdtempSync, rmSync } from 'fs';
|
import { mkdtempSync, rmSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
import { createBoxSetSection, createRecentlyWatchedSection, getWatchlistLabelsForTarget } from '../src/lib/constants.js';
|
import {
|
||||||
|
applySectionStandards,
|
||||||
|
createBoxSetSection,
|
||||||
|
createRecentlyWatchedSection,
|
||||||
|
getWatchlistLabelsForTarget
|
||||||
|
} from '../src/lib/constants.js';
|
||||||
import { normalizeLookupItem, rankRecommendationResults } from '../src/lib/collection-tools.js';
|
import { normalizeLookupItem, rankRecommendationResults } from '../src/lib/collection-tools.js';
|
||||||
import * as embyUserCache from '../src/lib/server/emby-user-cache.js';
|
import * as embyUserCache from '../src/lib/server/emby-user-cache.js';
|
||||||
|
|
||||||
@@ -152,6 +157,104 @@ test('box set section template links a created collection', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('section standards normalize up next label', () => {
|
||||||
|
const normalized = applySectionStandards(
|
||||||
|
{
|
||||||
|
Name: 'Resume / Up Next',
|
||||||
|
CustomName: 'Resume / Up Next',
|
||||||
|
SectionType: 'resume'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
embyName: 'Bob',
|
||||||
|
sections: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(normalized.Name, 'Up Next');
|
||||||
|
assert.equal(normalized.CustomName, 'Up Next');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section standards normalize watchlist label for target user', () => {
|
||||||
|
const normalized = applySectionStandards(
|
||||||
|
{
|
||||||
|
Name: "Matt's Watchlist",
|
||||||
|
CustomName: "Matt's Watchlist",
|
||||||
|
SectionType: 'items',
|
||||||
|
Query: { IsFavorite: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
embyName: 'Bob',
|
||||||
|
sections: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(normalized.Name, "Bob's Watchlist");
|
||||||
|
assert.equal(normalized.CustomName, "Bob's Watchlist");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section standards keep new to emby fixed to date added descending', () => {
|
||||||
|
const normalized = applySectionStandards(
|
||||||
|
{
|
||||||
|
Name: 'New To Emby',
|
||||||
|
CustomName: 'New To Emby',
|
||||||
|
SectionType: 'items',
|
||||||
|
SortBy: 'ProductionYear,PremiereDate,SortName',
|
||||||
|
SortOrder: 'Ascending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
embyName: 'Bob',
|
||||||
|
sections: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(normalized.Name, 'New to Emby');
|
||||||
|
assert.equal(normalized.CustomName, 'New to Emby');
|
||||||
|
assert.equal(normalized.SortBy, 'DateLastContentAdded,SortName');
|
||||||
|
assert.equal(normalized.SortOrder, 'Descending');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section standards randomize regular curated sections', () => {
|
||||||
|
const normalized = applySectionStandards(
|
||||||
|
{
|
||||||
|
Name: 'Crime / Drama Shows',
|
||||||
|
CustomName: 'Crime / Drama Shows',
|
||||||
|
SectionType: 'items',
|
||||||
|
SortBy: 'ProductionYear,PremiereDate,SortName',
|
||||||
|
SortOrder: 'Ascending',
|
||||||
|
Query: { GenreIds: ['4910', '62'] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
embyName: 'Bob',
|
||||||
|
sections: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(normalized.SortBy, 'Random');
|
||||||
|
assert.equal(normalized.SortOrder, 'Descending');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('section standards preserve recently watched ordering semantics', () => {
|
||||||
|
const normalized = applySectionStandards(
|
||||||
|
{
|
||||||
|
Name: 'Recently Watched - Matt',
|
||||||
|
CustomName: 'Recently Watched - Matt',
|
||||||
|
SectionType: 'items',
|
||||||
|
SortBy: 'Random',
|
||||||
|
SortOrder: 'Ascending',
|
||||||
|
Query: { IsPlayed: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
embyName: 'Bob',
|
||||||
|
sections: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(normalized.Name, 'Recently Watched - Bob');
|
||||||
|
assert.equal(normalized.CustomName, 'Recently Watched - Bob');
|
||||||
|
assert.equal(normalized.SortBy, 'DatePlayed');
|
||||||
|
assert.equal(normalized.SortOrder, 'Descending');
|
||||||
|
});
|
||||||
|
|
||||||
test('normalizeLookupItem preserves already-normalized recommendation items', () => {
|
test('normalizeLookupItem preserves already-normalized recommendation items', () => {
|
||||||
const normalized = normalizeLookupItem({
|
const normalized = normalizeLookupItem({
|
||||||
id: 'pick-1',
|
id: 'pick-1',
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user