Unify local path joining
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
safeStorage,
|
||||
} from '../web/resolver/utils/html_utils.js';
|
||||
import { getModelCardUrl } from '../web/resolver/utils/url_utils.js';
|
||||
import { joinPathPreservingStyle } from '../web/resolver/utils/path_utils.js';
|
||||
import {
|
||||
classifyLocalMatches,
|
||||
matchesLocalModelDownload,
|
||||
@@ -4027,6 +4028,29 @@ test('portable subfolder paths use forward slashes and join to the host path sty
|
||||
);
|
||||
});
|
||||
|
||||
test('shared local path join preserves host separators and empty-base contracts', () => {
|
||||
assert.equal(
|
||||
joinPathPreservingStyle('/models/loras', 'Pony\\Styles'),
|
||||
'/models/loras/Pony/Styles'
|
||||
);
|
||||
assert.equal(
|
||||
joinPathPreservingStyle('C:\\models\\loras', 'Pony/Styles'),
|
||||
'C:\\models\\loras\\Pony\\Styles'
|
||||
);
|
||||
assert.equal(
|
||||
joinPathPreservingStyle('\\\\server\\share\\', 'Pony/Styles'),
|
||||
'\\\\server\\share\\Pony\\Styles'
|
||||
);
|
||||
assert.equal(
|
||||
joinPathPreservingStyle('', 'Pony\\Styles', { normalizeRelativeWhenBaseEmpty: true }),
|
||||
'Pony/Styles'
|
||||
);
|
||||
assert.equal(
|
||||
joinPathPreservingStyle('', 'Pony\\Styles'),
|
||||
'Pony\\Styles'
|
||||
);
|
||||
});
|
||||
|
||||
test('download subfolder and path template normalization apply the same segment rules', () => {
|
||||
const normalizeDownloadPathValue = eval(`(${extractMethod(downloadTargetMethodsSource, 'normalizeDownloadPathValue')})`);
|
||||
const dialog = {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createFloatingTreePicker } from "../utils/tree_picker.js";
|
||||
import { normalizeDownloadCategoryValue } from "../utils/category_utils.js";
|
||||
import { safeStorage } from "../utils/html_utils.js";
|
||||
import { getSha256Field } from "../utils/hash_utils.js";
|
||||
import { joinPathPreservingStyle } from "../utils/path_utils.js";
|
||||
import { matchesSearchText } from "../utils/search_utils.js";
|
||||
const localStorage = safeStorage;
|
||||
const invalidWindowsPathCharacters = new RegExp(
|
||||
@@ -953,19 +954,9 @@ export const downloadTargetMethods = {
|
||||
},
|
||||
|
||||
joinLocalPath(basePath = '', relativePath = '') {
|
||||
const rawBase = String(basePath || '');
|
||||
const relative = String(relativePath || '').replace(/^[/\\]+/, '');
|
||||
const usesBackslash = /^[A-Za-z]:\\/.test(rawBase)
|
||||
|| /^\\\\/.test(rawBase)
|
||||
|| (!rawBase.includes('/') && rawBase.includes('\\'));
|
||||
const separator = usesBackslash ? '\\' : '/';
|
||||
const base = rawBase.replace(usesBackslash ? /[/\\]+$/ : /\/+$/, '')
|
||||
|| (usesBackslash ? (/^\\+$/.test(rawBase) ? '\\' : '') : (/^\/+$/.test(rawBase) ? '/' : ''));
|
||||
if (!base) return this.normalizePathToForward(relative);
|
||||
if (!relative) return base;
|
||||
const normalizedRelative = relative.replace(/[/\\]+/g, separator);
|
||||
const joiner = base.endsWith(separator) ? '' : separator;
|
||||
return `${base}${joiner}${normalizedRelative}`;
|
||||
return joinPathPreservingStyle(basePath, relativePath, {
|
||||
normalizeRelativeWhenBaseEmpty: true,
|
||||
});
|
||||
},
|
||||
|
||||
getDownloadTargetFolderContext(category = '', subfolder = '', baseDirectory = '') {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export function joinPathPreservingStyle(
|
||||
basePath = '',
|
||||
relativePath = '',
|
||||
{ normalizeRelativeWhenBaseEmpty = false } = {},
|
||||
) {
|
||||
const rawBase = String(basePath || '');
|
||||
const relative = String(relativePath || '').replace(/^[/\\]+/, '');
|
||||
const usesBackslash = /^[A-Za-z]:\\/.test(rawBase)
|
||||
|| /^\\\\/.test(rawBase)
|
||||
|| (!rawBase.includes('/') && rawBase.includes('\\'));
|
||||
const separator = usesBackslash ? '\\' : '/';
|
||||
const base = rawBase.replace(usesBackslash ? /[/\\]+$/ : /\/+$/, '')
|
||||
|| (usesBackslash ? (/^\\+$/.test(rawBase) ? '\\' : '') : (/^\/+$/.test(rawBase) ? '/' : ''));
|
||||
if (!base) {
|
||||
return normalizeRelativeWhenBaseEmpty
|
||||
? relative.trim().replace(/\\/g, '/')
|
||||
: relative;
|
||||
}
|
||||
if (!relative) return base;
|
||||
const normalizedRelative = relative.replace(/[/\\]+/g, separator);
|
||||
const joiner = base.endsWith(separator) ? '' : separator;
|
||||
return `${base}${joiner}${normalizedRelative}`;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { getCivitaiModelUrl } from "../globals.js";
|
||||
import { safeStorage, normalizePathIdentity } from "../utils/html_utils.js";
|
||||
import { matchesWorkflowModelReference } from "../node_context_menu.js";
|
||||
import { classifyLocalMatches } from "../utils/local_match_utils.js";
|
||||
import { joinPathPreservingStyle } from "../utils/path_utils.js";
|
||||
|
||||
const MISSING_ROW_FALLBACK_HEIGHT = 70;
|
||||
const MISSING_VIRTUAL_INITIAL_ROWS = 24;
|
||||
@@ -1874,23 +1875,6 @@ export const missingBrowserMethods = {
|
||||
return Array.from(byIdentity.values());
|
||||
};
|
||||
|
||||
const joinLocalModelPath = (basePath = '', relativePath = '') => {
|
||||
if (typeof this.joinLocalPath === 'function') {
|
||||
return this.joinLocalPath(basePath, relativePath);
|
||||
}
|
||||
const rawBase = String(basePath || '');
|
||||
const relative = String(relativePath || '').replace(/^[/\\]+/, '');
|
||||
const usesBackslash = /^[A-Za-z]:\\/.test(rawBase)
|
||||
|| /^\\\\/.test(rawBase)
|
||||
|| (!rawBase.includes('/') && rawBase.includes('\\'));
|
||||
const separator = usesBackslash ? '\\' : '/';
|
||||
const base = rawBase.replace(usesBackslash ? /[/\\]+$/ : /\/+$/, '')
|
||||
|| (usesBackslash ? (/^\\+$/.test(rawBase) ? '\\' : '') : (/^\/+$/.test(rawBase) ? '/' : ''));
|
||||
if (!base) return relative;
|
||||
if (!relative) return base;
|
||||
const joiner = base.endsWith(separator) ? '' : separator;
|
||||
return `${base}${joiner}${relative.replace(/[/\\]+/g, separator)}`;
|
||||
};
|
||||
const buildLocalFolderContext = (folderPath = '', name = 'Folder', category = '') => {
|
||||
const path = String(folderPath || '').trim();
|
||||
if (!path) return null;
|
||||
@@ -1911,7 +1895,7 @@ export const missingBrowserMethods = {
|
||||
const path = entry.fullPath || model.path || model.resolved_path || '';
|
||||
if (!path) return null;
|
||||
const directory = entry.folderPath
|
||||
? joinLocalModelPath(entry.baseDirectory, entry.folderPath)
|
||||
? joinPathPreservingStyle(entry.baseDirectory, entry.folderPath)
|
||||
: entry.baseDirectory;
|
||||
return {
|
||||
...model,
|
||||
@@ -2037,7 +2021,7 @@ export const missingBrowserMethods = {
|
||||
const modelCount = countTreeModels(folder);
|
||||
const shouldExpand = Boolean(filter) || expandedSet.has(stateKey);
|
||||
const folderFullPath = group.baseDirectory
|
||||
? joinLocalModelPath(group.baseDirectory, folder.path)
|
||||
? joinPathPreservingStyle(group.baseDirectory, folder.path)
|
||||
: '';
|
||||
const context = buildLocalFolderContext(folderFullPath, folder.path, group.category);
|
||||
const contextAttrs = getLocalPathContextAttrs(context, folderFullPath);
|
||||
|
||||
Reference in New Issue
Block a user