mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-13 04:42:13 -03:00
feat(settings): show default root cue in download root rules editor
Each rule group now shows which root is the type's default ('Default:
{root} — unmatched downloads go here'), and the default root's option in
the per-rule root dropdown carries a '(default)' suffix. Comparison is
deliberately loose (separators, trailing slashes, case) since this is
display-only. Changing a default root select in the Library section
re-renders the rules editor so the cue never goes stale.
This commit is contained in:
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "Download Root Rules",
|
"title": "Download Root Rules",
|
||||||
"help": "Route downloads to a different root folder based on the model's base model.",
|
"help": "Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "default",
|
||||||
"addRule": "Add rule",
|
"addRule": "Add rule",
|
||||||
"removeRule": "Remove rule",
|
"removeRule": "Remove rule",
|
||||||
"moveUp": "Move up",
|
"moveUp": "Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -499,6 +499,8 @@
|
|||||||
"title": "[TODO: Translate] Download Root Rules",
|
"title": "[TODO: Translate] Download Root Rules",
|
||||||
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
"help": "[TODO: Translate] Route downloads to a different root folder based on the model's base model.",
|
||||||
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
"description": "[TODO: Translate] Rules are checked in order. First match wins; unmatched downloads use the default root.",
|
||||||
|
"defaultHint": "[TODO: Translate] Default: {root} — unmatched downloads go here",
|
||||||
|
"defaultSuffix": "[TODO: Translate] default",
|
||||||
"addRule": "[TODO: Translate] Add rule",
|
"addRule": "[TODO: Translate] Add rule",
|
||||||
"removeRule": "[TODO: Translate] Remove rule",
|
"removeRule": "[TODO: Translate] Remove rule",
|
||||||
"moveUp": "[TODO: Translate] Move up",
|
"moveUp": "[TODO: Translate] Move up",
|
||||||
|
|||||||
@@ -686,6 +686,11 @@
|
|||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-root-rule-default-hint {
|
||||||
|
padding-top: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
.download-root-rule-panel {
|
.download-root-rule-panel {
|
||||||
margin-top: var(--space-1);
|
margin-top: var(--space-1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,20 @@ const DOWNLOAD_ROOT_RULE_ROOT_ENDPOINTS = {
|
|||||||
embeddings: '/api/lm/embeddings/roots',
|
embeddings: '/api/lm/embeddings/roots',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Rule type -> the settings key holding that type's default download root.
|
||||||
|
const DOWNLOAD_ROOT_RULE_DEFAULT_ROOT_SETTINGS = {
|
||||||
|
loras: 'default_lora_root',
|
||||||
|
checkpoints: 'default_checkpoint_root',
|
||||||
|
unet: 'default_unet_root',
|
||||||
|
embeddings: 'default_embedding_root',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setting keys whose change must refresh the download root rules editor
|
||||||
|
// (it shows which root is the default for each type).
|
||||||
|
const DOWNLOAD_ROOT_RULE_DEFAULT_SETTING_KEYS = new Set(
|
||||||
|
Object.values(DOWNLOAD_ROOT_RULE_DEFAULT_ROOT_SETTINGS)
|
||||||
|
);
|
||||||
|
|
||||||
export class SettingsManager {
|
export class SettingsManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
@@ -3257,6 +3271,12 @@ export class SettingsManager {
|
|||||||
// Apply frontend settings immediately
|
// Apply frontend settings immediately
|
||||||
this.applyFrontendSettings();
|
this.applyFrontendSettings();
|
||||||
|
|
||||||
|
// The download root rules editor marks each type's default root;
|
||||||
|
// refresh it when a default root select changes.
|
||||||
|
if (DOWNLOAD_ROOT_RULE_DEFAULT_SETTING_KEYS.has(settingKey)) {
|
||||||
|
this.renderDownloadRootRules();
|
||||||
|
}
|
||||||
|
|
||||||
// Recalculate layout when display density changes
|
// Recalculate layout when display density changes
|
||||||
if (settingKey === 'display_density' && state.virtualScroller) {
|
if (settingKey === 'display_density' && state.virtualScroller) {
|
||||||
state.virtualScroller.calculateLayout();
|
state.virtualScroller.calculateLayout();
|
||||||
@@ -4469,7 +4489,7 @@ export class SettingsManager {
|
|||||||
|
|
||||||
renderDownloadRootRules() {
|
renderDownloadRootRules() {
|
||||||
const container = document.getElementById('downloadRootRulesContainer');
|
const container = document.getElementById('downloadRootRulesContainer');
|
||||||
if (!container) {
|
if (!container || !this.downloadRootRuleRoots) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
@@ -4478,6 +4498,24 @@ export class SettingsManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDownloadRootRuleDefaultRoot(typeKey) {
|
||||||
|
const settingKey = DOWNLOAD_ROOT_RULE_DEFAULT_ROOT_SETTINGS[typeKey];
|
||||||
|
const value = settingKey ? state.global.settings[settingKey] : '';
|
||||||
|
return typeof value === 'string' ? value.trim() : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loose display-only comparison: separator, trailing slash and case
|
||||||
|
// differences must not hide the "(default)" cue.
|
||||||
|
isDownloadRootRuleDefaultRoot(root, defaultRoot) {
|
||||||
|
const normalize = (value) => String(value || '')
|
||||||
|
.replace(/\\/g, '/')
|
||||||
|
.replace(/\/+$/, '')
|
||||||
|
.toLowerCase();
|
||||||
|
const normalizedRoot = normalize(root);
|
||||||
|
const normalizedDefault = normalize(defaultRoot);
|
||||||
|
return !!normalizedRoot && !!normalizedDefault && normalizedRoot === normalizedDefault;
|
||||||
|
}
|
||||||
|
|
||||||
buildDownloadRootRuleGroup(typeKey) {
|
buildDownloadRootRuleGroup(typeKey) {
|
||||||
const group = document.createElement('div');
|
const group = document.createElement('div');
|
||||||
group.className = 'download-root-rule-group';
|
group.className = 'download-root-rule-group';
|
||||||
@@ -4496,6 +4534,18 @@ export class SettingsManager {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultRoot = this.getDownloadRootRuleDefaultRoot(typeKey);
|
||||||
|
if (defaultRoot) {
|
||||||
|
const defaultHint = document.createElement('div');
|
||||||
|
defaultHint.className = 'download-root-rules-hint download-root-rule-default-hint';
|
||||||
|
defaultHint.textContent = translate(
|
||||||
|
'settings.downloadRootRules.defaultHint',
|
||||||
|
{ root: defaultRoot },
|
||||||
|
`Default: ${defaultRoot} — unmatched downloads go here`
|
||||||
|
);
|
||||||
|
group.appendChild(defaultHint);
|
||||||
|
}
|
||||||
|
|
||||||
const rules = this.getDownloadRootRules()[typeKey];
|
const rules = this.getDownloadRootRules()[typeKey];
|
||||||
rules.forEach((rule, index) => {
|
rules.forEach((rule, index) => {
|
||||||
group.appendChild(this.buildDownloadRootRuleRow(typeKey, rule, index, false, roots));
|
group.appendChild(this.buildDownloadRootRuleRow(typeKey, rule, index, false, roots));
|
||||||
@@ -4548,10 +4598,13 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const rootSelect = document.createElement('select');
|
const rootSelect = document.createElement('select');
|
||||||
rootSelect.className = 'base-model-select download-root-rule-root';
|
rootSelect.className = 'base-model-select download-root-rule-root';
|
||||||
|
const defaultRoot = this.getDownloadRootRuleDefaultRoot(typeKey);
|
||||||
roots.forEach((root) => {
|
roots.forEach((root) => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = root;
|
option.value = root;
|
||||||
option.textContent = root;
|
option.textContent = this.isDownloadRootRuleDefaultRoot(root, defaultRoot)
|
||||||
|
? `${root} (${translate('settings.downloadRootRules.defaultSuffix', {}, 'default')})`
|
||||||
|
: root;
|
||||||
rootSelect.appendChild(option);
|
rootSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
if (roots.includes(rule.root)) {
|
if (roots.includes(rule.root)) {
|
||||||
|
|||||||
@@ -325,3 +325,107 @@ describe('SettingsManager download root rules', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('SettingsManager download root rules default-root cue', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
vi.clearAllMocks();
|
||||||
|
state.global.settings = {
|
||||||
|
download_root_rules: { loras: [], checkpoints: [], unet: [], embeddings: [] },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultHintFor = (typeIndex) =>
|
||||||
|
document.querySelectorAll('.download-root-rule-group')[typeIndex]
|
||||||
|
.querySelector('.download-root-rule-default-hint');
|
||||||
|
|
||||||
|
it('renders a default hint under the group header when a default root is set', () => {
|
||||||
|
appendDownloadRootRulesUi();
|
||||||
|
state.global.settings.default_lora_root = '/models/loras-main';
|
||||||
|
const manager = createManager();
|
||||||
|
|
||||||
|
manager.renderDownloadRootRules();
|
||||||
|
|
||||||
|
expect(defaultHintFor(0).textContent)
|
||||||
|
.toBe('Default: /models/loras-main — unmatched downloads go here');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits the default hint when the type has no roots or no default root', () => {
|
||||||
|
appendDownloadRootRulesUi();
|
||||||
|
state.global.settings.default_lora_root = '';
|
||||||
|
state.global.settings.default_embedding_root = '/models/embeddings';
|
||||||
|
const manager = createManager();
|
||||||
|
|
||||||
|
manager.renderDownloadRootRules();
|
||||||
|
|
||||||
|
// loras: default root is empty -> no hint
|
||||||
|
expect(defaultHintFor(0)).toBeNull();
|
||||||
|
// embeddings: no roots configured -> noRoots hint only, no default hint
|
||||||
|
expect(defaultHintFor(3)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks the default root option with a (default) suffix but keeps the plain value', () => {
|
||||||
|
appendDownloadRootRulesUi();
|
||||||
|
state.global.settings.default_lora_root = '/models/lora-archive';
|
||||||
|
state.global.settings.download_root_rules = {
|
||||||
|
loras: [{ match_type: 'base_model', match: ['Flux.1 D'], root: '/models/loras-main' }],
|
||||||
|
checkpoints: [],
|
||||||
|
unet: [],
|
||||||
|
embeddings: [],
|
||||||
|
};
|
||||||
|
const manager = createManager();
|
||||||
|
|
||||||
|
manager.renderDownloadRootRules();
|
||||||
|
|
||||||
|
const options = groupRows('loras')[0].querySelectorAll('select.download-root-rule-root option');
|
||||||
|
expect([...options].map((o) => o.value)).toEqual(['/models/loras-main', '/models/lora-archive']);
|
||||||
|
expect([...options].map((o) => o.textContent)).toEqual([
|
||||||
|
'/models/loras-main',
|
||||||
|
'/models/lora-archive (default)',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compares roots loosely: separators, trailing slashes and case are ignored', () => {
|
||||||
|
const manager = createManager();
|
||||||
|
|
||||||
|
expect(manager.isDownloadRootRuleDefaultRoot('C:\\Models\\Loras\\', 'c:/models/loras')).toBe(true);
|
||||||
|
expect(manager.isDownloadRootRuleDefaultRoot('/a/b/', '/a/b')).toBe(true);
|
||||||
|
expect(manager.isDownloadRootRuleDefaultRoot('/a/b', '/a/c')).toBe(false);
|
||||||
|
expect(manager.isDownloadRootRuleDefaultRoot('/a/b', '')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render groups before the roots have been fetched', () => {
|
||||||
|
appendDownloadRootRulesUi();
|
||||||
|
const manager = createManager();
|
||||||
|
manager.downloadRootRuleRoots = null;
|
||||||
|
|
||||||
|
manager.renderDownloadRootRules();
|
||||||
|
|
||||||
|
expect(document.querySelectorAll('.download-root-rule-group')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changing a default root select re-renders the rules editor with the new default', async () => {
|
||||||
|
appendDownloadRootRulesUi();
|
||||||
|
document.body.insertAdjacentHTML(
|
||||||
|
'beforeend',
|
||||||
|
'<select id="defaultLoraRoot"><option value="/models/loras-main">a</option><option value="/models/lora-archive" selected>b</option></select>'
|
||||||
|
);
|
||||||
|
state.global.settings.default_lora_root = '/models/loras-main';
|
||||||
|
const manager = createManager();
|
||||||
|
manager.applyFrontendSettings = vi.fn();
|
||||||
|
// Mimic the real saveSetting, which writes the value into state.
|
||||||
|
manager.saveSetting = vi.fn().mockImplementation(async (key, value) => {
|
||||||
|
state.global.settings[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
manager.renderDownloadRootRules();
|
||||||
|
expect(defaultHintFor(0).textContent)
|
||||||
|
.toBe('Default: /models/loras-main — unmatched downloads go here');
|
||||||
|
|
||||||
|
await manager.saveSelectSetting('defaultLoraRoot', 'default_lora_root');
|
||||||
|
|
||||||
|
expect(manager.saveSetting).toHaveBeenCalledWith('default_lora_root', '/models/lora-archive');
|
||||||
|
expect(defaultHintFor(0).textContent)
|
||||||
|
.toBe('Default: /models/lora-archive — unmatched downloads go here');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user