Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 10,395 Bytes
65b0b93 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | """搜索卡与搜索参数区域的 NiceGUI 视图。"""
from typing import Any
from nicegui import ui
from platform_utils import nsfw_allowed
from webui.constants import SEARCH_MODE_OPTIONS
def build_search_panel(controller: Any) -> None:
with ui.card().classes('w-full product-search-card'):
with ui.row().classes('w-full items-start justify-between gap-3 mb-1'):
with ui.row().classes('items-center gap-2'):
ui.icon('search', size='2em', color='primary')
ui.label('Danbooru 标签模糊搜索').classes('text-2xl font-bold text-gray-800')
ui.button(
'帮助 / 关于', icon='help_outline', on_click=controller.help_dialog.open,
).props('flat dense no-caps color=grey-7').classes('text-sm')
ui.label(
'基于语义匹配的标签搜索引擎,支持多维匹配与共现关联推荐。'
).classes('text-sm text-gray-500 -mt-1 mb-1')
with ui.row().classes(
'w-full items-center justify-between gap-x-4 gap-y-1 flex-wrap mb-3'
):
ui.link(
'查看工具链介绍 / 使用指南 →',
'http://intro.sakizuki.site/index.html',
new_tab=True,
).classes('text-sm text-blue-600 hover:text-blue-800 font-medium')
ui.html(
'觉得好用?给 '
'<a href="https://huggingface.co/spaces/SAkizuki/DanbooruSearch">'
'Space 点个 Like ❤️</a>,或到 '
'<a href="https://github.com/SuzumiyaAkizuki/DanbooruSearchOnline">'
'GitHub 点 Star ⭐</a>'
).classes('homepage-support-note')
with ui.row().classes('w-full gap-3 items-stretch'):
controller.search_input = ui.textarea(
placeholder='输入自然语言描述或模糊概念,例如:一个穿着白色水手服的少女在雨中奔跑...'
).classes('flex-grow text-base motion-search-input').props('outlined rows=2')
controller.search_input.on('keydown.ctrl.enter', controller.perform_search)
with ui.column().classes('justify-center'):
controller.search_btn = ui.button(
'', on_click=controller.perform_search, icon='search'
).classes('px-6 h-full min-h-16 motion-search-button').props('unelevated color=dark')
with controller.search_btn:
ui.label('搜索').classes('text-sm mt-1')
controller.spinner = ui.spinner(size='2em').classes(
'hidden motion-search-spinner'
)
controller.search_params_row = ui.row().classes(
'w-full gap-6 items-center mt-3 flex-wrap'
)
with controller.search_params_row:
with ui.row().classes('items-center gap-2'):
ui.label('搜索模式 (beta)').classes('text-sm text-gray-600')
controller.input_search_mode = ui.select(
SEARCH_MODE_OPTIONS, value='自定义',
).classes('w-28').props('outlined dense')
controller.input_search_mode.on(
'update:model-value', controller._on_search_mode_change
)
with ui.tooltip().props('content-class="bg-black text-white shadow-4"'):
ui.label('选择模式自动填充对应参数;手动修改参数后自动变为「自定义」').style('font-size:14px;')
with ui.row().classes('items-center gap-2'):
ui.label('Top K (语义相关)').classes('text-sm text-gray-600')
controller.input_top_k = ui.number(value=10, min=1, max=200).classes('w-20') \
.props('outlined dense')
controller.input_top_k.on('update:model-value', controller._on_param_changed)
with ui.row().classes('items-center gap-2'):
ui.label('结果上限').classes('text-sm text-gray-600')
controller.input_limit = ui.number(value=80, min=10, max=500).classes('w-20') \
.props('outlined dense')
controller.input_limit.on('update:model-value', controller._on_param_changed)
with ui.switch('显示 NSFW(成人) 内容', value=False).props('color=red') as nsfw_switch:
if not nsfw_allowed():
with ui.tooltip().props('content-class="bg-black text-white shadow-4"'):
ui.label('NSFW 内容在当前平台不可用').style('font-size:14px;')
controller.input_nsfw = nsfw_switch
if not nsfw_allowed():
controller.input_nsfw.disable()
else:
controller.input_nsfw.on('update:model-value', controller.on_nsfw_toggle)
with ui.switch('智能分词', value=True).props('color=primary') as segment_switch:
with ui.tooltip().props('content-class="bg-black text-white shadow-4"'):
ui.label('关闭后系统将只匹配完整句子,适用于精准搜索整句。').style('font-size:14px;')
controller.input_segment = segment_switch
controller.input_segment.on('update:model-value', controller._on_param_changed)
controller.advanced_options = ui.expansion('高级选项', icon='tune').classes('w-full mt-2')
with controller.advanced_options:
with ui.column().classes('w-full p-3 gap-4'):
with ui.row().classes('items-center gap-2'):
ui.label('热度权重').classes('text-sm font-bold text-gray-700')
controller.input_weight = ui.slider(
min=0.0, max=1.0, value=0.15, step=0.05,
).classes('w-40')
ui.label().bind_text_from(
controller.input_weight, 'value', lambda value: f'{value:.2f}',
).classes('text-sm font-mono text-gray-700 w-8')
controller.input_weight.on('update:model-value', controller._on_param_changed)
with ui.row().classes('w-full gap-8 flex-wrap'):
with ui.column().classes('gap-2'):
ui.label('匹配层筛选').classes('font-bold text-sm text-gray-700')
display_map = {
'英文': '英文标签', '中文扩展词': '中文扩展词',
'释义': '维基释义', '中文核心词': '中文核心词',
'artist': 'artist',
}
for layer in ['英文', '中文扩展词', '释义', '中文核心词', 'artist']:
checkbox = ui.checkbox(
display_map.get(layer, layer), value=True,
on_change=lambda event, current=layer:
controller.selected_layers.__setitem__(current, event.value),
).props('color=primary dense')
controller._layer_checkboxes[layer] = checkbox
with ui.column().classes('gap-2'):
ui.label('类型筛选').classes('font-bold text-sm text-gray-700')
color_map = {'General': 'blue', 'Copyright': 'purple', 'Character': 'green'}
label_map = {
'General': '通用 (General)',
'Copyright': '作品 (Copyright)',
'Character': '角色 (Character)',
}
for category in ['General', 'Copyright', 'Character']:
checkbox = ui.checkbox(
label_map[category], value=True,
on_change=lambda event, current=category:
controller.selected_cats.__setitem__(current, event.value),
).props(f'color={color_map[category]} dense')
controller._cat_checkboxes[category] = checkbox
with ui.column().classes('gap-2'):
ui.label('表格显示列').classes('font-bold text-sm text-gray-700')
controller.sw_semantic = ui.switch('显示语义分', value=False)
controller.sw_layer = ui.switch('显示匹配层', value=False)
controller.sw_source = ui.switch('显示匹配来源', value=False)
controller.sw_semantic.on('update:model-value', controller._update_table_columns)
controller.sw_layer.on('update:model-value', controller._update_table_columns)
controller.sw_source.on('update:model-value', controller._update_table_columns)
with ui.column().classes('gap-2'):
ui.label('标签分组模式').classes('font-bold text-sm text-gray-700')
controller.input_group_mode = ui.select(
['off', 'expand', 'diverse'], value='off',
).classes('w-40').props('outlined dense')
with ui.tooltip().props('content-class="bg-black text-white shadow-4"'):
ui.label('off=关闭 | expand=同类召回增强 | diverse=多样性约束').style('font-size:14px;')
controller.input_group_mode.on(
'update:model-value', controller._on_param_changed
)
controller.input_max_per_group = ui.number(
value=2, min=1, max=10,
).classes('w-20').props('outlined dense')
ui.label('每组最大标签数(diverse 模式)').classes('text-xs text-gray-500')
controller.input_max_per_group.on(
'update:model-value', controller._on_param_changed
)
with ui.element('div').classes('w-full border-t border-slate-100 pt-3 mt-1'):
controller.service_status_container = ui.column().classes('w-full gap-0')
controller._update_service_status()
controller._start_service_status_task()
|