The list column showed every type's tree (raw folder names, [Root] rows with 0, nothing marking where you are), repeating the type chips above it. Now (ui_folder_list.js) it follows the chips: the type's heading with "📂 Open", All, its subfolders as a tree whose numbers count their subfolders (empty ones dimmed, folding per session in owner.collapsedFolders), and "In the top folder" for models directly in the type's folder; the listed one is marked. A Bilibili comment asked for quick ways to open model folders: POST /anomalous/open_model_folder without a file name now opens the folder itself (type root or subfolder; still by type, index and subfolder, never a path, local only). Tests in tests/test_model_folder.py. Gone: the every-type tree, Expand / Collapse all, expandedFolders and their locale keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
63 lines
2.4 KiB
Python
63 lines
2.4 KiB
Python
"""POST /anomalous/open_model_folder: a model file shown selected, or a models folder opened."""
|
|
import asyncio
|
|
import json
|
|
import shutil
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
PLUGIN = Path(__file__).resolve().parents[1]
|
|
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
|
|
|
|
from api import model_folder
|
|
|
|
|
|
class _Request:
|
|
def __init__(self, body):
|
|
self.body = body
|
|
|
|
async def json(self):
|
|
return self.body
|
|
|
|
|
|
def ask(body, root):
|
|
with mock.patch.object(model_folder, "resolve_folder_subdir",
|
|
side_effect=lambda kind, idx, sub: (root, model_folder.resolve_within(root, sub.strip("/")))), \
|
|
mock.patch.object(model_folder, "_reveal") as reveal, \
|
|
mock.patch.object(model_folder, "_open_folder") as open_folder:
|
|
response = asyncio.run(model_folder.api_open_model_folder(_Request(body)))
|
|
return response.status, json.loads(response.text), reveal, open_folder
|
|
|
|
|
|
class OpenModelFolderTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.root = os.path.realpath(tempfile.mkdtemp())
|
|
self.addCleanup(shutil.rmtree, self.root, True)
|
|
Path(self.root, "Anime").mkdir()
|
|
Path(self.root, "Anime", "a.safetensors").write_bytes(b"x")
|
|
|
|
def test_a_file_is_shown_selected(self):
|
|
status, _, reveal, open_folder = ask({"type": "loras", "subfolder": "/Anime", "filename": "a.safetensors"}, self.root)
|
|
self.assertEqual(status, 200)
|
|
reveal.assert_called_once_with(str(Path(self.root, "Anime", "a.safetensors")))
|
|
open_folder.assert_not_called()
|
|
|
|
def test_without_a_file_name_the_folder_opens(self):
|
|
status, _, reveal, open_folder = ask({"type": "loras", "subfolder": "/Anime"}, self.root)
|
|
self.assertEqual(status, 200)
|
|
open_folder.assert_called_once_with(str(Path(self.root, "Anime")))
|
|
reveal.assert_not_called()
|
|
|
|
def test_missing_and_outside_are_refused(self):
|
|
self.assertEqual(ask({"type": "loras", "subfolder": "/Nope"}, self.root)[0], 404)
|
|
self.assertEqual(ask({"type": "loras", "subfolder": "/Anime", "filename": "b.safetensors"}, self.root)[0], 404)
|
|
self.assertEqual(ask({"type": "loras", "subfolder": "/../.."}, self.root)[0], 400)
|
|
self.assertEqual(ask({"type": "loras", "subfolder": "/", "filename": "../x"}, self.root)[0], 400)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|