Files
LittleWhiteBox/modules/story-summary/story-summary.html

605 lines
35 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- story-summary.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>剧情总结 · Story Summary</title>
<link rel="stylesheet" href="story-summary.css">
</head>
<body>
<div class="container">
<!-- Header -->
<header>
<div>
<h1>剧情<span>总结</span></h1>
<div class="subtitle">Story Summary · Timeline · Character Arcs</div>
</div>
<div class="stats">
<div class="stat">
<div class="stat-val" id="stat-events">0</div>
<div class="stat-lbl">已记录事件</div>
</div>
<div class="stat">
<div class="stat-val" id="stat-summarized">0</div>
<div class="stat-lbl">已总结楼层</div>
</div>
<div class="stat">
<div class="stat-val"><span class="hl" id="stat-pending">0</span></div>
<div class="stat-lbl">待总结</div>
<div class="stat-warning hidden" id="pending-warning">再删1条将回滚</div>
</div>
</div>
</header>
<!-- Controls -->
<div class="controls">
<label class="chk-label">
<input type="checkbox" id="hide-summarized">
<span>隐藏已总结 · <strong id="summarized-count">0</strong> 楼(保留<input type="number" id="keep-visible-count"
min="0" max="50" value="3">楼)</span>
</label>
<span class="spacer"></span>
<div class="btn-group">
<button class="btn btn-icon" id="btn-settings">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3" />
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
</svg>
<span>设置</span>
</button>
<button class="btn" id="btn-clear">清空</button>
<button class="btn btn-p" id="btn-generate">总结</button>
</div>
</div>
<!-- Main Content -->
<main>
<div class="left">
<!-- Keywords -->
<section class="card">
<div class="sec-head">
<div class="sec-title">核心关键词</div>
<button class="sec-btn" data-section="keywords">编辑</button>
</div>
<div class="keywords" id="keywords-cloud"></div>
</section>
<!-- Timeline -->
<section class="card timeline">
<div class="sec-head">
<div class="sec-title">剧情时间线</div>
<button class="sec-btn" data-section="events">编辑</button>
</div>
<div class="tl-list scroll" id="timeline-list"></div>
</section>
</div>
<div class="right">
<!-- World State -->
<section class="card world-state">
<div class="sec-head">
<div class="sec-title">世界状态</div>
<button class="sec-btn" data-section="world">编辑</button>
</div>
<div class="world-state-list scroll" id="world-state-list"></div>
</section>
<!-- Relations -->
<section class="card relations">
<div class="sec-head">
<div class="sec-title">人物关系</div>
<div class="sec-actions">
<button class="sec-btn sec-icon" id="btn-fullscreen-relations" title="全屏查看">
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor"
stroke-width="2">
<path
d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
</svg>
</button>
<button class="sec-btn" data-section="characters">编辑</button>
</div>
</div>
<div id="relation-chart"></div>
</section>
<!-- Profile -->
<section class="card profile">
<div class="sec-head">
<div class="sec-title">人物档案</div>
<div class="sec-actions">
<div class="custom-select" id="char-sel">
<div class="sel-trigger" id="char-sel-trigger">
<span id="sel-char-text">选择角色</span>
</div>
<div class="sel-opts" id="char-sel-opts">
<div class="sel-opt" data-value="">暂无角色</div>
</div>
</div>
<button class="sec-btn" data-section="arcs">编辑</button>
</div>
</div>
<div class="profile-content scroll" id="profile-content"></div>
</section>
</div>
</main>
</div>
<!-- Editor Modal -->
<div class="modal" id="editor-modal">
<div class="modal-bg" id="editor-backdrop"></div>
<div class="modal-box">
<div class="modal-head">
<h2 id="editor-title">编辑</h2>
<button class="modal-close" id="editor-close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="modal-body">
<div class="editor-hint" id="editor-hint"></div>
<div id="editor-struct" class="hidden"></div>
<textarea class="editor-ta" id="editor-ta"></textarea>
<div class="editor-err" id="editor-err"></div>
</div>
<div class="modal-foot">
<button class="btn" id="editor-cancel">取消</button>
<button class="btn btn-p" id="editor-save">保存</button>
</div>
</div>
</div>
<!-- Settings Modal -->
<div class="modal" id="settings-modal">
<div class="modal-bg" id="settings-backdrop"></div>
<div class="modal-box settings-modal-box">
<div class="modal-head">
<div class="settings-tabs">
<div class="settings-tab active" data-tab="tab-summary">总结设置</div>
<div class="settings-tab" data-tab="tab-vector">向量设置</div>
<div class="settings-tab" data-tab="tab-debug">调试</div>
</div>
<button class="modal-close" id="settings-close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="modal-body">
<!-- Tab 1: Summary Settings -->
<div class="tab-pane active" id="tab-summary">
<!-- API Config & Gen Params Combined -->
<div class="settings-section">
<div class="settings-section-title">API 配置</div>
<div class="settings-row">
<div class="settings-field">
<label>渠道</label>
<select id="api-provider">
<option value="st">酒馆主 API沿用当前</option>
<option value="openai">OpenAI 兼容</option>
<option value="google">Google (Gemini)</option>
<option value="claude">Claude (Anthropic)</option>
<option value="custom">自定义</option>
</select>
</div>
</div>
<div class="settings-row hidden" id="api-url-row">
<div class="settings-field full">
<label>API URL</label>
<input type="text" id="api-url" placeholder="https://api.openai.com 或代理地址">
<div class="settings-hint">默认端点OpenAI/v1Gemini/v1betaClaude/v1</div>
</div>
</div>
<div class="settings-row hidden" id="api-key-row">
<div class="settings-field full">
<label>API KEY</label>
<input type="password" id="api-key" placeholder="仅保存在本地,不会上传">
</div>
</div>
<div class="settings-row hidden" id="api-model-manual-row">
<div class="settings-field full">
<label>模型</label>
<input type="text" id="api-model-text" placeholder="如 gemini-1.5-pro、claude-3-haiku">
</div>
</div>
<div class="settings-row hidden" id="api-model-select-row">
<div class="settings-field full">
<label>可用模型</label>
<select id="api-model-select">
<option value="">请先拉取模型列表</option>
</select>
</div>
</div>
<div class="settings-btn-row hidden" id="api-connect-row"
style="display: flex; gap: 12px; align-items: center; justify-content: space-between;">
<button class="btn btn-sm btn-p" id="btn-connect" style="flex: 4;">连接 / 拉取模型列表</button>
<label class="chk-label compact"
style="margin: 0; flex: 1; display: flex; align-items: center; gap: 6px; white-space: nowrap; justify-content: center;">
<input type="checkbox" id="trigger-stream" checked>
<span>流式</span>
</label>
</div>
<!-- Collapsible Gen Params -->
<div class="settings-collapse">
<div class="settings-collapse-header" id="gen-params-toggle">
<span>生成参数</span>
<svg class="collapse-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</div>
<div class="settings-collapse-content hidden" id="gen-params-content">
<div class="settings-row">
<div class="settings-field">
<label>Temperature</label>
<input type="number" id="gen-temp" step="0.01" min="0" max="2"
placeholder="未设置">
</div>
<div class="settings-field">
<label>Top P</label>
<input type="number" id="gen-top-p" step="0.01" min="0" max="1"
placeholder="未设置">
</div>
<div class="settings-field">
<label>Top K</label>
<input type="number" id="gen-top-k" step="1" min="1" placeholder="未设置">
</div>
</div>
<div class="settings-row">
<div class="settings-field">
<label>存在惩罚</label>
<input type="number" id="gen-presence" step="0.01" min="-2" max="2"
placeholder="未设置">
</div>
<div class="settings-field">
<label>频率惩罚</label>
<input type="number" id="gen-frequency" step="0.01" min="-2" max="2"
placeholder="未设置">
</div>
</div>
</div>
</div>
</div>
<!-- Trigger Settings -->
<div class="settings-section">
<div class="settings-section-title">总结设置</div>
<div class="settings-row">
<div class="settings-field">
<label>注入角色</label>
<select id="trigger-role">
<option value="system">System</option>
<option value="user">User</option>
<option value="assistant">Assistant</option>
</select>
</div>
<div class="settings-field">
<label>单次最大总结(楼)</label>
<select id="trigger-max-per-run">
<option value="50">50</option>
<option value="100" selected>100</option>
<option value="150">150</option>
<option value="200">200</option>
</select>
</div>
</div>
<!-- Auto Summary with sub-options -->
<div class="settings-checkbox-group">
<label class="settings-checkbox">
<input type="checkbox" id="trigger-enabled">
<span class="checkbox-mark"></span>
<span class="checkbox-label">启用自动总结</span>
</label>
<div class="settings-sub-options hidden" id="auto-summary-options">
<div class="settings-row">
<div class="settings-field">
<label>自动总结间隔(楼)</label>
<input type="number" id="trigger-interval" min="1" max="30" step="1" value="20">
</div>
<div class="settings-field">
<label>触发时机</label>
<select id="trigger-timing">
<option value="after_ai">AI 回复后</option>
<option value="before_user" selected>用户发送前</option>
<option value="manual">仅手动</option>
</select>
</div>
</div>
</div>
</div>
<!-- Force Insert with wrapper options -->
<div class="settings-checkbox-group">
<label class="settings-checkbox">
<input type="checkbox" id="trigger-insert-at-end">
<span class="checkbox-mark"></span>
<span class="checkbox-label">强制插入到聊天最后(插件冲突用)</span>
</label>
<div class="settings-sub-options hidden" id="insert-wrapper-options">
<div class="settings-row">
<div class="settings-field full">
<label>头部包裹词</label>
<input type="text" id="trigger-wrapper-head" placeholder="添加到开头应对NoAss配置">
</div>
</div>
<div class="settings-row">
<div class="settings-field full">
<label>尾部包裹词</label>
<input type="text" id="trigger-wrapper-tail" placeholder="添加到结尾应对NoAss配置">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tab 2: Vector Settings -->
<div class="tab-pane" id="tab-vector">
<div class="settings-section">
<div class="settings-section-title">智能记忆(向量检索)</div>
<div class="settings-checkbox-group">
<label class="settings-checkbox">
<input type="checkbox" id="vector-enabled">
<span class="checkbox-mark"></span>
<span class="checkbox-label">启用向量检索</span>
</label>
</div>
<div id="vector-config-area" class="hidden">
<div class="settings-row" style="margin-top:16px">
<div class="settings-field full">
<label>Embedding 引擎</label>
<div class="engine-selector">
<label class="engine-option">
<input type="radio" name="vector-engine" value="local">
<span>本地模型</span>
</label>
<label class="engine-option">
<input type="radio" name="vector-engine" value="online" checked>
<span>在线服务</span>
</label>
</div>
</div>
</div>
<!-- Local Engine -->
<div id="local-engine-area" class="engine-area hidden">
<div class="model-select-row">
<select id="local-model-select">
<option value="bge-small-zh">中文轻量 (51MB)</option>
<option value="bge-base-zh">中文标准 (102MB)</option>
<option value="e5-small">多语言 (118MB)</option>
</select>
</div>
<div class="model-desc" id="local-model-desc">手机/低配适用</div>
<div class="engine-status-row">
<div class="engine-status" id="local-model-status">
<span class="status-dot"></span>
<span class="status-text">检查中...</span>
</div>
<div class="engine-actions" id="local-model-actions">
<button class="btn btn-sm btn-p" id="btn-download-model">下载</button>
<button class="btn btn-sm" id="btn-cancel-download"
style="display:none">取消</button>
<button class="btn btn-sm btn-del" id="btn-delete-model"
style="display:none">删除</button>
</div>
</div>
<div class="engine-progress hidden" id="local-model-progress" style="margin-top: 8px;">
<div class="progress-bar">
<div class="progress-inner"></div>
</div>
<span class="progress-text">0%</span>
</div>
</div>
<!-- Online Engine -->
<div id="online-engine-area" class="engine-area">
<div class="settings-row">
<div class="settings-field full">
<label>服务渠道</label>
<select id="online-provider">
<option value="siliconflow">硅基流动(推荐)</option>
<option value="cohere">Cohere</option>
<option value="openai">OpenAI 兼容(可自建)</option>
</select>
</div>
</div>
<div class="settings-row" id="online-url-row">
<div class="settings-field full">
<label>API URL</label>
<input type="text" id="vector-api-url" placeholder="https://api.siliconflow.cn">
</div>
</div>
<div class="settings-row">
<div class="settings-field full">
<label>API Key</label>
<input type="password" id="vector-api-key" placeholder="sk-xxx">
</div>
</div>
<div class="settings-row">
<div class="settings-field full">
<label>模型</label>
<div style="display:flex;gap:8px">
<select id="vector-model-select" style="flex:1">
<option value="">请选择模型</option>
</select>
<button class="btn btn-sm" id="btn-fetch-models"
style="display:none">拉取</button>
</div>
</div>
</div>
<div class="engine-status-row">
<div class="engine-status" id="online-api-status">
<span class="status-dot"></span>
<span class="status-text">未测试</span>
</div>
<button class="btn btn-sm" id="btn-test-vector-api">测试连接</button>
</div>
<div class="provider-hint" id="provider-hint">
💡 <a href="https://siliconflow.cn" target="_blank">硅基流动</a> 免费、速度快、质量好,推荐
BAAI/bge-m3
</div>
</div>
<!-- 文本过滤规则 - Redesigned for mobile -->
<div class="filter-rules-section">
<div class="filter-rules-header">
<label>文本过滤规则</label>
<button class="btn btn-sm btn-add" id="btn-add-filter-rule">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none"
stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
添加
</button>
</div>
<p class="settings-hint">过滤干扰内容(如思考标签):遇到「起始」跳过直到「结束」</p>
<div id="filter-rules-list" class="filter-rules-list"></div>
</div>
<!-- Vector Stats -->
<div class="vector-chat-section">
<div class="settings-row">
<div class="settings-field full">
<label>当前聊天向量</label>
<div class="vector-stats" id="vector-stats">
<div class="vector-stat-col">
<span class="vector-stat-label">事件向量:</span>
<span class="vector-stat-value"><strong
id="vector-event-count">0</strong>/<strong
id="vector-event-total">0</strong></span>
</div>
<span class="vector-stat-sep">·</span>
<div class="vector-stat-col">
<span class="vector-stat-label">Chunks:</span>
<span class="vector-stat-value"><strong
id="vector-chunk-count">0</strong>
个(<span id="vector-chunk-floors">0</span>/<span
id="vector-chunk-total">0</span> 层)</span>
</div>
<span class="vector-stat-sep">·</span>
<div class="vector-stat-col">
<span class="vector-stat-label">消息:</span>
<span class="vector-stat-value"><strong
id="vector-message-count">0</strong></span>
</div>
</div>
<div class="vector-mismatch-warning hidden" id="vector-mismatch-warning">
⚠ 引擎/模型已变更,需重新生成向量
</div>
</div>
</div>
<div class="engine-progress hidden" id="vector-gen-progress-l1">
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L1 片段</div>
<div class="progress-bar">
<div class="progress-inner"></div>
</div>
<span class="progress-text">0/0</span>
</div>
<div class="engine-progress hidden" id="vector-gen-progress-l2">
<div style="font-size:.75rem;color:var(--txt3);margin-bottom:4px">L2 事件</div>
<div class="progress-bar">
<div class="progress-inner"></div>
</div>
<span class="progress-text">0/0</span>
</div>
<div class="settings-hint" id="vector-perf-l1"></div>
<div class="settings-hint" id="vector-perf-l2"></div>
<div class="settings-btn-row" id="vector-action-row">
<button class="btn btn-sm btn-p" id="btn-gen-vectors">生成向量</button>
<button class="btn btn-sm btn-del" id="btn-clear-vectors">清除向量</button>
<button class="btn btn-sm hidden" id="btn-cancel-vectors">取消</button>
</div>
<div class="settings-hint" style="margin-top:8px">首次生成向量可能耗时较久,页面短暂卡顿属正常。若本地模型重进酒馆后需重下。
</div>
<!-- 向量导入导出 -->
<div class="vector-io-section">
<div class="settings-row">
<div class="settings-field full">
<label>向量迁移(跨设备 / 防清缓存)</label>
<div class="settings-hint" style="margin-bottom:8px">导出/导入均为 zip 格式,勿解压
</div>
<div class="settings-btn-row" id="vector-io-row" style="margin-top:8px">
<button class="btn btn-sm" id="btn-export-vectors">导出向量</button>
<button class="btn btn-sm" id="btn-import-vectors">导入向量</button>
</div>
<div class="settings-hint" id="vector-io-status"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tab 3: Debug -->
<div class="tab-pane" id="tab-debug">
<div class="debug-log-header">
<div class="debug-title">🔧 记忆召回日志</div>
<div class="settings-hint">显示最近一次 AI 生成时的向量检索详情</div>
</div>
<pre id="recall-log-content" class="debug-log-viewer"></pre>
</div>
</div>
<div class="modal-foot">
<button class="btn" id="settings-cancel">取消</button>
<button class="btn btn-p" id="settings-save">保存</button>
</div>
</div>
</div>
<!-- Fullscreen Relations Modal -->
<div class="modal fullscreen" id="rel-fs-modal">
<div class="modal-bg" id="rel-fs-backdrop"></div>
<div class="modal-box">
<div class="modal-head">
<h2>人物关系图</h2>
<button class="modal-close" id="rel-fs-close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="modal-body">
<div id="relation-chart-fullscreen"></div>
</div>
</div>
</div>
<!-- HF Guide Modal -->
<div class="modal" id="hf-guide-modal">
<div class="modal-bg" id="hf-guide-backdrop"></div>
<div class="modal-box" style="max-width:900px">
<div class="modal-head">
<h2>🤗 Hugging Face Space 部署指南</h2>
<button class="modal-close" id="hf-guide-close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="modal-body" id="hf-guide-body"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
<script src="story-summary-ui.js"></script>
</body>
</html>