diff --git a/web/static/css/style.css b/web/static/css/style.css index 59da76f2..1638ddfb 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -6851,13 +6851,40 @@ html[data-theme="dark"] .login-card .login-submit:disabled { font-size: 0.78rem; } -.robot-binding-footer { +.modal-footer.robot-binding-footer { + align-items: center; justify-content: space-between; + min-height: 74px; + padding: 14px 26px; + background: linear-gradient(180deg, var(--bg-primary) 0%, var(--bg-secondary) 145%); } -.robot-binding-footer > span { +.robot-binding-service-hint { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 7px; + min-width: 0; color: var(--text-secondary); - font-size: 0.76rem; + font-size: 0.78rem; + line-height: 1.5; +} + +.robot-binding-service-hint-icon { + width: 26px; + height: 26px; + flex: 0 0 26px; + display: inline-grid; + place-items: center; + color: var(--accent-color); + border-radius: 8px; + background: rgba(0, 102, 255, 0.09); +} + +.robot-binding-footer > .btn-secondary { + flex: 0 0 auto; + min-width: 66px; + min-height: 38px; } html[data-theme="dark"] .robot-binding-guide-number, @@ -6871,6 +6898,10 @@ html[data-theme="dark"] .robot-binding-one-time-badge { background: rgba(167, 139, 250, 0.14); } +html[data-theme="dark"] .robot-binding-service-hint-icon { + background: rgba(96, 165, 250, 0.14); +} + @media (max-width: 680px) { .robot-account-binding-content { width: calc(100% - 20px); @@ -6911,9 +6942,17 @@ html[data-theme="dark"] .robot-binding-one-time-badge { margin-left: 50px; } - .robot-binding-footer > span { - display: none; + .modal-footer.robot-binding-footer { + gap: 10px; + min-height: 68px; + padding: 12px 18px; } + + .robot-binding-service-hint { + flex: 1; + font-size: 0.74rem; + } + } .robot-type-grid { @@ -10545,18 +10584,107 @@ html[data-theme="dark"] .robot-binding-one-time-badge { } /* Workflow package import */ +.workflow-package-modal { + padding: 14px; + background: + radial-gradient(circle at 50% 14%, rgba(59, 130, 246, 0.12), transparent 34rem), + rgba(3, 9, 22, 0.72); + backdrop-filter: blur(7px); +} + .workflow-package-modal-content { - width: min(1040px, calc(100vw - 32px)); - max-height: calc(100vh - 48px); - margin: 24px auto; + width: min(1080px, 100%); + max-width: 1080px; + max-height: calc(100vh - 28px); + margin: 0 auto; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--border-color) 72%, var(--accent-color)); + border-radius: 20px; + background: var(--bg-primary); + box-shadow: 0 34px 100px rgba(2, 8, 23, 0.48), 0 0 0 1px rgba(255, 255, 255, 0.025) inset; } .workflow-package-modal-header { - align-items: flex-start; + align-items: center; + padding: 18px 24px !important; + background: + radial-gradient(circle at 15% 0%, color-mix(in srgb, var(--accent-color) 13%, transparent), transparent 25rem), + var(--bg-primary) !important; + box-shadow: none; +} + +.workflow-package-modal-header::after { + left: 24px; + right: 24px; +} + +.workflow-package-title-group, +.workflow-package-title-line { + display: flex; + align-items: center; +} + +.workflow-package-title-group { + min-width: 0; + gap: 14px; +} + +.workflow-package-title-icon { + display: inline-flex; + flex: 0 0 40px; + width: 40px; + height: 40px; + align-items: center; + justify-content: center; + border: 1px solid color-mix(in srgb, var(--accent-color) 28%, transparent); + border-radius: 13px; + background: linear-gradient(145deg, color-mix(in srgb, var(--accent-color) 17%, var(--bg-primary)), color-mix(in srgb, #8b5cf6 10%, var(--bg-primary))); + color: var(--accent-color); + box-shadow: 0 8px 22px color-mix(in srgb, var(--accent-color) 14%, transparent); +} + +.workflow-package-title-icon svg { + width: 22px; + height: 22px; +} + +.workflow-package-title-line { + min-width: 0; + gap: 10px; } .workflow-package-modal-header h2 { + flex: 0 1 auto; margin: 0; + font-size: 1.25rem !important; + background: none; + color: var(--text-primary); + -webkit-text-fill-color: currentColor; +} + +.workflow-package-modal-header h2::before { + display: none; +} + +.workflow-package-safe-badge, +.workflow-package-format-pill, +.workflow-package-decision-status { + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid color-mix(in srgb, var(--accent-color) 24%, transparent); + border-radius: 999px; + background: color-mix(in srgb, var(--accent-color) 8%, transparent); + color: var(--accent-color); + font-size: 10px; + font-weight: 700; + letter-spacing: .04em; + white-space: nowrap; +} + +.workflow-package-safe-badge { + height: 23px; + padding: 0 9px; } .workflow-package-subtitle, @@ -10568,32 +10696,79 @@ html[data-theme="dark"] .robot-binding-one-time-badge { } .workflow-package-steps { + position: relative; display: grid; grid-template-columns: 1fr 1fr; - gap: 16px; - padding: 12px 22px; + gap: 0; + padding: 9px 28px 10px; border-bottom: 1px solid var(--border-color); - background: var(--bg-secondary); + background: color-mix(in srgb, var(--bg-secondary) 68%, var(--bg-primary)); +} + +.workflow-package-steps::before { + content: ''; + position: absolute; + z-index: 0; + top: 24px; + left: 25%; + right: 25%; + height: 1px; + background: var(--border-color); + pointer-events: none; } .workflow-package-steps > span { + position: relative; + z-index: 1; display: flex; + flex-direction: column; align-items: center; - gap: 8px; + gap: 3px; color: var(--text-secondary); + text-align: center; +} + +.workflow-package-steps > span > span { + display: flex; + min-width: 0; + align-items: baseline; + justify-content: center; + gap: 7px; + white-space: nowrap; +} + +.workflow-package-steps strong { + color: inherit; font-size: 13px; - font-weight: 600; + font-weight: 700; +} + +.workflow-package-steps small { + color: var(--text-muted); + font-size: 10px; + font-weight: 500; +} + +.workflow-package-steps small::before { + content: '·'; + margin-right: 7px; + color: var(--text-muted); } .workflow-package-steps b { + position: relative; + z-index: 1; display: inline-flex; align-items: center; justify-content: center; - width: 22px; - height: 22px; + flex: 0 0 30px; + width: 30px; + height: 30px; border: 1px solid var(--border-color); border-radius: 999px; + background: var(--bg-primary); font-size: 12px; + box-shadow: 0 0 0 5px color-mix(in srgb, var(--bg-secondary) 68%, var(--bg-primary)); } .workflow-package-steps .is-active { @@ -10602,54 +10777,341 @@ html[data-theme="dark"] .robot-binding-one-time-badge { .workflow-package-steps .is-active b { border-color: var(--accent-color); - background: var(--accent-color); + background: linear-gradient(135deg, var(--accent-color), #7c3aed); color: #fff; + box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent-color) 10%, var(--bg-primary)), 0 5px 14px color-mix(in srgb, var(--accent-color) 28%, transparent); +} + +.workflow-package-steps .is-complete b { + border-color: color-mix(in srgb, var(--success-color) 65%, transparent); + background: color-mix(in srgb, var(--success-color) 13%, var(--bg-primary)); + color: var(--success-color); +} + +.workflow-package-steps .is-complete b { + font-size: 0; +} + +.workflow-package-steps .is-complete b::before { + content: '✓'; + font-size: 13px; + font-weight: 800; } .workflow-package-modal-body { display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + grid-template-columns: minmax(0, 1.16fr) minmax(330px, .84fr); + align-items: stretch; gap: 16px; - padding: 18px 22px; + padding: 16px 24px 18px; + background: + linear-gradient(110deg, color-mix(in srgb, var(--accent-color) 2%, transparent), transparent 48%), + color-mix(in srgb, var(--bg-secondary) 52%, var(--bg-primary)); } .workflow-package-pane { min-width: 0; padding: 16px; border: 1px solid var(--border-color); - border-radius: 8px; + border-radius: 12px; background: var(--bg-primary); + box-shadow: 0 10px 30px rgba(2, 8, 23, 0.045); } .workflow-package-pane h3 { - margin: 0 0 12px; - font-size: 14px; + margin: 2px 0 0; + color: var(--text-primary); + font-size: 15px; + line-height: 1.35; } -.workflow-package-file-row { +.workflow-package-pane-heading { display: flex; align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 10px; +} + +.workflow-package-eyebrow { + color: var(--text-muted); + font-size: 9px; + font-weight: 800; + letter-spacing: .12em; + text-transform: uppercase; +} + +.workflow-package-format-pill, +.workflow-package-decision-status { + height: 24px; + padding: 0 9px; + color: var(--text-secondary); + border-color: var(--border-color); + background: var(--bg-secondary); + font-size: 9px; +} + +.workflow-package-decision-status::before { + content: ''; + width: 5px; + height: 5px; + margin-right: 5px; + border-radius: 50%; + background: var(--text-muted); +} + +.workflow-package-decision-status.is-ready { + border-color: color-mix(in srgb, var(--success-color) 30%, transparent); + background: color-mix(in srgb, var(--success-color) 9%, transparent); + color: var(--success-color); +} + +.workflow-package-decision-status.is-ready::before { + background: var(--success-color); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--success-color) 12%, transparent); +} + +.workflow-package-pane--resolution { + align-self: stretch; + background: + radial-gradient(circle at 100% 0%, color-mix(in srgb, #8b5cf6 5%, transparent), transparent 16rem), + var(--bg-primary); +} + +.workflow-package-pane--resolution .workflow-package-resolution { + margin-top: 0; +} + +.workflow-package-dropzone { + display: flex; + min-height: 116px; + box-sizing: border-box; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px; + border: 1.5px dashed color-mix(in srgb, var(--border-color) 64%, var(--accent-color)); + border-radius: 12px; + background: color-mix(in srgb, var(--accent-color) 3%, var(--bg-secondary)); + cursor: pointer; + outline: none; + transition: border-color 150ms ease, background-color 150ms ease; +} + +.workflow-package-dropzone:hover, +.workflow-package-dropzone.is-dragging { + border-color: color-mix(in srgb, var(--accent-color) 72%, var(--border-color)); + background: color-mix(in srgb, var(--accent-color) 5%, var(--bg-secondary)); +} + +.workflow-package-dropzone:focus-visible { + border-color: var(--accent-color); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-color) 18%, transparent); +} + +.workflow-package-upload-icon { + display: inline-flex; + width: 40px; + height: 40px; + align-items: center; + justify-content: center; + border: 1px solid color-mix(in srgb, var(--accent-color) 18%, transparent); + border-radius: 11px; + background: color-mix(in srgb, var(--accent-color) 11%, var(--bg-primary)); + color: var(--accent-color); +} + +.workflow-package-upload-icon svg { + width: 20px; + height: 20px; +} + +.workflow-package-dropzone-copy { + display: grid; + gap: 5px; + color: var(--text-secondary); + font-size: 12px; + text-align: center; +} + +.workflow-package-dropzone-copy strong { + color: var(--text-primary); + font-size: 15px; +} + +.workflow-package-browse { + color: var(--accent-color); + font-weight: 600; +} + +.workflow-package-selected-file { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; gap: 10px; + margin-top: 9px; + padding: 8px 10px; + border: 1px solid color-mix(in srgb, var(--accent-color) 18%, var(--border-color)); + border-radius: 10px; + background: color-mix(in srgb, var(--accent-color) 3%, var(--bg-secondary)); +} + +.workflow-package-selected-file[hidden] { + display: none; +} + +.workflow-package-file-badge { + display: inline-flex; + height: 30px; + padding: 0 8px; + align-items: center; + justify-content: center; + border-radius: 6px; + background: color-mix(in srgb, var(--accent-color) 14%, var(--bg-primary)); + color: var(--accent-color); + font-size: 9px; + font-weight: 800; + letter-spacing: .04em; +} + +.workflow-package-file-details { + display: grid; + min-width: 0; + gap: 3px; } .workflow-package-file-name { min-width: 0; overflow: hidden; color: var(--text-secondary); + color: var(--text-primary); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } -.workflow-package-pane--inspection > .btn-primary { - margin-top: 12px; +.workflow-package-file-details small { + color: var(--text-secondary); + font-size: 11px; +} + +.workflow-package-file-remove { + display: inline-flex; + flex: 0 0 28px; + width: 28px; + height: 28px; + padding: 0; + align-items: center; + justify-content: center; + overflow: hidden; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--text-secondary); + cursor: pointer; + font-size: 20px; + line-height: 1; +} + +.workflow-package-file-remove:hover, +.workflow-package-file-remove:focus-visible { + background: color-mix(in srgb, var(--text-secondary) 10%, transparent); + color: var(--text-primary); +} + +.workflow-package-file-hint { + margin-top: 4px; + padding-left: 14px; + text-align: center; +} + +.workflow-package-file-hint::before { + content: 'i'; + display: inline-flex; + width: 13px; + height: 13px; + margin: 0 5px 0 -14px; + align-items: center; + justify-content: center; + border: 1px solid var(--text-muted); + border-radius: 50%; + font-size: 8px; + font-style: normal; + font-weight: 800; + vertical-align: 1px; +} + +.workflow-package-inspect-btn { + width: 100%; + margin-top: 9px; + min-height: 36px; + border-radius: 9px; + background: var(--accent-color); + font-weight: 700; + box-shadow: none; +} + +.workflow-package-modal .btn-primary { + background: var(--accent-color); + box-shadow: none; + transition: background-color 150ms ease, border-color 150ms ease, opacity 150ms ease; +} + +.workflow-package-modal .btn-primary:hover:not(:disabled) { + background: var(--accent-hover); + box-shadow: none; + transform: none; +} + +.workflow-package-modal .btn-primary:active:not(:disabled) { + background: color-mix(in srgb, var(--accent-color) 88%, #000); + box-shadow: none; + transform: none; +} + +.workflow-package-modal .btn-primary:disabled, +.workflow-package-modal .btn-primary:disabled:hover { + background: color-mix(in srgb, var(--accent-color) 62%, var(--bg-tertiary)); + box-shadow: none; + opacity: .62; + transform: none; + cursor: not-allowed; +} + +.workflow-package-modal .btn-secondary { + transition: background-color 150ms ease, border-color 150ms ease; +} + +.workflow-package-modal .btn-secondary:hover:not(:disabled) { + border-color: color-mix(in srgb, var(--accent-color) 35%, var(--border-color)); + background: var(--bg-tertiary); + box-shadow: none; + transform: none; +} + +.workflow-package-inspect-btn.is-loading::before { + content: ''; + display: inline-block; + width: 12px; + height: 12px; + margin-right: 8px; + border: 2px solid rgba(255, 255, 255, .35); + border-top-color: #fff; + border-radius: 50%; + vertical-align: -2px; + animation: workflow-package-spin .7s linear infinite; +} + +@keyframes workflow-package-spin { + to { transform: rotate(360deg); } } .workflow-package-summary, .workflow-package-resolution { display: grid; - gap: 10px; - margin-top: 16px; + gap: 8px; + margin-top: 11px; } .workflow-package-summary:empty, @@ -10657,13 +11119,46 @@ html[data-theme="dark"] .robot-binding-one-time-badge { display: none; } -.workflow-package-summary-row { +.workflow-package-summary-heading { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 2px; + color: var(--text-primary); + font-size: 13px; + font-weight: 700; +} + +.workflow-package-summary-heading b { + color: var(--success-color); + font-size: 10px; + font-weight: 700; +} + +.workflow-package-summary-heading b::before { + content: '✓'; + margin-right: 4px; +} + +.workflow-package-summary-grid { display: grid; - grid-template-columns: minmax(88px, 0.65fr) minmax(0, 1fr); - gap: 10px; - padding-bottom: 8px; - border-bottom: 1px solid var(--border-color); - font-size: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1px; + overflow: hidden; + border: 1px solid var(--border-color); + border-radius: 10px; + background: var(--border-color); +} + +.workflow-package-summary-row { + display: flex; + min-width: 0; + flex-direction: column; + gap: 2px; + padding: 7px 10px; + border: 0; + background: var(--bg-secondary); + font-size: 11px; } .workflow-package-summary-row span { @@ -10673,18 +11168,46 @@ html[data-theme="dark"] .robot-binding-one-time-badge { .workflow-package-summary-row code { overflow: hidden; color: var(--text-primary); + font-family: inherit; + font-size: 12px; + font-weight: 650; text-overflow: ellipsis; white-space: nowrap; } .workflow-package-status { - padding: 10px 12px; + display: flex; + align-items: flex-start; + gap: 8px; + padding: 8px 10px; border: 1px solid rgba(59, 130, 246, 0.35); - border-radius: 7px; + border-radius: 9px; background: rgba(59, 130, 246, 0.08); color: var(--text-primary); - font-size: 13px; - line-height: 1.55; + font-size: 12px; + line-height: 1.45; +} + +.workflow-package-status-icon { + position: relative; + flex: 0 0 16px; + width: 16px; + height: 16px; + margin-top: 2px; + border-radius: 50%; + background: rgba(59, 130, 246, .18); +} + +.workflow-package-status-icon::after { + content: 'i'; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: #60a5fa; + font-size: 9px; + font-weight: 800; } .workflow-package-status.is-warning { @@ -10692,26 +11215,87 @@ html[data-theme="dark"] .robot-binding-one-time-badge { background: rgba(245, 158, 11, 0.1); } +.workflow-package-status.is-warning .workflow-package-status-icon { + background: rgba(245, 158, 11, .18); +} + +.workflow-package-status.is-warning .workflow-package-status-icon::after { + content: '!'; + color: var(--warning-color); +} + .workflow-package-status.is-success { border-color: rgba(34, 197, 94, 0.45); background: rgba(34, 197, 94, 0.1); } +.workflow-package-status.is-success .workflow-package-status-icon { + background: rgba(34, 197, 94, .18); +} + +.workflow-package-status.is-success .workflow-package-status-icon::after { + content: '✓'; + color: var(--success-color); +} + +.workflow-package-resolution-hero { + display: flex; + align-items: center; + gap: 12px; + min-height: 66px; + padding: 12px; + border: 1px solid color-mix(in srgb, var(--success-color) 34%, transparent); + border-radius: 11px; + background: linear-gradient(135deg, color-mix(in srgb, var(--success-color) 11%, transparent), color-mix(in srgb, var(--success-color) 4%, transparent)); +} + +.workflow-package-resolution-icon { + display: inline-flex; + flex: 0 0 34px; + width: 34px; + height: 34px; + align-items: center; + justify-content: center; + border-radius: 10px; + background: color-mix(in srgb, var(--success-color) 16%, var(--bg-primary)); + color: var(--success-color); + font-size: 15px; + font-weight: 800; +} + +.workflow-package-resolution-hero strong, +.workflow-package-resolution-hero small { + display: block; +} + +.workflow-package-resolution-hero strong { + margin-bottom: 3px; + color: var(--text-primary); + font-size: 13px; +} + +.workflow-package-resolution-hero small { + color: var(--text-secondary); + font-size: 11px; + line-height: 1.5; +} + .workflow-package-choice, .workflow-package-reveal { display: flex; align-items: flex-start; gap: 9px; - padding: 12px; + padding: 10px; border: 1px solid var(--border-color); - border-radius: 7px; + border-radius: 10px; background: var(--bg-secondary); cursor: pointer; } .workflow-package-choice.is-selected { border-color: var(--accent-color); - background: rgba(59, 130, 246, 0.08); + background: color-mix(in srgb, var(--accent-color) 8%, var(--bg-primary)); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-color) 8%, transparent); } .workflow-package-choice input, @@ -10732,21 +11316,51 @@ html[data-theme="dark"] .robot-binding-one-time-badge { .workflow-package-reveal { width: 100%; + justify-content: center; + border-style: dashed; color: var(--text-secondary); font: inherit; - text-align: left; + font-size: 12px; + text-align: center; } .workflow-package-rename-field { display: grid; - gap: 6px; + gap: 7px; + color: var(--text-primary); font-size: 12px; - font-weight: 600; + font-weight: 650; } .workflow-package-rename-field input { width: 100%; + height: 38px; box-sizing: border-box; + padding: 0 11px; + border: 1px solid var(--border-color); + border-radius: 9px; + outline: none; + background: var(--bg-secondary); + color: var(--text-primary); + font: inherit; + font-size: 12px; + font-weight: 500; + transition: border-color 150ms ease, background-color 150ms ease; +} + +.workflow-package-rename-field input::placeholder { + color: var(--text-muted); + opacity: 1; +} + +.workflow-package-rename-field input:hover { + border-color: color-mix(in srgb, var(--accent-color) 35%, var(--border-color)); +} + +.workflow-package-rename-field input:focus { + border-color: var(--accent-color); + background: color-mix(in srgb, var(--accent-color) 3%, var(--bg-secondary)); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-color) 12%, transparent); } .workflow-package-confirm-check { @@ -10761,10 +11375,75 @@ html[data-theme="dark"] .robot-binding-one-time-badge { margin: 18vh auto auto; } +.workflow-package-modal-footer { + align-items: center; + justify-content: space-between; + padding: 11px 24px; + background: color-mix(in srgb, var(--bg-secondary) 45%, var(--bg-primary)); +} + +.workflow-package-footer-note, +.workflow-package-footer-actions { + display: flex; + align-items: center; +} + +.workflow-package-footer-note { + gap: 7px; + color: var(--text-secondary); + font-size: 11px; +} + +.workflow-package-footer-note > span:first-child { + display: inline-flex; + width: 18px; + height: 18px; + align-items: center; + justify-content: center; + border-radius: 50%; + background: color-mix(in srgb, var(--success-color) 12%, transparent); + color: var(--success-color); + font-size: 10px; + font-weight: 800; +} + +.workflow-package-footer-actions { + gap: 10px; +} + +.workflow-package-footer-actions .btn-primary, +.workflow-package-footer-actions .btn-secondary { + min-width: 94px; + min-height: 36px; + border-radius: 9px; + font-weight: 650; +} + +.workflow-package-footer-actions .btn-primary { + background: var(--accent-color); +} + @media (max-width: 760px) { + .workflow-package-modal { + padding: 10px; + } + .workflow-package-modal-content { - width: calc(100vw - 20px); - margin: 10px auto; + width: 100%; + max-height: calc(100vh - 20px); + margin: 0 auto; + border-radius: 15px; + } + + .workflow-package-modal-header { + padding: 18px !important; + } + + .workflow-package-title-icon, + .workflow-package-safe-badge, + .workflow-package-steps small, + .workflow-package-footer-note { + display: none; } .workflow-package-modal-body { @@ -10773,8 +11452,32 @@ html[data-theme="dark"] .robot-binding-one-time-badge { } .workflow-package-steps { - gap: 8px; - padding: 10px 14px; + padding: 12px 18px; + } + + .workflow-package-steps::before { + top: 26px; + left: 25%; + right: 25%; + } + + .workflow-package-steps b { + flex-basis: 28px; + width: 28px; + height: 28px; + } + + .workflow-package-dropzone { + min-height: 138px; + } + + .workflow-package-summary-grid { + grid-template-columns: minmax(0, 1fr); + } + + .workflow-package-modal-footer { + justify-content: flex-end; + padding: 14px; } } @@ -35931,14 +36634,16 @@ html[data-theme="dark"] .form-group select { .workflow-list-item { display: flex; align-items: center; - gap: 8px; + gap: 10px; width: 100%; - padding: 8px 8px 8px 10px; + min-height: 62px; + padding: 10px 10px 10px 13px; border: 1px solid var(--border-color); border-radius: 10px; background: var(--bg-primary); color: var(--text-primary); - transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease; + box-sizing: border-box; + transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, transform 0.18s ease; } .workflow-list-item.is-active { @@ -35950,6 +36655,7 @@ html[data-theme="dark"] .form-group select { .workflow-list-item:hover { border-color: rgba(59, 130, 246, 0.45); background: rgba(59, 130, 246, 0.05); + transform: translateY(-1px); } .workflow-list-main { @@ -35968,16 +36674,95 @@ html[data-theme="dark"] .form-group select { .workflow-list-actions { display: flex; align-items: center; - gap: 6px; + gap: 3px; flex-shrink: 0; - padding-right: 2px; } .workflow-list-edit { - width: 28px; - height: 28px; + width: 26px; + height: 26px; padding: 0; - border-radius: 7px; + border: 0; + border-radius: 50%; + background: transparent; + color: var(--text-secondary); + opacity: 0.62; + transition: color 0.16s ease, background 0.16s ease, opacity 0.16s ease, transform 0.16s ease; +} + +.workflow-list-item:hover .workflow-list-edit, +.workflow-list-edit:focus-visible { + opacity: 1; +} + +.workflow-list-edit:hover { + color: var(--accent-color); + background: rgba(59, 130, 246, 0.10); + transform: scale(1.04); +} + +.workflow-status-toggle { + display: inline-flex; + align-items: center; + gap: 5px; + min-height: 24px; + padding: 3px 7px; + border: 1px solid transparent; + border-radius: 999px; + font: inherit; + font-size: 10px; + line-height: 1; + font-weight: 650; + white-space: nowrap; + cursor: pointer; + transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease; +} + +.workflow-status-toggle.is-enabled { + color: #15803d; + border-color: rgba(34, 197, 94, 0.22); + background: rgba(34, 197, 94, 0.09); +} + +.workflow-status-toggle.is-disabled { + color: var(--text-secondary); + border-color: var(--border-color); + background: var(--bg-secondary); +} + +.workflow-status-toggle:hover { + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.08); +} + +.workflow-status-toggle:focus-visible, +.workflow-list-edit:focus-visible { + outline: 2px solid rgba(59, 130, 246, 0.38); + outline-offset: 2px; +} + +.workflow-status-dot { + width: 6px; + height: 6px; + flex: 0 0 auto; + border-radius: 50%; + background: currentColor; + box-shadow: 0 0 0 2px rgba(100, 116, 139, 0.10); +} + +.workflow-status-toggle.is-enabled .workflow-status-dot { + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.14); +} + +html[data-theme="dark"] .workflow-status-toggle.is-enabled { + color: #86efac; + border-color: rgba(74, 222, 128, 0.20); + background: rgba(34, 197, 94, 0.10); +} + +html[data-theme="dark"] .workflow-status-toggle.is-disabled { + color: #94a3b8; + border-color: rgba(148, 163, 184, 0.16); + background: rgba(148, 163, 184, 0.07); } .workflow-switch { @@ -36292,7 +37077,7 @@ html[data-theme="dark"] .form-group select { flex-wrap: nowrap; justify-content: flex-end; align-items: center; - gap: 6px; + gap: 8px; flex-shrink: 0; white-space: nowrap; padding-left: 12px; @@ -36300,19 +37085,198 @@ html[data-theme="dark"] .form-group select { min-height: 32px; } -.workflow-toolbar .btn-small { +.workflow-toolbar .btn-small, +.workflow-more-actions-trigger { min-height: 30px; padding: 5px 11px; font-size: 12px; font-weight: 600; } +.workflow-toolbar-button, +.workflow-save-button, +.workflow-more-actions-trigger { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; +} + +.workflow-toolbar-button svg, +.workflow-save-button svg, +.workflow-more-actions-trigger svg, +.workflow-more-actions-menu svg { + flex: 0 0 auto; +} + +.workflow-toolbar-group { + display: inline-flex; + align-items: center; +} + +.workflow-toolbar-group .workflow-toolbar-button { + border-radius: 0; +} + +.workflow-toolbar-group .workflow-toolbar-button:first-child { + border-radius: 7px 0 0 7px; +} + +.workflow-toolbar-group .workflow-toolbar-button:last-child { + margin-left: -1px; + border-radius: 0 7px 7px 0; +} + +.workflow-toolbar .workflow-run-button { + color: var(--accent-color); + border-color: color-mix(in srgb, var(--accent-color) 40%, var(--border-color)); + background: color-mix(in srgb, var(--accent-color) 7%, var(--bg-primary)); +} + +html[data-theme="dark"] .workflow-toolbar .workflow-run-button { + color: #bfdbfe; + border-color: rgba(96, 165, 250, 0.38); + background: rgba(96, 165, 250, 0.08); +} + +.workflow-more-actions { + position: relative; + flex: 0 0 auto; +} + +.workflow-more-actions > summary { + list-style: none; + cursor: pointer; + box-sizing: border-box; +} + +.workflow-more-actions > summary::-webkit-details-marker { + display: none; +} + +.workflow-more-actions[open] .workflow-more-actions-trigger { + color: var(--accent-color); + border-color: color-mix(in srgb, var(--accent-color) 45%, var(--border-color)); + background: color-mix(in srgb, var(--accent-color) 9%, var(--bg-primary)); +} + +.workflow-more-actions-menu { + position: absolute; + z-index: 30; + top: calc(100% + 8px); + right: 0; + display: grid; + width: 158px; + padding: 6px; + border: 1px solid var(--border-color); + border-radius: 10px; + background: var(--bg-primary); + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18); +} + +.workflow-more-actions-menu button { + display: flex; + align-items: center; + gap: 9px; + width: 100%; + padding: 8px 9px; + border: 0; + border-radius: 7px; + background: transparent; + color: var(--text-primary); + font: inherit; + font-size: 12px; + font-weight: 600; + text-align: left; + cursor: pointer; +} + +.workflow-more-actions-menu button:hover, +.workflow-more-actions-menu button:focus-visible { + outline: none; + background: var(--bg-secondary); +} + +.workflow-more-actions-menu .workflow-more-actions-danger { + color: #dc2626; +} + +.workflow-more-actions-menu .workflow-more-actions-danger:hover, +.workflow-more-actions-menu .workflow-more-actions-danger:focus-visible { + background: rgba(239, 68, 68, 0.09); +} + +.workflow-more-actions-divider { + height: 1px; + margin: 4px 3px; + background: var(--border-color); +} + +.workflow-more-actions-menu button[hidden] + .workflow-more-actions-divider, +.workflow-more-actions-menu .workflow-more-actions-divider:has(+ button[hidden]) { + display: none; +} + +.workflow-toolbar .workflow-save-button { + min-width: 72px; + color: #fff; + border: 1px solid var(--accent-color); + background: var(--accent-color); + box-shadow: 0 3px 10px rgba(37, 99, 235, 0.18); +} + +.workflow-toolbar .workflow-save-button:hover { + color: #fff; + border-color: var(--accent-hover); + background: var(--accent-hover); +} + .workflow-toolbar .active { background: var(--accent-color); color: #fff; border-color: var(--accent-color); } +html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="true"] { + color: #bfdbfe; + border-color: rgba(96, 165, 250, 0.58); + background: rgba(37, 99, 235, 0.18); + box-shadow: inset 0 0 0 1px rgba(147, 197, 253, 0.06), 0 0 0 2px rgba(37, 99, 235, 0.05); +} + +html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="true"]:hover { + color: #dbeafe; + border-color: rgba(96, 165, 250, 0.72); + background: rgba(37, 99, 235, 0.24); +} + +@media (max-width: 1100px) { + .workflow-toolbar-label { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + + .workflow-toolbar .btn-small, + .workflow-more-actions-trigger { + width: 32px; + min-width: 32px; + padding-inline: 0; + } + + .workflow-save-button { + width: auto !important; + min-width: 38px !important; + padding-inline: 11px !important; + } +} + .workflow-canvas-wrap { position: relative; min-height: 520px; diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 2d3d34ac..f622e643 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -3131,18 +3131,35 @@ "enabledUpdateFailed": "Failed to update enabled status", "namePlaceholder": "Basic Web scan", "descriptionPlaceholder": "Optional", + "toolbarLabel": "Workflow editing tools", + "canvasTools": "Canvas tools", + "moreActions": "More", + "deleteWorkflow": "Delete workflow", "package": { "importLocal": "Import local package", "export": "Export", "title": "Import local package", - "preflightHint": "Preflight does not modify local workflows", + "preflightHint": "Review package contents and conflicts before you approve the import", + "safeBadge": "Safe preflight", "stepInspection": "Upload and preflight", + "stepInspectionHint": "Validate format, content, and version", "stepImport": "Resolve conflict and import", + "stepImportHint": "Choose a strategy and finish importing", "stepsLabel": "Import steps", + "packageEyebrow": "Local workflow package", + "packageTitle": "Choose a package to import", + "decisionEyebrow": "Import strategy", + "pending": "Pending", + "ready": "Ready", + "footerNote": "Existing workflows stay unchanged until you confirm", "selectFile": "Choose local package", + "dropTitle": "Drop a local package here", + "dropOr": "or", + "removeFile": "Remove file", "noFile": "No file selected", "fileHint": "One .csapkg.zip package only, up to 10 MiB.", "inspect": "Upload and preflight", + "inspecting": "Running safe preflight…", "resolutionTitle": "Resolution", "confirmImport": "Confirm import", "overwriteTitle": "Overwrite local workflow?", @@ -3180,6 +3197,8 @@ "none": "No local workflow with the same ID was found. A new workflow can be created." }, "summary": { + "title": "Preflight result", + "passed": "Checks passed", "workflowName": "Workflow name", "unnamedWorkflow": "Untitled workflow", "sourceId": "Source workflow ID", @@ -3191,7 +3210,9 @@ }, "resolution": { "needInspection": "Choose a local package and complete preflight first.", + "createTitle": "Safe to create", "createHint": "Import will create a new local workflow.", + "identicalTitle": "No duplicate needed", "identicalHint": "The same content already exists. No duplicate import is needed.", "keepExisting": "Keep local version", "keepExistingHint": "Do not change the current local workflow; the import record is retained.", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 7cfbaef2..3328974d 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -3119,18 +3119,35 @@ "enabledUpdateFailed": "更新启用状态失败", "namePlaceholder": "基础 Web 扫描", "descriptionPlaceholder": "可选", + "toolbarLabel": "流程编辑工具", + "canvasTools": "画布工具", + "moreActions": "更多", + "deleteWorkflow": "删除流程", "package": { "importLocal": "导入本地包", "export": "导出", "title": "导入本地包", - "preflightHint": "预检不会修改本地工作流", + "preflightHint": "先检查包内容与冲突,再由你确认是否导入", + "safeBadge": "安全预检", "stepInspection": "上传与预检", + "stepInspectionHint": "校验格式、内容与版本", "stepImport": "处理冲突与导入", + "stepImportHint": "确认处理方式并完成导入", "stepsLabel": "导入步骤", + "packageEyebrow": "本地工作流包", + "packageTitle": "选择需要导入的文件", + "decisionEyebrow": "导入策略", + "pending": "待预检", + "ready": "可处理", + "footerNote": "导入前不会改动现有工作流", "selectFile": "选择本地包", + "dropTitle": "拖拽本地包到这里", + "dropOr": "或", + "removeFile": "移除文件", "noFile": "未选择文件", "fileHint": "仅支持单个 .csapkg.zip,文件不得超过 10 MiB。", "inspect": "上传并预检", + "inspecting": "正在安全预检…", "resolutionTitle": "处理方式", "confirmImport": "确认导入", "overwriteTitle": "确认覆盖本地工作流?", @@ -3168,6 +3185,8 @@ "none": "未发现同 ID 的本地工作流,可创建导入。" }, "summary": { + "title": "预检结果", + "passed": "校验通过", "workflowName": "工作流名称", "unnamedWorkflow": "未命名工作流", "sourceId": "源工作流 ID", @@ -3179,7 +3198,9 @@ }, "resolution": { "needInspection": "请先选择本地包并完成预检。", + "createTitle": "可以安全创建", "createHint": "导入后会创建一个新的本地工作流。", + "identicalTitle": "无需重复导入", "identicalHint": "内容已经存在,无需重复导入。", "keepExisting": "保留本地版本", "keepExistingHint": "不修改当前本地工作流;导入记录会保留。", diff --git a/web/static/js/workflow-package-ui.test.cjs b/web/static/js/workflow-package-ui.test.cjs index 5ed4736a..42345033 100644 --- a/web/static/js/workflow-package-ui.test.cjs +++ b/web/static/js/workflow-package-ui.test.cjs @@ -12,6 +12,18 @@ test('图编排提供导入、导出和覆盖确认容器', () => { assert.equal(zh.workflows.package.importLocal, '导入本地包'); }); +test('本地包上传支持拖拽、键盘选择和选中文件反馈', () => { + const html = fs.readFileSync('web/templates/index.html', 'utf8'); + const workflows = fs.readFileSync('web/static/js/workflows.js', 'utf8'); + const css = fs.readFileSync('web/static/css/style.css', 'utf8'); + assert.match(html, /id="workflow-package-dropzone"[\s\S]*?ondrop="onWorkflowPackageDrop\(event\)"/); + assert.match(html, /onkeydown="onWorkflowPackageDropzoneKeydown\(event\)"/); + assert.match(html, /id="workflow-package-selected-file"/); + assert.match(workflows, /window\.onWorkflowPackageDrop = function \(event\)/); + assert.match(workflows, /endsWith\('\.csapkg\.zip'\)/); + assert.match(css, /\.workflow-package-dropzone\.is-dragging/); +}); + test('工作流脚本调用包契约的全部端点与冲突错误码', () => { const workflows = fs.readFileSync('web/static/js/workflows.js', 'utf8'); const client = fs.readFileSync('web/static/js/workflow-package-client.js', 'utf8'); diff --git a/web/static/js/workflows.js b/web/static/js/workflows.js index f693cf72..df1c0758 100644 --- a/web/static/js/workflows.js +++ b/web/static/js/workflows.js @@ -34,7 +34,8 @@ newWorkflowId: '', riskChoicesVisible: false, idempotencyKey: '', - requestSignature: '' + requestSignature: '', + dragDepth: 0 }; const WORKFLOW_PACKAGE_INSPECTION_STORAGE_KEY = 'csai.workflow-package.inspection-id'; const WORKFLOW_PACKAGE_IMPORT_STORAGE_KEY = 'csai.workflow-package.import-id'; @@ -826,6 +827,7 @@ const toggleTitle = esc(_t('workflows.toggleEnabled')); const editTitle = esc(_t('workflows.editMeta')); const enabled = wf.enabled !== false; + const statusText = esc(_t(enabled ? 'workflows.statusEnabled' : 'workflows.statusDisabled')); return `
${esc(row[1])}