Quellcode durchsuchen

web 合并为「知识点」单卡视图

- 去掉分开的「知识拆分」「知识解构」两节,合并成「知识点(N 条)」
- 每条知识一张完整卡:知识类型(组合) + What/Why/How + 对应阶段 + 作用域(大类+具体点)
- 作用域按五棵树分色;多阶段标注「N 个阶段都 cover」;证据/payload 折叠

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lisihan vor 1 Monat
Ursprung
Commit
167c2c44fd
1 geänderte Dateien mit 52 neuen und 33 gelöschten Zeilen
  1. 52 33
      web/index.html

+ 52 - 33
web/index.html

@@ -26,9 +26,6 @@
   .badge.no{background:rgba(224,85,107,.15);color:var(--no);border-color:transparent}
   .badge.ac{background:rgba(91,157,255,.15);color:var(--ac);border-color:transparent}
   .badge.warn{background:rgba(224,162,58,.15);color:var(--warn);border-color:transparent}
-  .badge.what{background:rgba(91,157,255,.15);color:var(--what);border-color:transparent}
-  .badge.why{background:rgba(192,132,252,.15);color:var(--why);border-color:transparent}
-  .badge.how{background:rgba(54,192,138,.15);color:var(--how);border-color:transparent}
   .stage{background:var(--panel);border:1px solid var(--line);border-radius:10px;margin-bottom:16px;overflow:hidden}
   .stage>h3{margin:0;padding:10px 16px;background:var(--panel2);font-size:13px;display:flex;gap:8px;align-items:center}
   .stage>h3 .n{width:20px;height:20px;border-radius:50%;background:var(--ac);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px}
@@ -37,21 +34,27 @@
   .imgs{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
   .imgs img{width:80px;height:80px;object-fit:cover;border-radius:6px;border:1px solid var(--line);cursor:zoom-in;transition:transform .1s}
   .imgs img:hover{transform:scale(1.05);border-color:var(--ac)}
-  .card{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:12px;margin-bottom:10px}
-  .card h4{margin:0 0 8px;font-size:14px}
-  .chips{display:flex;gap:6px;flex-wrap:wrap;margin:6px 0}
+  .card{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:12px 14px;margin-bottom:10px}
+  .kphead{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}
+  .kphead h4{margin:0;font-size:14px}
+  .ktype{font-size:12px;padding:3px 10px;border-radius:8px;background:rgba(91,157,255,.15);color:var(--ac);white-space:nowrap;flex:none}
+  .chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:0}
   pre{white-space:pre-wrap;word-break:break-word;background:#0c0e12;border:1px solid var(--line);border-radius:6px;padding:10px;font-size:12px;color:#c8cdd6;margin:6px 0 0;max-height:260px;overflow:auto}
   details summary{cursor:pointer;color:var(--mut);font-size:12px;margin-top:8px}
   .whh{margin:8px 0;padding-left:10px;border-left:2px solid var(--line)}
   .whh.w{border-color:var(--what)} .whh.y{border-color:var(--why)} .whh.h{border-color:var(--how)}
   .whh b{font-size:12px}
   .whh.w b{color:var(--what)} .whh.y b{color:var(--why)} .whh.h b{color:var(--how)}
+  .sect{border-top:1px solid var(--line);margin-top:12px;padding-top:10px;display:flex;flex-direction:column;gap:10px}
+  .srow{display:flex;align-items:flex-start;gap:10px}
+  .slabel{font-size:13px;color:var(--mut);min-width:62px;flex:none;padding-top:2px}
+  .scope{display:flex;align-items:baseline;gap:8px}
+  .scn{font-size:12px;padding:2px 8px;border-radius:8px;flex:none}
+  .foot{border-top:1px solid var(--line);margin-top:10px;padding-top:6px;display:flex;gap:18px;flex-wrap:wrap}
   .link{display:inline-flex;align-items:center;gap:6px;padding:5px 12px;background:rgba(91,157,255,.12);border:1px solid var(--ac);border-radius:8px;color:var(--ac);text-decoration:none;font-size:13px;margin-bottom:10px}
   .crit{background:#0c0e12;border:1px solid var(--line);border-radius:8px;padding:10px 14px;margin-bottom:12px}
   .crit .t{color:var(--mut);font-size:12px;margin-bottom:6px}
   .crit ul{margin:0;padding-left:18px} .crit li{margin:3px 0;font-size:13px}
-  .scope{display:flex;gap:8px;align-items:baseline;margin:4px 0}
-  .scope .st{color:var(--ac);font-size:12px;min-width:42px}
   .lb{position:fixed;inset:0;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;z-index:50;cursor:zoom-out}
   .lb img{max-width:92vw;max-height:92vh;border-radius:8px}
   a{color:var(--ac)}
@@ -65,7 +68,10 @@
 const {useState,useEffect} = React;
 const j = (u)=>fetch(u).then(r=>r.json());
 const SCOPE_CN = {substance:"实质",form:"形式",feeling:"感受",effect:"作用",intent:"意图"};
-const TYPE_KIND = {what:"what",why:"why",how:"how"};
+const SCOPE_COLOR = {
+  substance:["rgba(91,157,255,.16)","#9ec3ff"], form:["rgba(54,192,138,.16)","#6fe0b5"],
+  feeling:["rgba(192,132,252,.16)","#d8b6ff"], effect:["rgba(224,162,58,.16)","#f0c97a"],
+  intent:["rgba(224,85,107,.16)","#f0a0ad"]};
 
 function Badge({kind,children}){return <span className={"badge "+(kind||"")}>{children}</span>;}
 
@@ -86,29 +92,45 @@ function Stage({n,title,sub,children}){
     <div className="body">{sub&&<div className="sub2">{sub}</div>}{children}</div></div>;
 }
 
-function SplitCard({item}){
+function KPCard({it,idx}){
+  const item=it.item||{}, deco=it.deconstruction||{}, payload=it.ingest_payload||{};
+  const types=(item.knowledge_types||[]).map(t=>t.toUpperCase()).join(" + ");
+  const stages=deco.stages||[], scopes=deco.scopes||[];
   return <div className="card">
-    <h4>{item.title||"(无标题)"}</h4>
-    <div className="chips">{(item.knowledge_types||[]).map(t=><Badge key={t} kind={TYPE_KIND[t]}>{t.toUpperCase()}</Badge>)}</div>
+    <div className="kphead">
+      <h4>{idx}. {item.title||"(无标题)"}</h4>
+      <span className="ktype">知识类型 · {types||"—"}</span>
+    </div>
     {item.what&&<div className="whh w"><b>What · 是什么</b><div>{item.what}</div></div>}
     {item.why&&<div className="whh y"><b>Why · 为什么</b><div>{item.why}</div></div>}
     {item.how&&<div className="whh h"><b>How · 怎么做</b><div>{item.how}</div></div>}
-    {(item.evidence&&item.evidence.length>0)&&
-      <details><summary>原文证据({item.evidence.length})</summary><pre>{item.evidence.join("\n")}</pre></details>}
-  </div>;
-}
 
-function DeconCard({it}){
-  const item=it.item||{}, deco=it.deconstruction||{}, payload=it.ingest_payload||{};
-  return <div className="card">
-    <h4>{item.title||"(无标题)"}</h4>
-    <div className="kv" style={{marginTop:8}}>阶段(灵感 / 选题 / 脚本)</div>
-    <div className="chips">{(deco.stages||[]).length? deco.stages.map(s=><Badge key={s} kind="ac">{s}</Badge>) : <span className="kv">—</span>}</div>
-    <div className="kv" style={{marginTop:8}}>作用域(实质 / 形式 / 感受 / 作用 / 意图)</div>
-    {(deco.scopes||[]).length? (deco.scopes||[]).map((s,i)=>
-      <div className="scope" key={i}><span className="st">{SCOPE_CN[s.scope_type]||s.scope_type}</span><span>{s.value}</span></div>
-    ) : <div className="kv">—</div>}
-    <details><summary>组装后的入库数据(ingest payload)</summary><pre>{JSON.stringify(payload,null,2)}</pre></details>
+    <div className="sect">
+      <div className="srow">
+        <span className="slabel">对应阶段</span>
+        <div className="chips">
+          {stages.length? stages.map(s=><Badge key={s} kind="ac">{s}</Badge>) : <span className="kv">—</span>}
+          {stages.length>1 && <span className="kv">({stages.length} 个阶段都 cover)</span>}
+        </div>
+      </div>
+      <div className="srow">
+        <span className="slabel">作用域</span>
+        <div style={{display:'flex',flexDirection:'column',gap:6}}>
+          {scopes.length? scopes.map((s,i)=>{
+            const c=SCOPE_COLOR[s.scope_type]||["var(--panel)","var(--mut)"];
+            return <div className="scope" key={i}>
+              <span className="scn" style={{background:c[0],color:c[1]}}>{SCOPE_CN[s.scope_type]||s.scope_type}</span>
+              <span>{s.value}</span></div>;
+          }) : <span className="kv">—</span>}
+        </div>
+      </div>
+    </div>
+
+    <div className="foot">
+      {(item.evidence&&item.evidence.length>0)&&
+        <details><summary>原文证据({item.evidence.length})</summary><pre>{item.evidence.join("\n")}</pre></details>}
+      <details><summary>入库数据(ingest payload)</summary><pre>{JSON.stringify(payload,null,2)}</pre></details>
+    </div>
   </div>;
 }
 
@@ -152,12 +174,9 @@ function Pipeline({postId,onZoom}){
       <div>{scr.reason||"—"}</div>
     </Stage>
 
-    <Stage n="3" title={"知识拆分("+items.length+" 条)"} sub="把帖子拆成 What / Why / How 知识片段">
-      {items.length? items.map((it,i)=><SplitCard key={i} item={it.item||{}}/>) : <div className="kv">无(被淘汰或未拆出)</div>}
-    </Stage>
-
-    <Stage n="4" title="知识解构" sub="给每条知识标注 阶段 与 作用域">
-      {items.length? items.map((it,i)=><DeconCard key={i} it={it}/>) : <div className="kv">无</div>}
+    <Stage n="3" title={"知识点("+items.length+" 条)"}
+      sub="每条 = 知识类型 + What/Why/How + 对应阶段 + 作用域">
+      {items.length? items.map((it,i)=><KPCard key={i} it={it} idx={i+1}/>) : <div className="kv">无(被淘汰或未拆出)</div>}
     </Stage>
   </div>;
 }