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

3041 lines
142 KiB
HTML
Raw Normal View History

2026-01-17 16:34:39 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>小白板</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<style>
2026-02-02 00:00:53 +08:00
* {
margin: 0;
padding: 0;
box-sizing: border-box
}
:root {
--bg: #FDFDF9;
--bg2: #FFFFFF;
--bg3: #F0F0EE;
--c: #1C1917;
--c2: #57534E;
--c3: #78716C;
--bd: #E5E5E4;
--r4: 8px;
--r6: 12px;
--r8: 16px;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
--accent: #6366f1;
--accent2: #8b5cf6;
--glass: rgba(255, 255, 255, 0.8);
--glass-border: rgba(255, 255, 255, 0.5)
}
html.dark {
--bg: #0f0f12;
--bg2: #18181b;
--bg3: #27272a;
--c: #fafafa;
--c2: #a1a1aa;
--c3: #71717a;
--bd: #3f3f46;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
--shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.3);
--accent: #818cf8;
--accent2: #a78bfa;
--glass: rgba(24, 24, 27, 0.85);
--glass-border: rgba(63, 63, 70, 0.6)
}
html,
body {
transition: background .3s, color .3s
}
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: var(--bg);
color: var(--c)
}
.fc,
.fcc {
display: flex;
align-items: center
}
.fcc {
justify-content: center
}
.g4 {
gap: 4px
}
.g6 {
gap: 6px
}
.g10 {
gap: 10px
}
.g20 {
gap: 20px
}
.p12 {
padding: 12px
}
.r6 {
border-radius: var(--r6)
}
.bd {
border: 1px solid var(--bd)
}
.bg3 {
background: var(--bg3)
}
.fs12 {
font-size: 12px
}
.fw6 {
font-weight: 600
}
.c2 {
color: var(--c2)
}
.usn {
user-select: none
}
.f1 {
flex: 1
}
.jsb {
justify-content: space-between
}
.mt6 {
margin-top: 6px
}
.mt8 {
margin-top: 8px
}
.mt10 {
margin-top: 10px
}
.mt12 {
margin-top: 12px
}
.mt16 {
margin-top: 16px
}
.mb4 {
margin-bottom: 4px
}
.mb8 {
margin-bottom: 8px
}
.mb10 {
margin-bottom: 10px
}
.mb12 {
margin-bottom: 12px
}
.lh15 {
line-height: 1.5
}
.lh165 {
line-height: 1.65
}
.w100 {
width: 100px
}
.w140 {
width: 140px
}
.btn {
padding: 8px 16px;
background: var(--bg2);
font-size: 13px;
font-weight: 500;
cursor: pointer;
border: 1px solid var(--bd);
border-radius: var(--r4);
color: var(--c);
transition: all .2s;
box-shadow: var(--shadow-sm);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px
}
.btn:hover {
background: #fafafa;
border-color: #dcdcdc;
box-shadow: var(--shadow);
transform: translateY(-1px)
}
.btn:active {
transform: translateY(0)
}
.btn:disabled {
opacity: .5;
cursor: not-allowed;
box-shadow: none;
transform: none
}
.btn-p {
background: var(--c);
color: #fff;
border-color: var(--c)
}
.btn-p:hover {
background: #2a2a28;
color: #fff;
border-color: #2a2a28
}
.btn-due {
background: #ffe1e1 !important;
border-color: #ff9b9b !important;
color: #7a1f1f !important
}
.btn-s {
padding: 6px 12px;
font-size: 12px
}
.btn-c,
.btn-add {
padding: 0;
border-radius: 50%;
flex-shrink: 0
}
.btn-c {
width: 32px;
height: 32px;
background: var(--bg3);
border-color: var(--bd);
color: var(--c2)
}
.btn-c:hover {
background: #e7e5e4;
color: var(--c)
}
.btn-add {
width: 36px;
height: 36px;
background: var(--bg2);
color: var(--c);
box-shadow: var(--shadow-sm)
}
.fold {
background: var(--bg2);
border: 1px solid var(--bd);
border-radius: var(--r6);
margin-bottom: 8px;
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: box-shadow .2s
}
.fold:hover {
box-shadow: var(--shadow)
}
.fold-h {
padding: 12px 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center
}
.fold-a {
color: var(--c3);
font-size: 10px;
transition: transform .15s
}
.fold.exp .fold-a {
transform: rotate(180deg)
}
.fold-b {
max-height: 0;
overflow: hidden;
transition: max-height .2s cubic-bezier(0, 1, 0, 1)
}
.fold.exp .fold-b {
max-height: 500px;
transition: max-height .3s ease-in-out
}
.side-nav-wrap {
position: fixed;
left: 12px;
top: 50%;
transform: translateY(-50%);
z-index: 500;
display: flex;
flex-direction: column;
gap: 8px
}
.side-glass {
background: rgba(255, 255, 255, .8);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.5);
box-shadow: var(--shadow)
}
.side-nav,
.side-menu {
display: flex;
flex-direction: column;
border-radius: 24px
}
.side-nav {
gap: 6px;
padding: 8px 6px
}
.side-menu {
position: relative;
padding: 6px 6px;
align-items: center
}
.side-menu-btn,
.nav-i {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: var(--c3);
transition: all .2s
}
.side-menu-btn {
width: 32px;
height: 32px;
font-size: 12px;
border-radius: 10px
}
.nav-i {
width: 36px;
height: 36px;
border-radius: 50%
}
.nav-i i {
font-size: 14px
}
.side-menu-btn:hover,
.nav-i:hover {
background: var(--bg3);
color: var(--c)
}
.side-menu-btn.act,
.nav-i.act {
background: var(--c);
color: #fff;
box-shadow: var(--shadow-sm)
}
.side-menu-panel {
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
margin-left: 12px;
display: none;
flex-direction: column;
gap: 6px;
padding: 8px;
background: var(--bg2);
border-radius: var(--r6);
border: 1px solid var(--bd);
white-space: nowrap;
z-index: 600;
box-shadow: var(--shadow)
}
.side-menu-panel.show {
display: flex
}
.side-menu-panel .btn {
font-size: 11px;
padding: 6px 12px;
width: 100%;
justify-content: flex-start
}
.toolbar {
height: 52px;
background: var(--bg2);
border-bottom: 1px solid var(--bd);
padding: 0 16px 0 64px;
position: relative;
z-index: 200;
box-shadow: var(--shadow-sm)
}
.toolbar-t {
font-size: 16px;
font-weight: 700;
margin-right: auto;
letter-spacing: -0.5px;
color: var(--c)
}
.toolbar-t span {
font-weight: 400;
color: var(--c3);
margin-left: 8px;
font-size: 12px;
letter-spacing: 0
}
.main-wrap {
width: 100%;
height: calc(100% - 52px);
position: relative
}
.page {
position: absolute;
inset: 0;
background: var(--bg);
display: none;
overflow: hidden
}
.page.act {
display: block
}
.page-pad {
padding: 24px 24px 24px 72px;
overflow-y: auto;
height: 100%
}
.banner {
width: 100%;
height: 180px;
border-radius: var(--r8);
overflow: hidden;
margin-bottom: 24px;
position: relative;
background: var(--bg3);
box-shadow: var(--shadow-sm)
}
.banner img {
width: 100%;
height: 100%;
object-fit: cover
}
.banner-ov {
position: absolute;
inset: 0;
background: linear-gradient(transparent 40%, rgba(0, 0, 0, .6));
display: flex;
align-items: flex-end;
padding: 20px
}
.banner-ov div {
color: #fff;
font-size: 14px;
font-weight: 500;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
}
.sec-t {
font-size: 13px;
color: var(--c3);
margin-bottom: 12px;
font-weight: 600;
letter-spacing: 0.5px;
text-transform: uppercase
}
.sec-hd {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px
}
.sec-hd .sec-t {
margin-bottom: 0
}
.news-sec {
margin-bottom: 32px
}
.news-t {
font-size: 14px;
font-weight: 600;
color: var(--c)
}
.news-time {
font-size: 11px;
color: var(--c3)
}
.fold.exp .news-b {
padding: 0 16px 16px
}
.news-b p {
font-size: 13px;
color: var(--c2);
line-height: 1.7
}
.user-guide {
padding: 20px;
background: var(--bg2);
border-radius: var(--r6);
border: 1px solid var(--bd);
margin-bottom: 24px;
box-shadow: var(--shadow-sm)
}
.user-guide-state {
font-size: 14px;
font-weight: 500;
margin-bottom: 12px;
color: var(--c)
}
.user-guide-actions {
display: flex;
flex-direction: column;
gap: 8px
}
.user-guide-action {
padding: 10px 14px;
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r4);
font-size: 13px;
color: var(--c2);
cursor: pointer;
transition: all .2s
}
.user-guide-action:hover {
background: var(--bg2);
border-color: var(--c3)
}
#mapWrap {
width: 100%;
height: 100%;
background: var(--bg);
cursor: grab;
overflow: hidden;
position: relative;
touch-action: none
}
#inner,
#lines {
position: absolute;
width: 4000px;
height: 4000px;
transform-origin: 0 0
}
#lines {
pointer-events: none
}
.item {
position: absolute;
padding: 8px 16px;
background: var(--bg2);
border: 1px solid var(--bd);
border-radius: 20px;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
cursor: pointer;
user-select: none;
box-shadow: var(--shadow-sm);
color: var(--c);
transition: transform .1s, border-color .1s
}
.item.node-main {
background: var(--c);
color: #fff;
border-color: var(--c);
z-index: 10
}
.item.node-home {
background: #fff;
color: var(--c);
border: 2px solid var(--c);
z-index: 11
}
.item.node-sub {
background: var(--bg2)
}
.item:hover {
transform: scale(1.05);
z-index: 20
}
.item.hl {
border-color: var(--c);
box-shadow: 0 0 0 3px rgba(68, 64, 60, 0.2);
z-index: 20
}
.map-act {
position: absolute;
top: 16px;
right: 16px;
z-index: 100
}
#btn-goto {
display: none;
background: var(--c);
color: #fff;
border: none;
box-shadow: var(--shadow)
}
#btn-goto.show {
display: flex
}
.map-lbl {
position: absolute;
top: 16px;
left: 72px;
z-index: 100;
background: var(--bg2);
border: 1px solid var(--bd);
border-radius: 20px;
padding: 8px 16px;
font-size: 12px;
color: var(--c);
cursor: pointer;
display: flex;
align-items: center;
box-shadow: var(--shadow-sm)
}
.map-lbl i:first-child {
margin-right: 8px;
color: var(--c3)
}
.map-lbl .fa-chevron-down {
margin-left: 8px;
font-size: 9px;
color: var(--c3)
}
.map-lbl-sel {
position: absolute;
opacity: 0;
cursor: pointer;
inset: 0;
border: none;
background: transparent;
-webkit-appearance: none;
appearance: none
}
.panel {
position: fixed;
background: var(--bg2);
padding: 12px 16px;
border-radius: var(--r6);
font-size: 12px;
color: var(--c2);
border: 1px solid var(--bd);
box-shadow: var(--shadow)
}
#zoom-ind {
bottom: 20px;
right: 20px;
font-family: monospace
}
#tip {
bottom: 20px;
left: 72px;
max-width: 280px;
line-height: 1.6;
max-height: 220px;
overflow-y: auto;
display: none
}
#tip .desc {
display: none
}
#tip .info-w {
display: block
}
#tip.show {
display: block
}
.loc-lk {
color: var(--c);
font-weight: 600;
cursor: pointer;
border-bottom: 1px dashed var(--c3);
transition: color .2s
}
.loc-lk:hover {
color: #000;
border-bottom-style: solid
}
.local-map-title {
font-size: 15px;
font-weight: 700;
color: var(--c);
margin-bottom: 12px;
padding-bottom: 10px;
border-bottom: 1px solid var(--bd)
}
.info-h {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px
}
.info-t {
font-weight: 700;
color: var(--c);
font-size: 14px
}
.info-bk {
font-size: 11px;
color: var(--c3);
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
background: var(--bg3)
}
.info-c {
color: var(--c2);
font-size: 13px;
line-height: 1.6
}
.comm-hd {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px
}
.comm-tabs {
display: flex;
gap: 4px;
background: var(--bg3);
padding: 4px;
border-radius: var(--r6)
}
.comm-tab {
padding: 8px 20px;
text-align: center;
border-radius: var(--r4);
cursor: pointer;
font-size: 12px;
font-weight: 600;
color: var(--c3);
transition: all .2s;
border: none
}
.comm-tab:first-child {
border-radius: var(--r4)
}
.comm-tab:last-child {
border-radius: var(--r4);
border-left: none
}
.comm-tab.act {
background: var(--bg2);
color: var(--c);
box-shadow: var(--shadow-sm)
}
.comm-sec {
display: none
}
.comm-sec.act {
display: block
}
.ct-hd {
gap: 12px
}
.ct-av,
.chat-av {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 600;
font-size: 14px;
flex-shrink: 0;
box-shadow: var(--shadow-sm)
}
.ct-info {
flex: 1;
min-width: 0
}
.ct-name {
font-size: 14px;
font-weight: 600;
color: var(--c)
}
.ct-st {
font-size: 12px;
color: var(--c3);
margin-top: 2px
}
.ct-det {
padding: 0 16px 16px
}
.ct-info-text {
font-size: 13px;
color: var(--c2);
line-height: 1.7;
margin: 0 0 16px;
background: var(--bg);
padding: 10px;
border-radius: var(--r4)
}
.ct-acts {
display: flex;
gap: 10px
}
.ct-acts .btn {
flex: 1;
justify-content: center;
font-size: 12px;
height: 32px
}
.empty {
text-align: center;
padding: 60px 20px;
color: var(--c3);
font-size: 13px;
background: var(--bg3);
border-radius: var(--r6);
border: 1px dashed var(--bd)
}
.modal {
position: fixed;
inset: 0;
z-index: 10000;
display: none;
justify-content: center;
align-items: center;
backdrop-filter: blur(2px);
transition: all .3s
}
.modal.act {
display: flex
}
.modal-bd {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, .15)
}
.modal-p {
position: relative;
width: 90%;
max-width: 700px;
max-height: 85vh;
background: var(--bg2);
border: 1px solid var(--bd);
border-radius: var(--r8);
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-lg)
}
.modal-p.sm {
max-width: 400px
}
.modal-p.lg {
max-width: 800px
}
.modal-hd,
.modal-ft {
padding: 16px 20px;
background: var(--bg2)
}
.modal-hd {
justify-content: space-between;
border-bottom: 1px solid var(--bd)
}
.modal-hd h2 {
font-size: 16px;
font-weight: 700;
color: var(--c)
}
.modal-ft {
justify-content: flex-end;
gap: 12px;
border-top: 1px solid var(--bd);
background: #fafaf9
}
.modal-x {
width: 32px;
height: 32px;
background: transparent;
cursor: pointer;
border: none;
border-radius: 50%;
color: var(--c3);
font-size: 16px
}
.modal-x:hover {
background: var(--bg3);
color: var(--c)
}
.modal-by {
flex: 1;
overflow-y: auto;
padding: 24px
}
.form-g {
margin-bottom: 20px
}
.form-l {
display: block;
font-size: 12px;
font-weight: 600;
margin-bottom: 8px;
color: var(--c2);
text-transform: uppercase;
letter-spacing: 0.5px
}
.form-in {
width: 100%;
padding: 10px 14px;
border: 1px solid var(--bd);
border-radius: var(--r4);
font-size: 14px;
outline: none;
background: var(--bg);
color: var(--c);
transition: all .2s;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.03)
}
.form-in:focus {
background: #fff;
border-color: var(--c3);
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.05)
}
.form-ta {
min-height: 100px;
resize: vertical;
font-family: inherit;
line-height: 1.6
}
.goto-d {
font-size: 14px;
color: var(--c);
font-weight: 500;
padding: 12px 16px;
background: var(--bg3);
border-radius: var(--r4);
margin-bottom: 16px;
border: 1px solid var(--bd)
}
.ed-ta {
width: 100%;
min-height: 120px;
padding: 16px;
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r4);
font-family: "SF Mono", Monaco, Consolas, monospace;
font-size: 12px;
line-height: 1.6;
color: var(--c);
resize: vertical;
outline: 0;
overflow: auto
}
.ed-ta:focus {
background: #fff;
border-color: var(--c3)
}
.ed-preview {
margin-top: 12px;
padding: 16px;
background: var(--bg3);
border: 1px solid var(--bd);
border-radius: var(--r4);
font-size: 11px;
font-family: "SF Mono", Monaco, Consolas, monospace;
white-space: pre-wrap;
display: none;
color: var(--c2)
}
.ed-err {
padding: 12px;
background: #fef2f2;
border: 1px solid #fecaca;
border-radius: var(--r4);
color: #b91c1c;
font-size: 13px;
margin-top: 12px;
display: none
}
.ed-err.vis {
display: block
}
.chat {
position: fixed;
top: 0;
right: -420px;
width: 380px;
height: 100%;
background: var(--bg2);
border-left: 1px solid var(--bd);
z-index: 600;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-lg);
transition: right .3s cubic-bezier(0.16, 1, 0.3, 1)
}
.chat.act {
right: 0
}
.chat-hd {
padding: 16px 20px;
border-bottom: 1px solid var(--bd);
display: flex;
align-items: center;
gap: 14px;
flex-shrink: 0
}
.chat-t {
flex: 1;
min-width: 0
}
.chat-nm {
font-size: 15px;
font-weight: 700;
color: var(--c)
}
.chat-st {
font-size: 12px;
color: var(--c3);
margin-top: 2px
}
.chat-hd-acts {
display: flex;
align-items: center;
gap: 6px
}
.chat-compress,
.chat-clr,
.chat-x {
width: 36px;
height: 36px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: var(--c3);
transition: background .2s
}
.chat-compress:hover,
.chat-clr:hover,
.chat-x:hover {
background: var(--bg3);
color: var(--c)
}
.chat-compress:disabled {
opacity: .4;
cursor: not-allowed
}
.chat-divider {
width: 100%;
text-align: center;
padding: 12px 0;
color: var(--c3);
font-size: 11px;
position: relative
}
.chat-divider:before,
.chat-divider:after {
content: "";
position: absolute;
top: 50%;
width: 30%;
height: 1px;
background: var(--bd)
}
.chat-divider:before {
left: 0
}
.chat-divider:after {
right: 0
}
.chat-msgs {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
background: var(--bg)
}
.chat-msg {
max-width: 85%;
padding: 12px 16px;
border-radius: 18px;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
box-shadow: var(--shadow-sm)
}
.chat-msg.sent {
align-self: flex-end;
background: var(--c);
color: #fff;
border-bottom-right-radius: 4px
}
.chat-msg.recv {
align-self: flex-start;
background: var(--bg2);
border-bottom-left-radius: 4px;
border: 1px solid var(--bd);
color: var(--c)
}
.chat-msg.typing {
font-style: italic;
opacity: .7;
background: transparent;
box-shadow: none;
border: none;
padding: 0
}
.chat-in-w {
padding: 16px;
border-top: 1px solid var(--bd);
display: flex;
gap: 12px;
flex-shrink: 0;
background: var(--bg2)
}
.chat-in {
flex: 1;
padding: 12px 16px;
border: 1px solid var(--bd);
border-radius: 24px;
font-size: 14px;
outline: none;
background: var(--bg);
transition: all .2s
}
.chat-in:focus {
border-color: var(--c);
background: #fff;
box-shadow: var(--shadow-sm)
}
.chat-in:disabled,
.chat-send:disabled {
opacity: .5;
cursor: not-allowed
}
.chat-send {
width: 40px;
height: 40px;
border: none;
background: var(--c);
color: #fff;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-sm);
transition: transform .2s
}
.chat-send:hover {
transform: scale(1.05)
}
.chat-emp {
text-align: center;
color: var(--c3);
font-size: 13px;
padding: 60px 20px
}
.loc-list {
max-height: 300px;
overflow-y: auto
}
.loc-i {
padding: 14px 16px;
border: 1px solid var(--bd);
border-radius: var(--r6);
margin-bottom: 10px;
cursor: pointer;
background: var(--bg2);
transition: all .2s
}
.loc-i:hover {
border-color: var(--c3);
transform: translateY(-1px)
}
.loc-i.sel {
border-color: var(--c);
background: var(--c);
color: #fff;
box-shadow: var(--shadow)
}
.loc-i.sel .loc-i-info {
color: rgba(255, 255, 255, 0.8)
}
.loc-i-nm {
font-size: 14px;
font-weight: 600
}
.loc-i-info {
font-size: 12px;
opacity: .7;
margin-top: 4px
}
.mob-pop {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--bg2);
border-top: 1px solid var(--bd);
border-radius: 20px 20px 0 0;
z-index: 101;
display: none;
flex-direction: column;
box-shadow: var(--shadow-lg)
}
.mob-pop.act {
display: flex
}
.pop-hd {
padding: 12px;
cursor: grab;
touch-action: none;
flex-shrink: 0
}
.pop-handle {
width: 40px;
height: 5px;
background: var(--bd);
border-radius: 3px;
margin: 0 auto
}
.pop-ct {
flex: 1;
overflow-y: auto;
padding: 0 24px 24px;
-webkit-overflow-scrolling: touch
}
.pop-desc {
display: none
}
.pop-info {
display: block
}
.pop-info-h {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px
}
.pop-info-t {
font-weight: 700;
font-size: 16px;
color: var(--c)
}
.pop-info-bk {
font-size: 12px;
color: var(--c3);
cursor: pointer;
padding: 6px 12px;
border-radius: var(--r4);
background: var(--bg3)
}
.pop-h-ind {
position: absolute;
top: 50%;
right: 8px;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 4px;
opacity: 0
}
.mob-pop.drag .pop-h-ind {
opacity: 1
}
.pop-h-ind span {
width: 4px;
height: 10px;
background: var(--bd);
border-radius: 2px
}
.pop-h-ind span.act {
background: var(--c)
}
.side-pop {
position: fixed;
top: 44px;
right: 0;
bottom: 0;
background: var(--bg2);
border-left: 1px solid var(--bd);
z-index: 90;
display: none;
width: 12px;
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden
}
.side-pop.show {
display: flex
}
.side-pop.act {
width: 332px;
box-shadow: var(--shadow-lg)
}
.side-pop-handle {
width: 12px;
background: var(--bg3);
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
touch-action: none;
border-right: 1px solid var(--bd);
transition: background 0.2s
}
.side-pop-handle:hover {
background: var(--bd)
}
.side-pop-bar {
width: 3px;
height: 36px;
background: var(--bd);
border-radius: 2px
}
.side-pop-ct {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 16px;
margin-bottom: 30vh;
min-width: 0
}
.side-pop-hd {
font-size: 11px;
color: var(--c3);
margin-bottom: 10px
}
.side-pop-desc {
font-size: 13px;
color: var(--c2);
line-height: 1.7;
margin-top: 2em
}
.set-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px
}
.set-row .form-in {
flex: 1
}
.set-row .btn {
flex-shrink: 0
}
.set-hint {
font-size: 11px;
color: var(--c3);
margin-top: 4px
}
.set-test {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px
}
.set-test-res {
font-size: 12px;
padding: 6px 10px;
border-radius: var(--r4);
display: none
}
.set-test-res.ok {
display: block;
background: #dcfce7;
color: #166534;
border: 1px solid #86efac
}
.set-test-res.err {
display: block;
background: #fef2f2;
color: #b91c1c;
border: 1px solid #fecaca
}
.warn {
color: #f80
}
.settings-body {
height: 480px;
overflow-y: auto;
background: var(--bg2);
padding: 24px
}
.settings-nav {
display: flex;
align-items: center;
gap: 4px;
background: var(--bg3);
padding: 4px;
border-radius: var(--r6);
height: 40px
}
.set-nav-item {
padding: 0 16px;
height: 32px;
background: transparent;
border: none;
border-radius: var(--r4);
cursor: pointer;
font-size: 13px;
font-weight: 500;
color: var(--c3);
display: flex;
align-items: center;
gap: 8px;
transition: all .2s;
white-space: nowrap
}
.set-nav-item:hover {
color: var(--c2)
}
.set-nav-item.act {
background: var(--bg2);
color: var(--c);
box-shadow: var(--shadow-sm);
font-weight: 600
}
.set-tab-page {
display: none;
animation: fadeIn .3s
}
.set-tab-page.act {
display: block
}
.set-group {
background: #fff;
border: 1px solid var(--bd);
border-radius: var(--r6);
padding: 20px;
margin-bottom: 20px;
box-shadow: var(--shadow-sm)
}
.set-group-t {
font-size: 14px;
font-weight: 700;
color: var(--c);
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 10px;
border-bottom: 1px dashed var(--bd)
}
.tips-box {
background: var(--bg3);
padding: 10px 12px;
border-radius: var(--r4);
font-size: 12px;
color: var(--c2);
line-height: 1.5;
margin-top: 8px
}
.btn.w100 {
width: 100%
}
#data-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 12px
}
.data-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r6);
cursor: pointer;
transition: all .2s;
position: relative;
min-height: 60px
}
.data-item:hover {
border-color: var(--c3);
transform: translateY(-1px);
box-shadow: var(--shadow-sm)
}
.data-item.sel {
background: #fff;
border-color: var(--c);
box-shadow: 0 0 0 1px var(--c) inset
}
.data-ck {
width: 20px;
height: 20px;
border: 1px solid var(--bd);
background: var(--bg3);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
color: transparent;
font-size: 10px;
flex-shrink: 0
}
.data-item.sel .data-ck {
background: var(--c);
border-color: var(--c);
color: #fff
}
.data-info {
flex: 1;
min-width: 0
}
.data-nm {
font-size: 13px;
font-weight: 600;
color: var(--c);
margin-bottom: 2px
}
.data-desc {
font-size: 11px;
color: var(--c3);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis
}
.data-edit {
width: 28px;
height: 28px;
border-radius: 4px;
border: 1px solid transparent;
background: transparent;
color: var(--c3);
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
transition: all .2s;
cursor: pointer
}
.data-edit:hover {
background: var(--bg3);
color: var(--c);
border-color: var(--bd)
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(4px)
}
to {
opacity: 1;
transform: translateY(0)
}
}
@media (max-width:600px) {
.modal-p.lg {
width: 100%;
height: 100%;
max-height: 100%;
border-radius: 0
}
.settings-body {
height: auto;
flex: 1;
padding: 16px
}
.modal-hd {
padding: 10px 16px
}
.set-nav-item {
padding: 0 10px;
font-size: 12px
}
.set-nav-item i {
display: none
}
#data-list {
grid-template-columns: 1fr
}
.set-row {
flex-wrap: wrap
}
.mt-mob-8 {
margin-top: 8px
}
}
.data-item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px;
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r6);
margin-bottom: 8px;
cursor: pointer
}
.data-item.sel {
border-color: var(--c);
background: rgba(34, 34, 34, .05)
}
.data-ck {
width: 18px;
height: 18px;
border: 2px solid var(--bd);
border-radius: var(--r4);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 2px
}
.data-item.sel .data-ck {
background: var(--c);
border-color: var(--c);
color: #fff
}
.data-ck i {
font-size: 10px;
opacity: 0
}
.data-item.sel .data-ck i {
opacity: 1
}
.data-info {
flex: 1;
min-width: 0
}
.data-nm {
font-size: 13px;
font-weight: 500;
margin-bottom: 4px
}
.data-desc {
font-size: 11px;
color: var(--c3);
line-height: 1.4
}
.data-edit {
width: 28px;
height: 28px;
border: 1px solid var(--bd);
border-radius: var(--r4);
background: var(--bg2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--c3)
}
#set-model-list {
display: none;
margin-top: 8px
}
#world-gen-status,
#world-sim-status {
display: none;
color: #4a9
}
#adv-u1,
#adv-a1,
#adv-u2,
#adv-a2 {
height: 85px
}
#adv-json {
min-height: 140px
}
#data-edit-ta {
min-height: 300px
}
#res-msg {
margin-bottom: 10px;
line-height: 1.5
}
#res-record-box {
display: none
}
#res-action {
display: none
}
#res-record {
max-height: 200px;
overflow-y: auto;
background: var(--bg3);
padding: 8px;
border-radius: var(--r4);
font-size: 12px;
white-space: pre-wrap;
word-break: break-all
}
.adv-modal .modal-p {
max-width: 900px;
height: 80vh;
max-height: 800px;
display: flex;
flex-direction: column;
padding: 0;
background: var(--bg2);
border-radius: var(--r8);
box-shadow: var(--shadow-lg);
border: 1px solid var(--bd)
}
.adv-modal .modal-hd {
padding: 16px 24px;
border-bottom: 1px solid var(--bd);
background: var(--bg2);
flex-shrink: 0
}
.adv-modal .modal-hd h2 {
font-size: 18px;
font-weight: 700;
color: var(--c);
letter-spacing: -0.5px
}
.adv-layout {
display: flex;
flex: 1;
overflow: hidden;
height: 100%
}
.adv-sidebar {
width: 260px;
background: var(--bg3);
border-right: 1px solid var(--bd);
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 24px;
flex-shrink: 0
}
.adv-sidebar .form-g {
margin-bottom: 0
}
.adv-sidebar .set-hint {
font-size: 12px;
color: var(--c3);
line-height: 1.6
}
.adv-main {
flex: 1;
padding: 24px 32px;
overflow-y: auto;
background: var(--bg2);
display: flex;
flex-direction: column;
gap: 32px;
min-width: 0
}
.adv-var-group-title {
font-size: 12px;
font-weight: 700;
color: var(--c2);
text-transform: uppercase;
letter-spacing: 0.5px;
margin: 16px 0 8px 0;
padding-bottom: 4px;
border-bottom: 1px solid var(--bd)
}
.adv-var-item {
font-size: 12px;
color: var(--c2);
padding: 4px 0;
display: flex;
flex-direction: column;
gap: 2px
}
.adv-var-item code {
font-family: "SF Mono", Monaco, Consolas, monospace;
background: rgba(0, 0, 0, 0.06);
padding: 2px 5px;
border-radius: 4px;
color: var(--c);
font-size: 11px;
align-self: flex-start;
border: 1px solid rgba(0, 0, 0, 0.05)
}
.adv-var-desc {
color: var(--c3);
font-size: 11px;
line-height: 1.4;
padding-left: 2px
}
.adv-section {
background: var(--bg);
border: 1px solid var(--bd);
border-radius: var(--r6);
padding: 20px;
box-shadow: var(--shadow-sm)
}
.adv-h-title {
font-size: 14px;
font-weight: 700;
color: var(--c);
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px
}
.adv-h-title::before {
content: '';
width: 4px;
height: 16px;
background: var(--c);
border-radius: 2px;
display: block
}
.uaua-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px
}
.ed-ta-adv {
font-family: "SF Mono", Monaco, Consolas, monospace;
font-size: 12px;
line-height: 1.5;
padding: 12px;
border: 1px solid var(--bd);
border-radius: var(--r4);
background: #fff;
width: 100%;
resize: vertical;
min-height: 120px;
transition: border-color 0.2s;
outline: none
}
.ed-ta-adv:focus {
border-color: var(--c);
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05)
}
.adv-tag-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 12px
}
.adv-tag {
font-size: 10px;
padding: 4px 8px;
background: var(--bg2);
border: 1px solid var(--bd);
border-radius: 12px;
color: var(--c2);
cursor: pointer;
transition: all 0.2s;
user-select: none
}
.adv-tag:hover {
border-color: var(--c3);
color: var(--c);
background: #fff
}
@media(max-width:768px) {
.adv-layout {
flex-direction: column;
overflow-y: auto
}
.adv-sidebar {
width: 100%;
height: auto;
border-right: none;
border-bottom: 1px solid var(--bd);
padding: 16px
}
.adv-main {
overflow: visible;
padding: 16px
}
.uaua-grid {
grid-template-columns: 1fr
}
}
@media(max-width:550px) {
.chat {
width: 100%;
right: -100%
}
.side-pop {
bottom: 0
}
.side-nav-wrap {
left: 6px
}
.side-nav {
padding: 6px 4px
}
.side-menu {
padding: 5px 4px
}
.nav-i {
width: 28px;
height: 28px
}
.nav-i i {
font-size: 11px
}
.toolbar {
padding: 0 12px 0 56px
}
.toolbar-t span {
display: none
}
.btn {
padding: 6px 10px;
font-size: 11px
}
.modal-p {
max-width: 100%;
max-height: 100%;
border-radius: 0
}
#tip {
display: none !important
}
.page-pad {
padding: 14px 14px 14px 48px
}
.map-lbl {
left: 48px
}
}
/* 600px 响应式布局 */
@media (max-width: 600px) {
.modal-hd {
padding: 10px 16px;
display: flex;
justify-content: flex-end
}
}
/* 主题切换按钮样式 */
.theme-toggle {
width: 36px;
height: 36px;
border-radius: 50%;
border: 1px solid var(--bd);
background: var(--bg3);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--c2);
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden
}
.theme-toggle:hover {
background: var(--bd);
color: var(--c);
transform: scale(1.05)
}
.theme-toggle i {
font-size: 14px;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1)
}
.theme-toggle .fa-sun {
position: absolute;
opacity: 0;
transform: rotate(-90deg) scale(0)
}
.theme-toggle .fa-moon {
opacity: 1;
transform: rotate(0) scale(1)
}
html.dark .theme-toggle .fa-sun {
opacity: 1;
transform: rotate(0) scale(1)
}
html.dark .theme-toggle .fa-moon {
opacity: 0;
transform: rotate(90deg) scale(0)
}
/* 暗色模式额外样式修复 */
/* 按钮样式修复 */
html.dark .btn:hover {
background: var(--bg3);
border-color: var(--c3)
}
html.dark .btn-c:hover {
background: var(--bg3);
color: var(--c)
}
html.dark .btn-p {
background: var(--accent);
border-color: var(--accent);
color: #fff
}
html.dark .btn-p:hover {
background: var(--accent2);
border-color: var(--accent2);
color: #fff
}
html.dark .modal-ft {
background: var(--bg3)
}
/* 输入框样式修复 */
html.dark .form-in {
background: var(--bg);
color: var(--c)
}
html.dark .form-in:focus {
background: var(--bg2);
box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.2)
}
html.dark .ed-ta:focus,
html.dark .ed-ta-adv:focus {
background: var(--bg);
box-shadow: 0 0 0 2px rgba(129, 140, 248, 0.2)
}
html.dark .ed-ta-adv {
background: var(--bg)
}
html.dark .set-group {
background: var(--bg2)
}
/* 聊天界面修复 */
html.dark .chat-in {
background: var(--bg);
color: var(--c)
}
html.dark .chat-in:focus {
background: var(--bg2);
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.2)
}
html.dark .chat-send {
background: var(--accent);
color: #fff
}
html.dark .chat-send:hover {
background: var(--accent2)
}
html.dark .chat-msg.sent {
background: var(--accent);
color: #fff
}
html.dark .chat-msg.recv {
background: var(--bg2);
border-color: var(--bd);
color: var(--c)
}
html.dark .chat-av {
border: 2px solid var(--accent)
}
/* 地图节点修复 */
html.dark .item {
background: var(--bg2);
border-color: var(--bd);
color: var(--c)
}
html.dark .item.node-main {
background: var(--accent);
border-color: var(--accent);
color: #fff
}
html.dark .item.node-home {
background: var(--bg);
border-color: var(--accent);
color: var(--c)
}
html.dark .item.node-sub {
background: var(--bg2);
color: var(--c)
}
html.dark .item.hl {
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.3)
}
html.dark #btn-goto {
background: var(--accent);
color: #fff
}
/* 标签和导航修复 */
html.dark .nav-i.act {
background: var(--accent);
color: #fff
}
html.dark .side-menu-btn.act {
background: var(--accent);
color: #fff
}
html.dark .comm-tab.act {
background: var(--accent);
color: #fff;
border-color: var(--accent)
}
/* 其他元素修复 */
html.dark .adv-tag:hover {
background: var(--bg3)
}
html.dark .adv-var-item code {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.1)
}
html.dark .side-glass {
background: var(--glass);
border-color: var(--glass-border)
}
html.dark .loc-lk {
color: var(--accent)
}
html.dark .loc-lk:hover {
color: var(--accent2)
}
html.dark .data-item.sel {
background: rgba(129, 140, 248, 0.1);
border-color: var(--accent)
}
html.dark .loc-i.sel {
background: rgba(129, 140, 248, 0.1);
border-color: var(--accent)
}
html.dark .fold-h:hover {
background: var(--bg3)
}
html.dark .ct-av {
border: 2px solid rgba(255, 255, 255, 0.1)
}
html.dark .map-lbl {
background: var(--bg2);
border-color: var(--bd);
color: var(--c)
}
html.dark #zoom-ind {
background: var(--bg2);
border-color: var(--bd);
color: var(--c2)
}
html.dark #tip {
background: var(--bg2);
border-color: var(--bd)
}
html.dark .set-nav-item.act {
color: var(--accent);
border-color: var(--accent)
}
html.dark .btn-add {
border-color: var(--bd);
color: var(--c3)
}
html.dark .btn-add:hover {
background: var(--bg3);
color: var(--c)
}
/* 选择框修复 */
html.dark select.form-in {
background: var(--bg);
color: var(--c)
}
html.dark select.form-in option {
background: var(--bg2);
color: var(--c)
}
/* 复选框修复 */
html.dark input[type="checkbox"] {
accent-color: var(--accent)
}
/* SVG 地图线条修复 */
html.dark #lines line {
stroke: #71717a !important
}
html.dark #lines line[stroke="#333"] {
stroke: var(--accent) !important
}
</style>
2026-01-17 16:34:39 +08:00
</head>
<body>
<!-- Side Nav -->
<div class="side-nav-wrap">
<div class="side-menu side-glass">
<div class="side-menu-btn" id="btn-side-menu-toggle" title="快捷操作"><i class="fa-solid fa-ellipsis"></i></div>
<div class="side-menu-panel" id="side-menu-panel">
<button class="btn btn-s fc g4" id="btn-gen-local-map"><i class="fa-solid fa-plus"></i>局部地图</button>
<button class="btn btn-s fc g4" id="btn-simulate"><i class="fa-solid fa-rotate"></i>世界推演</button>
<button class="btn btn-s fc g4" id="btn-gen-local-scene"><i
class="fa-solid fa-feather-pointed"></i>局部剧情</button>
</div>
2026-01-17 16:34:39 +08:00
</div>
<nav class="side-nav side-glass">
<div class="nav-i" data-p="world"><i class="fa-solid fa-earth-americas"></i></div>
<div class="nav-i act" data-p="map"><i class="fa-solid fa-map"></i></div>
<div class="nav-i" data-p="comm"><i class="fa-solid fa-phone"></i></div>
</nav>
2026-01-17 16:34:39 +08:00
</div>
<!-- Toolbar -->
<div class="toolbar fc g10 usn">
<div class="toolbar-t">小白板<span>预测试</span></div>
<button class="btn btn-s fc g6" id="btn-deduce"><i class="fa-solid fa-wand-magic-sparkles"></i>生成</button>
<button class="btn btn-s fc g6" id="btn-settings"><i class="fa-solid fa-gear"></i>设置</button>
2026-02-02 00:00:53 +08:00
<button class="theme-toggle" id="btn-theme-toggle" title="切换主题"><i class="fa-solid fa-moon"></i><i
class="fa-solid fa-sun"></i></button>
<button class="btn btn-c fcc" id="btn-close"></button>
2026-01-17 16:34:39 +08:00
</div>
<!-- Main -->
<div class="main-wrap">
<!-- World -->
<div class="page page-pad" id="page-world">
<div class="banner">
<img src="https://picsum.photos/800/300" alt="" />
<div class="banner-ov">
<div>探索未知的世界...</div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<div class="news-sec">
<div class="sec-hd">
<h3 class="sec-t">最新消息</h3>
<button class="btn btn-add fcc" id="btn-refresh-world-news" title="只刷新世界新闻"><i
class="fa-solid fa-rotate"></i></button>
</div>
<div id="news-list"></div>
2026-01-17 16:34:39 +08:00
</div>
<div class="user-guide" id="user-guide">
<h3 class="sec-t">当前状态</h3>
<div class="user-guide-state" id="ug-state">尚未生成世界数据...</div>
<h3 class="sec-t mt12">行动指南</h3>
<div class="user-guide-actions" id="ug-actions">
<div class="user-guide-action">等待世界生成...</div>
</div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<!-- Map -->
<div class="page act" id="page-map">
<div id="mapWrap">
<div class="map-lbl" id="map-lbl">
<i class="fa-solid fa-map-location-dot"></i>
<span id="map-lbl-t">大地图</span>
<i class="fa-solid fa-chevron-down"></i>
<select id="map-lbl-select" class="map-lbl-sel"></select>
</div>
<div class="map-act">
<button class="btn btn-s btn-p fc g6" id="btn-goto"><i class="fa-solid fa-location-arrow"></i><span
id="goto-t">前往</span></button>
</div>
<div id="inner"><svg id="lines"></svg></div>
2026-01-17 16:34:39 +08:00
</div>
<div class="panel" id="zoom-ind">100%</div>
<div class="panel" id="tip">
<div class="desc" id="desc"></div>
<div class="info-w">
<div class="info-h">
<div class="info-t" id="info-t"></div>
<div class="info-bk" id="info-bk">← 返回</div>
</div>
<div class="info-c" id="info-c"></div>
</div>
2026-01-17 16:34:39 +08:00
</div>
</div>
2026-01-17 16:34:39 +08:00
<!-- Contacts -->
<div class="page page-pad" id="page-comm">
<div class="comm-hd">
<div class="comm-tabs">
<div class="comm-tab act" data-t="stranger">陌路人</div>
<div class="comm-tab" data-t="contact">联络人</div>
</div>
<button class="btn btn-add fcc" id="btn-refresh-strangers" title="微信摇一摇,寻找遇过的陌生人"><i
class="fa-solid fa-street-view"></i></button>
<button class="btn btn-add fcc" id="btn-add-ct"><i class="fa-solid fa-plus"></i></button>
2026-01-17 16:34:39 +08:00
</div>
<div id="sec-stranger" class="comm-sec act"></div>
<div id="sec-contact" class="comm-sec"></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
2026-01-17 16:34:39 +08:00
<!-- Chat -->
<div class="chat" id="chat">
<div class="chat-hd">
<div class="chat-av" id="chat-av"></div>
<div class="chat-t">
<div class="chat-nm" id="chat-nm"></div>
<div class="chat-st" id="chat-st"></div>
</div>
<div class="chat-hd-acts">
<button class="chat-compress" id="chat-compress" title="压缩总结"><i
class="fa-solid fa-compress"></i></button>
<button class="chat-clr" id="chat-clr" title="清空记录"><i class="fa-solid fa-trash"></i></button>
<button class="chat-x" id="chat-x"><i class="fa-solid fa-xmark"></i></button>
</div>
2026-01-17 16:34:39 +08:00
</div>
<div class="chat-msgs" id="chat-msgs"></div>
<div class="chat-in-w">
<button class="chat-send" id="chat-back" title="回退"><i class="fa-solid fa-rotate-left"></i></button>
<input type="text" class="chat-in" id="chat-in" placeholder="输入消息..." />
<button class="chat-send" id="chat-send"><i class="fa-solid fa-paper-plane"></i></button>
2026-01-17 16:34:39 +08:00
</div>
</div>
<!-- Right Panel -->
<div class="side-pop" id="side-pop">
<div class="side-pop-handle" id="side-pop-handle">
<div class="side-pop-bar"></div>
2026-01-17 16:34:39 +08:00
</div>
<div class="side-pop-ct">
<div class="side-pop-hd fc jsb">
<span>场景描述</span>
<button class="btn btn-s fc g4" id="btn-refresh-local-map"><i
class="fa-solid fa-rotate-right"></i>刷新</button>
</div>
<div class="side-pop-desc" id="side-desc"></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
2026-01-17 16:34:39 +08:00
<!-- Mobile Bottom Sheet -->
<div class="mob-pop" id="mob-pop">
<div class="pop-h-ind"><span data-l="2"></span><span data-l="1"></span><span data-l="0"></span></div>
<div class="pop-hd" id="pop-hd">
<div class="pop-handle"></div>
</div>
<div class="pop-ct">
<div class="pop-desc" id="mob-desc"></div>
<div class="pop-info">
<div class="pop-info-h">
<div class="pop-info-t" id="mob-info-t"></div>
<div class="pop-info-bk" id="mob-info-bk">← 返回</div>
</div>
<div id="mob-info-c"></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
</div>
2026-01-17 16:34:39 +08:00
<!-- Settings Modal (Redesigned) -->
<div class="modal" id="m-settings">
<div class="modal-bd"></div>
<div class="modal-p lg settings-modal">
<div class="modal-hd fc">
<nav class="settings-nav">
<button class="set-nav-item act" onclick="clickTab(this, 'tab-general')"><i
class="fa-solid fa-sliders"></i> <span class="nav-txt">常规</span></button>
<button class="set-nav-item" onclick="clickTab(this, 'tab-conn')"><i class="fa-solid fa-link"></i>
<span class="nav-txt">连接</span></button>
<button class="set-nav-item" onclick="clickTab(this, 'tab-data')"><i
class="fa-solid fa-database"></i> <span class="nav-txt">数据</span></button>
</nav>
<button class="modal-x fcc"></button>
2026-01-17 16:34:39 +08:00
</div>
<div class="settings-body">
<!-- Content -->
<div class="settings-content">
<!-- Tab: General -->
<div class="set-tab-page act" id="tab-general">
<div class="set-group">
<div class="set-group-t">运行模式</div>
<div class="form-g mb4">
<select class="form-in" id="set-mode">
<option value="story">故事模式 (Story Mode)</option>
<option value="assist">辅助模式 (Assist Mode)</option>
</select>
<div class="tips-box"><i class="fa-solid fa-circle-info"></i>
故事模式包含完整的世界模拟;辅助模式仅用于生成辅助地图/剧情。</div>
</div>
</div>
<div class="set-group">
<div class="set-group-t">剧情控制</div>
<div class="set-row g20">
<div class="form-g f1">
<label class="form-l">当前阶段 (Stage)</label>
<input type="number" class="form-in" id="set-stage" min="0" max="10" value="0" />
</div>
<div class="form-g f1">
<label class="form-l">偏离分数 (Deviation)</label>
<input type="number" class="form-in" id="set-deviation" min="0" max="100"
value="0" />
</div>
</div>
<div class="form-g">
<label class="form-l">推演倒计时 (Sim Countdown)</label>
<div class="fc g10">
<input type="number" class="form-in f1" id="set-sim-target" value="5" />
<span class="fs12 c2">行动数</span>
</div>
<div class="set-hint">当数值≤0时提醒进行世界推演。</div>
</div>
</div>
<div class="set-group">
<div class="set-group-t">历史记录</div>
<div class="set-test">
<label class="form-l">上下文楼层数</label>
<input type="number" class="form-in w100" id="set-history-count" min="0" max="200"
value="50" />
<label class="fc g4 fs12 c2 usn" style="margin-left:auto">
<input type="checkbox" id="set-use-stream" /> <span style="font-weight:600">启用流式
(Streaming)</span>
</label>
</div>
<div class="set-test-res" id="test-res"></div>
</div>
</div>
<!-- Tab: Connection -->
<div class="set-tab-page" id="tab-conn">
<div class="set-group">
<div class="set-group-t">API 设置</div>
<div class="form-g">
<label class="form-l">API 端点 (URL)</label>
<input type="text" class="form-in" id="set-api-url" placeholder="默认使用 SillyTavern 设置" />
</div>
<div class="form-g">
<label class="form-l">API 密钥 (Key)</label>
<input type="password" class="form-in" id="set-api-key" placeholder="如需覆盖请填写" />
</div>
</div>
<div class="set-group">
<div class="set-group-t">模型选择 (Model)</div>
<div class="set-row g10">
<input type="text" class="form-in f1" id="set-model" placeholder="Model ID" />
<button class="btn btn-s" id="btn-fetch-models">获取列表</button>
</div>
<select class="form-in mt6" id="set-model-list"></select>
<div class="mt10">
<button class="btn btn-s btn-p w100" id="btn-test-conn">测试连接</button>
</div>
</div>
</div>
<!-- Tab: Data -->
<div class="set-tab-page" id="tab-data">
<div class="set-group">
<div class="set-group-t">NPC 生成设置</div>
<div class="set-row g20">
<div class="form-g f1">
<label class="form-l">插入位置</label>
<select class="form-in" id="set-npc-position">
<option value="0">Char 前</option>
<option value="1">Char 后</option>
<option value="2">AN 前</option>
<option value="3">AN 后</option>
<option value="5">EM 前</option>
<option value="6">EM 后</option>
</select>
</div>
<div class="form-g f1">
<label class="form-l">条目顺序</label>
<input type="number" class="form-in" id="set-npc-order" min="0" max="1000"
value="100" />
</div>
</div>
</div>
<div class="set-group">
<div class="set-group-t">预设数据 (Presets)</div>
<div class="set-hint mb12">勾选要写入预设的数据块:</div>
<div id="data-list" class="data-grid"></div>
</div>
<div class="set-group">
<div class="set-group-t">高级模板</div>
<div class="set-hint mb10">自定义提示词与JSON格式。</div>
<button class="btn btn-s w100" id="btn-adv-prompts"><i class="fa-solid fa-pen"></i>
编辑高级模板</button>
</div>
</div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<div class="modal-ft fc">
<button class="btn btn-s m-cancel">取消</button>
<button class="btn btn-s btn-p" id="set-save">保存设置</button>
2026-01-17 16:34:39 +08:00
</div>
</div>
</div>
2026-01-17 16:34:39 +08:00
<!-- Advanced Prompts Modal -->
<div class="modal adv-modal" id="m-adv-prompts">
<div class="modal-bd"></div>
<div class="modal-p">
<div class="modal-hd fc">
<h2>高级设置 · 模板编辑</h2><button class="modal-x fcc"></button>
2026-01-17 16:34:39 +08:00
</div>
<div class="adv-layout">
<!-- Sidebar -->
<div class="adv-sidebar">
<div class="form-g">
<label class="form-l">选择模板</label>
<select class="form-in" id="adv-key"></select>
<div class="set-hint mt8">优先级:角色卡 &gt; 原保存 &gt; 默认。</div>
</div>
</div>
<!-- Main Content -->
<div class="adv-main">
<div class="adv-section">
<div class="adv-h-title">UAUA 提示词 (JS Function String)</div>
<div class="uaua-grid">
<div class="form-g"><label class="form-l">User 1 (u1)</label><textarea class="ed-ta-adv"
id="adv-u1"></textarea></div>
<div class="form-g"><label class="form-l">Assistant 1 (a1)</label><textarea
class="ed-ta-adv" id="adv-a1"></textarea></div>
<div class="form-g"><label class="form-l">User 2 (u2)</label><textarea class="ed-ta-adv"
id="adv-u2"></textarea></div>
<div class="form-g"><label class="form-l">Assistant 2 (a2)</label><textarea
class="ed-ta-adv" id="adv-a2"></textarea></div>
</div>
</div>
<div class="adv-section" id="adv-json-wrap">
<div class="adv-h-title">JSON Template</div>
<textarea class="ed-ta-adv" id="adv-json" style="min-height:300px;font-size:13px"></textarea>
</div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<div class="modal-ft fc">
<div class="fc g10 f1">
<button class="btn btn-s" id="adv-reset"><i class="fa-solid fa-rotate-left"></i> 重置</button>
<select class="form-in w140" id="adv-scope" title="选择来源">
<option value="character">角色</option>
<option value="global">通用</option>
</select>
</div>
<button class="btn btn-s" id="adv-save-global">通用保存</button>
<button class="btn btn-s btn-p" id="adv-save-char">角色卡保存</button>
2026-01-17 16:34:39 +08:00
</div>
</div>
</div>
<!-- Data Edit Modal -->
<div class="modal" id="m-data-edit">
<div class="modal-bd"></div>
<div class="modal-p">
<div class="modal-hd fc">
<h2 id="data-edit-title">编辑数据</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<textarea class="ed-ta" id="data-edit-ta"></textarea>
<pre class="ed-preview" id="data-edit-preview"></pre>
<div class="ed-err" id="data-edit-err"></div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="data-edit-save">保存</button></div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<!-- Goto Modal -->
<div class="modal" id="m-goto">
<div class="modal-bd"></div>
<div class="modal-p sm">
<div class="modal-hd fc">
<h2>确认前往</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div class="goto-d" id="goto-d"></div>
<div class="form-g"><label class="form-l">任务目标(可选)</label><textarea class="form-in form-ta"
id="goto-task" placeholder="描述你要做什么..."></textarea></div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="goto-ok">确认前往</button></div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<!-- Invite Modal -->
<div class="modal" id="m-invite">
<div class="modal-bd"></div>
<div class="modal-p sm">
<div class="modal-hd fc">
<h2>邀请前往</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div class="goto-d" id="inv-t"></div>
<div class="form-g"><label class="form-l">选择地点</label>
<div class="loc-list" id="loc-list"></div>
</div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="inv-ok">发送邀请</button></div>
</div>
2026-01-17 16:34:39 +08:00
</div>
<!-- World Gen Modal -->
<div class="modal" id="m-world-gen">
<div class="modal-bd"></div>
<div class="modal-p">
<div class="modal-hd fc">
<h2>世界生成</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div class="form-g">
<label class="form-l">玩家特殊需求(可选)</label>
<textarea class="form-in form-ta" id="world-gen-req" rows="4"
placeholder="例如:希望有更多悬疑元素、增加一个神秘组织..."></textarea>
<div class="set-hint">AI 会根据当前世界观和你的需求生成完整的世界数据</div>
</div>
<div id="world-gen-status" class="set-hint"></div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="world-gen-ok"><i class="fa-solid fa-wand-magic-sparkles"></i> 开始生成</button></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
<!-- World Sim Modal -->
<div class="modal" id="m-world-sim">
<div class="modal-bd"></div>
<div class="modal-p">
<div class="modal-hd fc">
<h2>世界推演</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div class="form-g">
<div class="set-hint mb12">
<strong>推演模式</strong>:根据玩家行为和时间流逝,演化世界状态。
<ul class="lh15" style="margin:8px 0;padding-left:20px;font-size:12px">
<li>L1/L2 将大幅更新</li>
<li>L3/L4 适度调整</li>
<li>L5 保持不变</li>
<li>Stage 将推进到下一阶段</li>
</ul>
</div>
<div class="set-hint warn">⚠️ 此操作会覆盖当前世界数据,建议先备份</div>
</div>
<div id="world-sim-status" class="set-hint"></div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="world-sim-ok"><i class="fa-solid fa-rotate"></i> 开始推演</button></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
<!-- Add Contact Modal -->
<div class="modal" id="m-add-ct">
<div class="modal-bd"></div>
<div class="modal-p sm">
<div class="modal-hd fc">
<h2>添加联络人</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div class="form-g">
<label class="form-l">UID世界书条目UID</label>
<div class="set-row">
<input type="text" class="form-in" id="add-uid" placeholder="请输入世界书条目UID" />
<button class="btn btn-s" id="btn-check-uid"><i class="fa-solid fa-search"></i> 检查</button>
</div>
<div class="set-hint">输入角色卡绑定世界书的条目UID</div>
</div>
<div class="form-g" id="name-select-group" style="display:none">
<label class="form-l">选择名字</label>
<select class="form-in" id="add-name">
<option value="">-- 请选择 --</option>
</select>
</div>
<div id="uid-check-err" class="ed-err"></div>
</div>
<div class="modal-ft fc"><button class="btn btn-s m-cancel">取消</button><button class="btn btn-s btn-p"
id="add-ct-ok" disabled>确定</button></div>
2026-01-17 16:34:39 +08:00
</div>
</div>
<!-- Result Modal -->
<div class="modal" id="m-result">
<div class="modal-bd"></div>
<div class="modal-p sm">
<div class="modal-hd fc">
<h2 id="res-title">操作结果</h2><button class="modal-x fcc"></button>
</div>
<div class="modal-by">
<div id="res-msg"></div>
<div id="res-record-box">
<div class="set-hint mb4">详细记录:</div>
<pre id="res-record"></pre>
</div>
</div>
<div class="modal-ft fc">
<button class="btn btn-s" id="res-action"></button>
<button class="btn btn-s btn-p m-cancel">确定</button>
</div>
2026-01-17 16:34:39 +08:00
</div>
</div>
<script>
2026-02-02 00:00:53 +08:00
const D = { stage: 0, deviationScore: 0, simulationTarget: 5, meta: { truth: null, onion_layers: null, timeline: null, user_guide: null }, world: {}, maps: { outdoor: { nodes: [] }, indoor: null }, sceneSetup: null, contacts: { strangers: [], contacts: [{ name: "{{characterName}}", avatar: "", color: "#555", location: "在线", info: "角色卡联络人", online: !0, worldbookUid: "__CHARACTER_CARD__", messages: [], summarizedCount: 0 }] } }; let charSmsHistory = { messages: [], summarizedCount: 0, summaries: {} }; const $ = t => document.getElementById(t), $$ = t => document.querySelectorAll(t), isMob = () => innerWidth <= 550, escHtml = t => t.replace(/[&<>"']/g, (t => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[t]))), h = t => escHtml(String(t ?? "")), stripXml = t => t ? t.replace(/<(\w+)[^>]*>[\s\S]*?<\/\1>/g, "").replace(/<[^>]+\/?>/g, "").trim() : "", parseLinks = t => h(t).replace(/\*\*([^*]+)\*\*/g, '<span class="loc-lk" data-loc="$1">$1</span>'), PARENT_ORIGIN = (() => { try { return new URL(document.referrer).origin } catch { return window.location.origin } })(), post = (t, e = {}) => parent.postMessage({ source: "LittleWhiteBox-OutlineFrame", type: t, ...e }, PARENT_ORIGIN), syncSimDueUI = () => { const t = (Number(D.simulationTarget) || 0) <= 0; $("btn-simulate")?.classList.toggle("btn-due", t), $("world-sim-ok")?.classList.toggle("btn-due", t) }, BtnState = { load: (t, e) => { t.disabled = !0, t._o = t.innerHTML, t.innerHTML = `<i class="fa-solid fa-spinner fa-spin"></i> ${e}` }, reset: (t, e) => { t.disabled = !1, t.innerHTML = e || t._o } }, Req = { _p: {}, _id: 0, create(t) { const e = t + "_" + ++this._id; return this._p[t] = { id: e }, e }, get(t) { return this._p[t] }, match(t) { const e = t?.split("_")[0]; return this._p[e]?.id === t ? this._p[e] : null }, set(t, e) { Object.assign(this._p[t] || {}, e) }, clear(t) { delete this._p[t] } }, openM = t => $(t).classList.add("act"), closeM = t => $(t).classList.remove("act"), dirMap = { north: [0, -1], south: [0, 1], east: [1, 0], west: [-1, 0], northeast: [1, -1], northwest: [-1, -1], southeast: [1, 1], southwest: [-1, 1] }; let playerLocation = "未知", curNode = null, drag = !1, scale = 1, offX = 0, offY = 0, seed = 123456789, nodes = [], lines = [], anim = !1, chatTgt = null, invTgt = null, selLoc = null, smsGen = !1, selectedMapValue = "current"; const inner = $("inner"), svg = $("lines"), mapWrap = $("mapWrap"), popup = $("mob-pop"), chat = $("chat"), sidePop = $("side-pop"), rand = () => (seed = (9301 * seed + 49297) % 233280) / 233280, getCurInside = () => D.maps?.indoor?.[playerLocation] || null, snaps = () => [$("pop-hd")?.offsetHeight || 0, .3 * innerHeight, .65 * innerHeight]; let popH = 0, popDrag = !1, popSY = 0, popSH = 0, popLv = 1; const inds = popup.querySelectorAll(".pop-h-ind span"), setPopH = t => { const e = snaps(); popH = Math.max(e[0], Math.min(.85 * innerHeight, t)), popup.style.height = popH + "px", popLv = e.map(((t, e) => [e, Math.abs(popH - t)])).sort(((t, e) => t[1] - e[1]))[0][0], inds.forEach(((t, e) => t.classList.toggle("act", e === popLv))) }, snapTo = t => { popLv = Math.max(0, Math.min(2, t)), setPopH(snaps()[popLv]) }, openPop = (t = 1) => { popup.classList.add("act"), snapTo(t) }; $("side-pop-handle").onclick = () => sidePop.classList.toggle("act"), $("pop-hd").onmousedown = t => { popDrag = !0, popSY = t.clientY, popSH = popH || snaps()[1], popup.classList.add("drag"), t.preventDefault() }, $("pop-hd").ontouchstart = t => { t.preventDefault(), popDrag = !0, popSY = t.touches[0].clientY, popSH = popH || snaps()[1], popup.classList.add("drag") }, document.onmousemove = t => { popDrag && setPopH(popSH + popSY - t.clientY) }, document.ontouchmove = t => { popDrag && t.touches.length && (t.preventDefault(), setPopH(popSH + popSY - t.touches[0].clientY)) }; const endDrag = () => { popDrag && (popDrag = !1, popup.classList.remove("drag"), snapTo(popLv)) }; document.onmouseup = endDrag, document.ontouchend = endDrag, document.ontouchcancel = endDrag; const bindLinks = t => t.querySele
// 主题切换逻辑
const initTheme = () => {
const saved = localStorage.getItem('lwb-theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (saved === 'dark' || (!saved && prefersDark)) {
document.documentElement.classList.add('dark');
}
};
const toggleTheme = () => {
const html = document.documentElement;
const isDark = html.classList.toggle('dark');
localStorage.setItem('lwb-theme', isDark ? 'dark' : 'light');
};
// 初始化主题
initTheme();
// 绑定主题切换按钮
$("btn-theme-toggle").onclick = toggleTheme;
</script>
2026-01-17 16:34:39 +08:00
</body>
2026-01-17 16:34:39 +08:00
</html>