Polish asset sidebar UI
This commit is contained in:
+3
-3
@@ -2,12 +2,12 @@ import { Wt as e, h as t, v as n } from "./viewerRuntimeHosts-B9wQ_Nxj.js";
|
||||
import { B as r, C as i, I as a, M as o, O as s, P as c, R as l, S as u, V as d, j as f, m as p, o as m, pt as h, r as g, rt as _, v, w as y, x as b } from "./events-BpkKbGZs.js";
|
||||
import { a as x, i as S, o as C, s as w } from "./graphTraversal-CjIZsRsP.js";
|
||||
import { a as T, i as ee } from "./mediaFps-CbdE2lHO.js";
|
||||
import { a as E, c as D, i as O, n as k, o as te, r as A } from "./SidebarWorkflowSection-BkH7KoSY.js";
|
||||
import { a as j, c as M, i as N, n as P, o as ne, r as F, s as re, t as ie } from "./openMajoorSettings-DDiqOHU0.js";
|
||||
import { a as E, c as D, i as O, n as k, o as te, r as A } from "./SidebarWorkflowSection-Dhrg9X98.js";
|
||||
import { a as j, c as M, i as N, n as P, o as ne, r as F, s as re, t as ie } from "./openMajoorSettings-CVZbL23Z.js";
|
||||
import { i as I, n as L, r as R } from "./VideoControls-Dy5An_lN.js";
|
||||
import { i as ae, o as z, r as oe, t as se } from "./geninfoParser-DMd9PWlT.js";
|
||||
import { l as ce, o as le, p as ue, r as de, s as fe } from "./mediaPlayer-DTyhTlcJ.js";
|
||||
import { t as pe } from "./genInfo-CPsL2cZc.js";
|
||||
import { t as pe } from "./genInfo-B6Ymnifm.js";
|
||||
//#region ui/features/viewer/floatingViewerConstants.ts
|
||||
var B = Object.freeze({
|
||||
SIMPLE: "simple",
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { J as e, r as t } from "./events-BpkKbGZs.js";
|
||||
import { t as n } from "./floatingViewerManager-pz1ceTHv.js";
|
||||
import { t as n } from "./floatingViewerManager-tOuaq4XR.js";
|
||||
//#region ui/features/viewer/LiveStreamTracker.ts
|
||||
var r = !1, i = null, a = null, o = null, s = null, c = null, l = 0, u = 0, d = 400, f = new Set([
|
||||
".png",
|
||||
Vendored
+1069
-1044
File diff suppressed because it is too large
Load Diff
@@ -6449,7 +6449,7 @@ function Zt() {
|
||||
qt = e;
|
||||
}), Jt || import("./scopes-X1iFrTle.js").then((e) => {
|
||||
Jt = e;
|
||||
}), Yt || import("./genInfo-CPsL2cZc.js").then((e) => e.n).then((e) => {
|
||||
}), Yt || import("./genInfo-B6Ymnifm.js").then((e) => e.n).then((e) => {
|
||||
Yt = e;
|
||||
}), Xt || import("./frameExport-tksSZ7sb.js").then((e) => {
|
||||
Xt = e;
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { a as e, i as t } from "./viewerRuntimeHosts-B9wQ_Nxj.js";
|
||||
import { r as n } from "./events-BpkKbGZs.js";
|
||||
import { i as r, r as i } from "./floatingViewerManager-pz1ceTHv.js";
|
||||
import { i as r, r as i } from "./floatingViewerManager-tOuaq4XR.js";
|
||||
import { B as a, C as o, D as s, E as c, G as l, H as u, I as d, O as f, R as p, T as m, W as h, ct as g, dt as _, j as v, k as y, lt as b, nt as x, q as S, ut as C, w, z as T } from "./mjr-primevue-n1rsQYJg.js";
|
||||
import { t as E } from "./TagsEditor-DtmqPU71.js";
|
||||
import { a as D, c as O, i as k, n as A, o as ee, r as j, s as M, t as N } from "./Viewer-BG31OAT8.js";
|
||||
import { a as D, c as O, i as k, n as A, o as ee, r as j, s as M, t as N } from "./Viewer-CnTOrl2S.js";
|
||||
//#region ui/vue/components/viewer/FloatingViewerHost.vue
|
||||
var P = {
|
||||
__name: "FloatingViewerHost",
|
||||
Vendored
+1
-1
@@ -98,7 +98,7 @@ var ie = /* @__PURE__ */ e({
|
||||
teardownFloatingViewerManager: () => $
|
||||
}), h = null, g = null;
|
||||
async function ae() {
|
||||
return h || (g ||= import("./FloatingViewer-BokZM7bF.js").then((e) => (h = e.FloatingViewer, h)), g);
|
||||
return h || (g ||= import("./FloatingViewer-mV836Kgg.js").then((e) => (h = e.FloatingViewer, h)), g);
|
||||
}
|
||||
var _ = null, oe = null;
|
||||
async function se() {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
||||
import { m as t, o as n } from "./events-BpkKbGZs.js";
|
||||
import { c as r } from "./mediaFps-CbdE2lHO.js";
|
||||
import { c as i, h as a, l as o, s, t as c } from "./SidebarWorkflowSection-BkH7KoSY.js";
|
||||
import { c as i, h as a, l as o, s, t as c } from "./SidebarWorkflowSection-Dhrg9X98.js";
|
||||
import { B as l, D as u, E as d, O as f, T as p, ct as m, dt as h, k as g, ut as _ } from "./mjr-primevue-n1rsQYJg.js";
|
||||
//#region ui/vue/components/viewer/ViewerMetadataBlock.vue
|
||||
var v = { style: {
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { A as e, E as t, J as n, R as r, Y as i, b as a, j as o, m as s } from "./events-BpkKbGZs.js";
|
||||
import { a as c, n as l } from "./graphTraversal-CjIZsRsP.js";
|
||||
import { S as u } from "./SidebarWorkflowSection-BkH7KoSY.js";
|
||||
import { S as u } from "./SidebarWorkflowSection-Dhrg9X98.js";
|
||||
//#region ui/features/viewer/floatingViewerProgress.ts
|
||||
var d = "progress-update", f = "__MJR_MFV_PROGRESS_SERVICE__";
|
||||
function p() {
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { r as e } from "./events-BpkKbGZs.js";
|
||||
import { t } from "./Viewer-BG31OAT8.js";
|
||||
import { t } from "./Viewer-CnTOrl2S.js";
|
||||
//#region ui/features/viewer/viewerOpenRequest.ts
|
||||
function n(e) {
|
||||
let t = String(e || "").trim().toLowerCase();
|
||||
Vendored
+634
-651
File diff suppressed because one or more lines are too long
@@ -71,7 +71,11 @@ export function createSettingsSync({
|
||||
|
||||
const applySidebarPosition = (nextPosition: any) => {
|
||||
const pos = String(nextPosition || "right").toLowerCase() === "left" ? "left" : "right";
|
||||
if (_sidebarPosition === pos) return;
|
||||
const isAlreadyOrdered =
|
||||
pos === "left"
|
||||
? sidebar?.nextElementSibling === gridWrapper
|
||||
: gridWrapper?.nextElementSibling === sidebar;
|
||||
if (_sidebarPosition === pos && isAlreadyOrdered) return;
|
||||
_sidebarPosition = pos;
|
||||
|
||||
let scrollTop = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { nextTick } from "vue";
|
||||
|
||||
const drawWorkflowMinimap = vi.fn();
|
||||
const synthesizeWorkflowFromPromptGraph = vi.fn(() => null);
|
||||
@@ -281,4 +282,45 @@ describe("SidebarWorkflowSection", () => {
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it("shows a compact workflow category label for deep paths", async () => {
|
||||
const { default: SidebarWorkflowSection } =
|
||||
await import("../vue/components/panel/sidebar/SidebarWorkflowSection.vue");
|
||||
|
||||
const wrapper = mount(SidebarWorkflowSection, {
|
||||
props: {
|
||||
asset: {
|
||||
filepath: "F:/workflows/demo.json",
|
||||
category: "PROJECTS/PERSONAL/02_OUT/VIDEOS/260513/Atopi",
|
||||
has_generation_data: true,
|
||||
workflow: {
|
||||
nodes: [{ id: 1, pos: [0, 0], size: [180, 80], type: "KSampler" }],
|
||||
links: [],
|
||||
groups: [],
|
||||
extra: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
attachTo: document.body,
|
||||
global: {
|
||||
stubs: {
|
||||
MButton: MButtonStub,
|
||||
MTree: MTreeStub,
|
||||
},
|
||||
},
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
const categoryBlock = wrapper
|
||||
.findAll("div")
|
||||
.find((node) => node.text().includes("Category") && node.text().includes("Atopi"));
|
||||
|
||||
expect(categoryBlock?.text()).toContain("Atopi");
|
||||
expect(categoryBlock?.text()).not.toContain("PROJECTS");
|
||||
|
||||
const input = wrapper.find('input[placeholder="Workflow category"]');
|
||||
expect(input.element.value).toBe("PROJECTS/PERSONAL/02_OUT/VIDEOS/260513/Atopi");
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1287,8 +1287,11 @@
|
||||
}
|
||||
|
||||
.mjr-assets-manager .mjr-am-browse {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
border-top: 1px solid color-mix(in srgb, var(--mjr-border) 75%, transparent);
|
||||
padding-top: 4px;
|
||||
@@ -1296,6 +1299,8 @@
|
||||
|
||||
/* Scrollbars (match ComfyUI feel) */
|
||||
.mjr-assets-manager .mjr-am-grid-scroll {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
@@ -3714,6 +3719,9 @@
|
||||
|
||||
/* Sidebar (can be left or right) */
|
||||
.mjr-sidebar {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
flex: 0 0 auto;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "rating-change"]);
|
||||
@@ -51,6 +55,13 @@ const starColor = (starValue) => (starValue <= displayRating.value ? "#FFD45A" :
|
||||
|
||||
const starTransform = (starValue) => (starValue <= displayRating.value ? "scale(1.1)" : "scale(1)");
|
||||
|
||||
const starSize = computed(() => {
|
||||
const raw = props.size;
|
||||
if (typeof raw === "number") return `${Math.max(8, raw)}px`;
|
||||
const text = String(raw || "").trim();
|
||||
return text || "16px";
|
||||
});
|
||||
|
||||
const retryDelay = (attemptIndex) => Math.min(100 * 2 ** Math.max(0, attemptIndex - 1), 2000);
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
@@ -264,7 +275,7 @@ watch(
|
||||
:style="{
|
||||
color: starColor(i),
|
||||
transform: starTransform(i),
|
||||
fontSize: '16px',
|
||||
fontSize: starSize,
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: disabled ? 'default' : 'pointer',
|
||||
|
||||
@@ -35,6 +35,7 @@ const dataListRef = ref(null);
|
||||
const similarBtnRef = ref(null);
|
||||
const semanticBtnRef = ref(null);
|
||||
const dataListId = createUniqueId("mjr-search-autocomplete-", 8);
|
||||
const METADATA_SEARCH_MODE = "AND";
|
||||
|
||||
const resolveDomElement = (value) => value?.$el || value || null;
|
||||
const getSearchInputEl = () => resolveDomElement(searchInputRef.value);
|
||||
@@ -42,7 +43,7 @@ const getSearchInputEl = () => resolveDomElement(searchInputRef.value);
|
||||
// Local semantic mode state (synced with settings, not persisted)
|
||||
const semanticMode = ref(false);
|
||||
const semanticEnabled = ref(true);
|
||||
const metadataMode = ref(String(panelStore.metadataSearchMode || "AND").toUpperCase() === "OR" ? "OR" : "AND");
|
||||
const metadataMode = ref(METADATA_SEARCH_MODE);
|
||||
let metadataKeysCache = null;
|
||||
let metadataKeysPromise = null;
|
||||
|
||||
@@ -90,12 +91,6 @@ const semanticBtnStyle = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const metadataModeTitle = computed(() =>
|
||||
metadataMode.value === "OR"
|
||||
? t("search.metadataModeOr", "Metadata prefix terms match any tag (OR)")
|
||||
: t("search.metadataModeAnd", "Metadata prefix terms must all match (AND)"),
|
||||
);
|
||||
|
||||
const syncSemanticDataset = () => {
|
||||
try {
|
||||
const input = getSearchInputEl();
|
||||
@@ -133,24 +128,12 @@ const toggleSemanticMode = () => {
|
||||
const handleSearchInput = async (e) => {
|
||||
const value = e.target.value || "";
|
||||
panelStore.searchQuery = value;
|
||||
panelStore.metadataSearchMode = metadataMode.value;
|
||||
panelStore.metadataSearchMode = METADATA_SEARCH_MODE;
|
||||
syncSemanticDataset();
|
||||
emitSearchChange({ query: value });
|
||||
await handleAutocomplete();
|
||||
};
|
||||
|
||||
const toggleMetadataMode = () => {
|
||||
metadataMode.value = metadataMode.value === "OR" ? "AND" : "OR";
|
||||
panelStore.metadataSearchMode = metadataMode.value;
|
||||
syncSemanticDataset();
|
||||
emitSearchChange({ metadataMode: metadataMode.value });
|
||||
try {
|
||||
getSearchInputEl()?.dispatchEvent?.(new Event("input", { bubbles: true }));
|
||||
} catch (e) {
|
||||
console.debug?.(e);
|
||||
}
|
||||
};
|
||||
|
||||
const getLastSearchToken = (value) => {
|
||||
const parts = String(value || "").split(/\s+/);
|
||||
return parts[parts.length - 1] || "";
|
||||
@@ -261,8 +244,10 @@ watch(semanticMode, () => {
|
||||
watch(
|
||||
() => panelStore.metadataSearchMode,
|
||||
(newVal) => {
|
||||
const next = String(newVal || "AND").toUpperCase() === "OR" ? "OR" : "AND";
|
||||
if (metadataMode.value !== next) metadataMode.value = next;
|
||||
if (String(newVal || "").toUpperCase() !== METADATA_SEARCH_MODE) {
|
||||
panelStore.metadataSearchMode = METADATA_SEARCH_MODE;
|
||||
}
|
||||
if (metadataMode.value !== METADATA_SEARCH_MODE) metadataMode.value = METADATA_SEARCH_MODE;
|
||||
syncSemanticDataset();
|
||||
},
|
||||
);
|
||||
@@ -273,6 +258,7 @@ onMounted(() => {
|
||||
if (input) {
|
||||
input.value = panelStore.searchQuery || "";
|
||||
}
|
||||
panelStore.metadataSearchMode = METADATA_SEARCH_MODE;
|
||||
} catch (e) {
|
||||
console.debug?.(e);
|
||||
}
|
||||
@@ -319,20 +305,6 @@ defineExpose({
|
||||
<datalist ref="dataListRef" :id="dataListId" />
|
||||
</div>
|
||||
<div class="mjr-am-search-tools">
|
||||
<div class="mjr-popover-anchor">
|
||||
<MButton
|
||||
type="button"
|
||||
class="mjr-icon-btn mjr-ai-control"
|
||||
severity="secondary"
|
||||
text
|
||||
rounded
|
||||
:title="metadataModeTitle"
|
||||
:aria-label="metadataModeTitle"
|
||||
@click="toggleMetadataMode"
|
||||
>
|
||||
<span style="font-size:10px;font-weight:700;letter-spacing:0">{{ metadataMode }}</span>
|
||||
</MButton>
|
||||
</div>
|
||||
<div class="mjr-popover-anchor">
|
||||
<MButton
|
||||
ref="semanticBtnRef"
|
||||
|
||||
@@ -84,6 +84,7 @@ function handleClose() {
|
||||
<RatingEditor
|
||||
:asset="asset"
|
||||
:model-value="rating"
|
||||
:size="60"
|
||||
/>
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;gap:4px">
|
||||
|
||||
@@ -215,6 +215,20 @@ function modelGroupStyle(accentColor, emphasis = true) {
|
||||
};
|
||||
}
|
||||
|
||||
function modelBranchAccent(branch) {
|
||||
return "#CE6DE0";
|
||||
}
|
||||
|
||||
function samplingBranchAccent(branch) {
|
||||
const key = String(branch?.key || "").toLowerCase();
|
||||
if (key.includes("high")) return "#FFC107";
|
||||
if (key.includes("low")) return "#FFB300";
|
||||
if (key.includes("base")) return "#FF9800";
|
||||
if (key.includes("upscale") || key.includes("refine")) return "#FFCA28";
|
||||
if (key.includes("pass")) return "#FDD835";
|
||||
return "#FF9800";
|
||||
}
|
||||
|
||||
function modelGroupAccent(key) {
|
||||
if (key === "high_noise") return "#FF7043";
|
||||
if (key === "low_noise") return "#29B6F6";
|
||||
@@ -559,47 +573,6 @@ watch(
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="modelBranches.length"
|
||||
:style="boxStyle('#9C27B0', { emphasis: true, startAlpha: 0.18, endAlpha: 0.10 })"
|
||||
>
|
||||
<div style="font-size:11px;font-weight:600;color:#9C27B0;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:10px">
|
||||
{{ t("sidebar.generation.models", "Models") }}
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));gap:10px">
|
||||
<div
|
||||
v-for="branch in modelBranches"
|
||||
:key="`models-top-${branch.key}`"
|
||||
:style="modelGroupStyle(branch.accent, true)"
|
||||
>
|
||||
<div :style="{ fontSize: '10px', fontWeight: '800', color: branch.accent, letterSpacing: '0.6px', textTransform: 'uppercase' }">
|
||||
{{ branch.label }}
|
||||
</div>
|
||||
<div
|
||||
v-for="field in branch.modelFields"
|
||||
:key="`model-top-${branch.key}-${field.label}`"
|
||||
style="display:flex;flex-direction:column;gap:3px;min-width:0"
|
||||
>
|
||||
<span style="font-size:10px;font-weight:700;color:rgba(255,255,255,0.58);text-transform:uppercase;letter-spacing:0.4px">{{ field.label }}</span>
|
||||
<span style="font-size:12px;color:var(--fg-color, rgba(255,255,255,0.96));line-height:1.35;word-break:break-word;cursor:pointer" @click="copyText(field.value, $event.currentTarget)">
|
||||
{{ field.value || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="branch.loras.length" style="display:flex;flex-direction:column;gap:5px">
|
||||
<span style="font-size:10px;font-weight:700;color:rgba(255,255,255,0.58);text-transform:uppercase;letter-spacing:0.4px">LoRA</span>
|
||||
<span
|
||||
v-for="(lora, index) in branch.loras"
|
||||
:key="`model-top-${branch.key}-lora-${index}`"
|
||||
style="font-size:12px;color:var(--fg-color, rgba(255,255,255,0.92));line-height:1.35;word-break:break-word;padding:6px 8px;border-radius:6px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.08);cursor:pointer"
|
||||
@click="copyText(lora, $event.currentTarget)"
|
||||
>
|
||||
{{ lora }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isCaptionSectionVisible"
|
||||
style="background:linear-gradient(135deg, rgba(0, 188, 212, 0.14) 0%, rgba(33, 150, 243, 0.10) 100%);border:1px solid rgba(0, 188, 212, 0.40);border-radius:6px;padding:12px;display:flex;flex-direction:column;gap:10px"
|
||||
@@ -713,6 +686,47 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="modelBranches.length"
|
||||
:style="boxStyle('#9C27B0', { emphasis: true, startAlpha: 0.18, endAlpha: 0.10 })"
|
||||
>
|
||||
<div style="font-size:11px;font-weight:600;color:#9C27B0;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:10px">
|
||||
{{ t("sidebar.generation.models", "Models") }}
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(190px, 1fr));gap:10px">
|
||||
<div
|
||||
v-for="branch in modelBranches"
|
||||
:key="`models-top-${branch.key}`"
|
||||
:style="modelGroupStyle(modelBranchAccent(branch), true)"
|
||||
>
|
||||
<div :style="{ fontSize: '10px', fontWeight: '800', color: modelBranchAccent(branch), letterSpacing: '0.6px', textTransform: 'uppercase' }">
|
||||
{{ branch.label }}
|
||||
</div>
|
||||
<div
|
||||
v-for="field in branch.modelFields"
|
||||
:key="`model-top-${branch.key}-${field.label}`"
|
||||
style="display:flex;flex-direction:column;gap:3px;min-width:0"
|
||||
>
|
||||
<span style="font-size:10px;font-weight:700;color:rgba(255,255,255,0.58);text-transform:uppercase;letter-spacing:0.4px">{{ field.label }}</span>
|
||||
<span style="font-size:12px;color:var(--fg-color, rgba(255,255,255,0.96));line-height:1.35;word-break:break-word;cursor:pointer" @click="copyText(field.value, $event.currentTarget)">
|
||||
{{ field.value || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="branch.loras.length" style="display:flex;flex-direction:column;gap:5px">
|
||||
<span style="font-size:10px;font-weight:700;color:rgba(255,255,255,0.58);text-transform:uppercase;letter-spacing:0.4px">LoRA</span>
|
||||
<span
|
||||
v-for="(lora, index) in branch.loras"
|
||||
:key="`model-top-${branch.key}-lora-${index}`"
|
||||
style="font-size:12px;color:var(--fg-color, rgba(255,255,255,0.92));line-height:1.35;word-break:break-word;padding:6px 8px;border-radius:6px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.08);cursor:pointer"
|
||||
@click="copyText(lora, $event.currentTarget)"
|
||||
>
|
||||
{{ lora }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="sectionState.lyrics"
|
||||
:style="boxStyle('#00BCD4', { emphasis: false })"
|
||||
@@ -767,9 +781,9 @@ watch(
|
||||
<div
|
||||
v-for="branch in samplingBranches"
|
||||
:key="`sampling-${branch.key}`"
|
||||
:style="modelGroupStyle(branch.accent, true)"
|
||||
:style="modelGroupStyle(samplingBranchAccent(branch), true)"
|
||||
>
|
||||
<div :style="{ fontSize: '10px', fontWeight: '800', color: branch.accent, letterSpacing: '0.6px', textTransform: 'uppercase' }">
|
||||
<div :style="{ fontSize: '10px', fontWeight: '800', color: samplingBranchAccent(branch), letterSpacing: '0.6px', textTransform: 'uppercase' }">
|
||||
{{ branch.label }}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -302,6 +302,8 @@ const currentCategory = computed(() => {
|
||||
const categorySegments = computed(() =>
|
||||
currentCategory.value ? currentCategory.value.split(/[\\/]+/).filter(Boolean) : [],
|
||||
);
|
||||
const categoryDisplayName = computed(() => categorySegments.value.at(-1) || currentCategory.value || "Root");
|
||||
const categoryDisplaySegments = computed(() => categorySegments.value.slice(-1));
|
||||
|
||||
function workflowNodeLabel(node, index) {
|
||||
const id = node?.id ?? node?.key ?? index + 1;
|
||||
@@ -884,21 +886,25 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:12px;padding:10px;border-radius:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.10)">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px">
|
||||
<div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;min-width:0">
|
||||
<div style="min-width:0;flex:1 1 auto">
|
||||
<div style="font-size:10px;font-weight:700;color:rgba(255,255,255,0.55);text-transform:uppercase;letter-spacing:0.4px">Category</div>
|
||||
<div style="font-size:12px;color:rgba(255,255,255,0.8);margin-top:2px">
|
||||
{{ currentCategory || 'Root' }}
|
||||
<div
|
||||
:title="currentCategory || 'Root'"
|
||||
style="font-size:12px;color:rgba(255,255,255,0.8);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%"
|
||||
>
|
||||
{{ categoryDisplayName }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="categorySegments.length"
|
||||
style="display:flex;flex-wrap:wrap;gap:4px;justify-content:flex-end"
|
||||
v-if="categoryDisplaySegments.length"
|
||||
:title="currentCategory"
|
||||
style="display:flex;flex-wrap:wrap;gap:4px;justify-content:flex-end;min-width:0;max-width:45%"
|
||||
>
|
||||
<span
|
||||
v-for="segment in categorySegments"
|
||||
v-for="segment in categoryDisplaySegments"
|
||||
:key="segment"
|
||||
style="padding:3px 7px;border-radius:999px;background:rgba(33,150,243,0.12);border:1px solid rgba(33,150,243,0.22);font-size:10px;font-weight:700;color:#90CAF9;text-transform:uppercase;letter-spacing:0.3px"
|
||||
style="padding:3px 7px;border-radius:999px;background:rgba(33,150,243,0.12);border:1px solid rgba(33,150,243,0.22);font-size:10px;font-weight:700;color:#90CAF9;text-transform:uppercase;letter-spacing:0.3px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
|
||||
>
|
||||
{{ segment }}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user