docs+framework: canon, system-profile, gdd, registry, 6a framework
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
# .dockerignore for a make-my-arg game. It ships inside assets/starter/, so it lands in the project
|
||||||
|
# root when the starter tree is copied at scaffold time (step 6) — no rename, no second copy step.
|
||||||
|
# Deploying the static tree is a COPY, so this is the second half of the .gitignore job: what Git keeps
|
||||||
|
# out of history, the image must keep out of the visitor's hands.
|
||||||
|
#
|
||||||
|
# What stays OUT of the image, and why:
|
||||||
|
# data/*.src.json — plaintext keyword / credential tables: the answer key in a request away (R3).
|
||||||
|
# docs/ — the GDD, reachability chain, puzzle audit, site graph: every answer in prose.
|
||||||
|
# tools/ viewer/ — the checkers, config and renderer template: dev machinery, not pages.
|
||||||
|
# deploy/ — deploy scripts and manifests.
|
||||||
|
# .git/ node_modules/ — nothing a static host needs.
|
||||||
|
#
|
||||||
|
# Everything else ships: index.html, pages/ (or apps/), assets/ (css, js, img, audio, docs),
|
||||||
|
# the hashed data/*.json tables, portal.html / query.html / results.html and README.md as desired.
|
||||||
|
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile*
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
docs
|
||||||
|
tools
|
||||||
|
viewer
|
||||||
|
deploy
|
||||||
|
|
||||||
|
data/*.src.json
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# .gitignore for a make-my-arg game. It ships inside assets/starter/, so it lands in the project
|
||||||
|
# root when the starter tree is copied at scaffold time (step 6) — no rename, no second copy step.
|
||||||
|
# It keeps development by-products out of the repository while everything the deploy needs
|
||||||
|
# (index.html, pages/, assets/, data/*.json, tools/, viewer/) stays tracked.
|
||||||
|
#
|
||||||
|
# Two different jobs, two different files: .gitignore keeps junk out of Git history, .dockerignore /
|
||||||
|
# the rsync exclude list keeps development material out of the deployed image. A plaintext table must be
|
||||||
|
# listed in both (it is worse than junk: it is the answer key, R3).
|
||||||
|
|
||||||
|
# ── Development-only plaintext sources (R3): the answer key, never committed, never deployed ──
|
||||||
|
data/*.src.json
|
||||||
|
|
||||||
|
# ── dev/ops trees: no site page lives here (the tools skip them via CONFIG.skipDirs) ──
|
||||||
|
docs/site-graph*
|
||||||
|
memory/
|
||||||
|
review_comments.md
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# ── Story source material (may be copyright-protected; keep it locally, do not publish it) ──
|
||||||
|
docs/source-material/
|
||||||
|
|
||||||
|
# ── OS files ──
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# ── Editor / IDE ──
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# ── Agent scratch dirs (an agent working in this repo writes its own logs here) ──
|
||||||
|
.codex/
|
||||||
|
.opencode/
|
||||||
|
.claude/
|
||||||
|
.superpowers/
|
||||||
|
docs/superpowers/
|
||||||
|
|
||||||
|
# ── Node (the tools need no install; a project that adds one gets node_modules) ──
|
||||||
|
node_modules/
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# 《游戏名》
|
||||||
|
|
||||||
|
make-my-arg 的起点项目:每个生成的项目都把这棵树复制为起点,再按 `docs/system-profile.md` 组装、删改、填充。
|
||||||
|
纯静态、无构建步骤——HTML + CSS + 本地 vendored 的 Alpine v3 + 零依赖 Node 工具,任何静态托管都能跑。
|
||||||
|
|
||||||
|
## 模板说明(成稿时改写或删除本节)
|
||||||
|
|
||||||
|
- `《》` 是占位符:组织名、页面标题、正文文案,全部换成虚构自己的话。本页随项目一起改写。
|
||||||
|
- 模板里的 `<!-- -->` 注释是写作指引,不是给玩家看的内容:填充页面时逐条删除。step 8 的 chrome 扫描会把残留提示当泄漏处理(R4)。
|
||||||
|
- `<html lang>` 随故事的语言设置。
|
||||||
|
- 入口页带一个指向 make-my-arg 技能仓库的 GitHub 链接(对技能的引用,请勿删除);入口除此之外只有进入按钮,没有“关于”页。
|
||||||
|
- 这棵树只放**不随模块变化**的那部分:入口外壳、共用 css/js、两份忽略文件、README。带模块标记的文件——`search.html`(M1)、`data/`(M1/M2/M6)、`secret.css`(M5)、`phone.css` / `chrome.css`(设备类)、`pages/` 与受限区域、结局页(M10)——由 step 6a 从技能仓库的 `assets/templates/`(js/css/html 前端代码模板,索引见其 README)按 `docs/system-profile.md` 的选择复制创建;profile 没选的机制一个都不出现。`tools/` 和 `viewer/` 由 step 6a 从 skill 的 `assets/` 拼装进来,它们各有唯一出处,不要在项目里分叉维护。
|
||||||
|
- 起点项目里没有 `data/*.src.json`:它自带的 `.gitignore` 会把这些明文表忽略掉,而明文表只应在 6a 选中对应模块时才创建。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
| 路径 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| `index.html` | 入口外壳(M9):身份赋予 + 进入按钮;底部带指向 make-my-arg 仓库的引用链接;规则说明只在虚构确有必要时出现 |
|
||||||
|
| `pages/` | 站点页面,一篇一文档;受限区域用虚构自己的词命名(`internal/` `staff/` `archive/`),绝不叫 `secret/`,文件名不许剧透 |
|
||||||
|
| `assets/css/base.css` | 共用骨架:常驻顶栏、页面栏宽、文档套件、门禁表单、`x-cloak`、M11 纹理 |
|
||||||
|
| `assets/css/surface.css` | 公开层皮肤:色板与字体,唯一随虚构气质改动的地方 |
|
||||||
|
| `assets/css/secret.css` | M5 深层皮肤;profile 未选 M5 时这个文件不存在 |
|
||||||
|
| `assets/js/components.js` | 共用内核:`hash()`、session store 与 `readState`(M14);只注册 profile 选中的组件 |
|
||||||
|
| `assets/js/vendor/alpine.min.js` | `node tools/vendor-alpine.mjs` 拉取并校验 sha256 后落下,永不改动、不走 CDN |
|
||||||
|
| `.gitignore` | 随起点项目落到项目根:开发副产物不进 Git 历史 |
|
||||||
|
| `.dockerignore` | 随起点项目落到项目根:明文表 / `docs/` / 机制文件不进部署镜像 |
|
||||||
|
| `assets/img/` `audio/` `docs/` | 图片、音频谜题、可下载的仿制文档(pdf/xlsx),用到才建 |
|
||||||
|
| `data/keywords.*.src.json` | 明文索引表,开发专用,不进部署树 |
|
||||||
|
| `data/keywords.*.json` | `node tools/build-keywords.mjs` 产出的哈希表,站点真正读取的文件 |
|
||||||
|
| `data/forbidden.json` | M6 禁用词模块(哈希 + 触发态文案) |
|
||||||
|
| `data/credentials.src.json` | M2 派生凭据的出处登记(部件 + 规则),开发专用 |
|
||||||
|
| `tools/` | step 6a 从 `assets/tools/` 复制:检查器与 `config.mjs` |
|
||||||
|
| `viewer/` | step 6a 从 `assets/viewer/` 复制:站点图渲染树,不是站点页面 |
|
||||||
|
| `docs/` | step 1–5 的产物:story canon(五张表)、system-profile、GDD、触达链、谜题分析 |
|
||||||
|
| `deploy/` | 部署脚本与清单,用到才建 |
|
||||||
|
|
||||||
|
`tools/`、`viewer/`、`docs/`、`deploy/` 里没有站点页面:走图工具按 `CONFIG.skipDirs` 跳过它们,所以生成的站点图永远不会把页数撑大。
|
||||||
|
|
||||||
|
## 本地运行
|
||||||
|
|
||||||
|
静态站点,没有构建:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 8000 # 然后打开 http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
直接双击 `index.html` 也能看,但检索模块要 `fetch()` 读 `data/*.json`,需要 http(s),所以请用上面的本地服务。
|
||||||
|
|
||||||
|
## 检查(`tools/` 拼装后可用)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node tools/build-keywords.mjs # 改动任何 data/keywords*.src.json 之后,重新生成哈希表
|
||||||
|
node tools/check-links.mjs # 死链 + 公开索引泄漏;期望 "0 dead · 0 layer leaks"
|
||||||
|
node tools/check-solvable.mjs # 冷启动走查:每页可达、每个门可解、关键词都被读过
|
||||||
|
node tools/check-credentials.mjs # 派生凭据:部件 + 规则 + 零明文(存在 credentials.src.json 时)
|
||||||
|
node tools/check-reachability.mjs # 演算副本:把凭据注入一次性副本,复跑可达性
|
||||||
|
node tools/site-graph.mjs # 产出 docs/site-graph.json 与 docs/site-graph/(报告型,有问题也退出 0)
|
||||||
|
node tools/check-solvable.mjs --self-test # 改过 tools/config.mjs 之后自检匹配器
|
||||||
|
```
|
||||||
|
|
||||||
|
每次内容编辑后重跑,不只是部署前。改名、改层名、改标记只改 `tools/config.mjs` 一个文件,不要重写检查器。
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
静态树部署就是一次拷贝,因此有第二条泄漏路径:明文 `data/*.src.json`(答案本身,R3)、`docs/`(GDD 与触达链就是全部答案)、`tools/` `viewer/` `deploy/` 机制文件,都不得进入部署目录。两份忽略文件就在这棵树里,随复制落到项目根;GitHub Pages 直接发布仓库即可。
|
||||||
|
|
||||||
|
## 设计文档
|
||||||
|
|
||||||
|
- `docs/system-profile.md` — step 2 与用户一起选定的容器与模块
|
||||||
|
- `docs/gdd.md` — 游戏设计文档:front matter(资产清单 + 实体登记表)+ 八节
|
||||||
|
- `docs/reachability.md` — 触达链分析:每页从哪来、守门是什么
|
||||||
|
- `docs/puzzle-audit.md` — 谜题设计分析:必要性 / 可得性 / 直观性 / 泄漏扫描
|
||||||
|
- `docs/self-check.md` — step 8 的逐项通过 / 失败清单
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/* base.css — the shared skeleton of a make-my-arg project.
|
||||||
|
*
|
||||||
|
* Structure only: the persistent top bar, the page column, the document kit, the gate form, the
|
||||||
|
* entry shell, x-cloak. Every colour, font and surface lives in surface.css (and secret.css on
|
||||||
|
* deep pages, M5) — a project edits the skin, never this file's layout, per page.
|
||||||
|
*
|
||||||
|
* Conventions this file owes the rest of the system:
|
||||||
|
* · header is sticky and opaque, site-wide, deep pages included (base.md §2)
|
||||||
|
* · every input names its field; nothing here hints at an answer (R4)
|
||||||
|
* · .hidden-text / .blurred are the M11 texture kit: black-on-black selectable text, redaction
|
||||||
|
* as a clue. Client-side masking is not privacy — anything hidden this way is public (R12)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ── Alpine: hide a component until it initializes, so no raw template flashes ── */
|
||||||
|
[x-cloak] { display: none !important; }
|
||||||
|
|
||||||
|
/* ── Reset ── */
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; }
|
||||||
|
img { max-width: 100%; }
|
||||||
|
ul, ol { padding-left: 1.4em; }
|
||||||
|
|
||||||
|
/* ── Persistent top bar ─────────────────────────────────────────────────────────
|
||||||
|
Sticky to the viewport top; background must stay opaque so long-page body text never shows
|
||||||
|
through. The nav links are this organization's own IA — together with the footer links (and the
|
||||||
|
M1 search box) they are the only cross-page links a public page carries (R7). */
|
||||||
|
header {
|
||||||
|
position: sticky; top: 0; z-index: 10;
|
||||||
|
background: var(--bar, #f3e2d0);
|
||||||
|
border-bottom: 1px solid var(--rule, #d8c4b0);
|
||||||
|
}
|
||||||
|
header nav {
|
||||||
|
max-width: 60rem; margin: 0 auto; padding: .55rem 1rem;
|
||||||
|
display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem 1.1rem;
|
||||||
|
font-family: var(--sans, system-ui, sans-serif); font-size: .9rem;
|
||||||
|
}
|
||||||
|
header nav a { color: var(--ink, #4a4a4a); text-decoration: none; }
|
||||||
|
header nav a:hover { color: var(--accent, #c1440e); text-decoration: underline; }
|
||||||
|
header nav .brand { font-weight: 700; letter-spacing: .02em; }
|
||||||
|
header nav form { display: flex; gap: .35rem; margin-left: auto; }
|
||||||
|
|
||||||
|
/* ── Page column: one document per page ── */
|
||||||
|
main {
|
||||||
|
max-width: 60rem; margin: 0 auto; padding: 1.6rem 1rem 3rem;
|
||||||
|
line-height: 1.75;
|
||||||
|
}
|
||||||
|
main > h1 { font-size: 1.5rem; margin: 0 0 1rem; }
|
||||||
|
main > h2 { font-size: 1.15rem; margin: 1.8rem 0 .6rem; }
|
||||||
|
main p { margin: .6rem 0; }
|
||||||
|
|
||||||
|
/* ── Footer: the site's own links + the M7 progress number ── */
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid var(--rule, #d8c4b0);
|
||||||
|
font-family: var(--sans, system-ui, sans-serif); font-size: .8rem; color: var(--ink-2, #6b6b6b);
|
||||||
|
}
|
||||||
|
footer .footer-inner { max-width: 60rem; margin: 0 auto; padding: .9rem 1rem; display: flex; flex-wrap: wrap; gap: .3rem 1rem; }
|
||||||
|
footer a { color: var(--ink-2, #6b6b6b); }
|
||||||
|
.progress { margin-left: auto; font-family: var(--mono, monospace); letter-spacing: .04em; }
|
||||||
|
|
||||||
|
/* ── Document kit: what a real system prints ─────────────────────────────────────
|
||||||
|
.sheet is a formal document (R6: issuing body, number, date, addressee, body, seal,
|
||||||
|
distribution list); .narrow tightens it to a memo or a single record. */
|
||||||
|
.sheet { border: 1px solid var(--rule, #d8c4b0); background: var(--paper-2, #fffdf9); padding: 1.4rem 1.6rem; }
|
||||||
|
.sheet.narrow { max-width: 34rem; margin: 0 auto; }
|
||||||
|
.sheet .doc-head { border-bottom: 2px solid var(--ink, #4a4a4a); padding-bottom: .5rem; margin-bottom: 1rem; }
|
||||||
|
.sheet .doc-no { float: right; font-family: var(--mono, monospace); font-size: .85rem; }
|
||||||
|
.sheet .doc-meta { font-size: .85rem; color: var(--ink-2, #6b6b6b); }
|
||||||
|
.sheet .doc-seal { margin-top: 1.2rem; text-align: right; color: var(--stamp, #a11212); font-weight: 700; letter-spacing: .15em; }
|
||||||
|
.sheet table { width: 100%; border-collapse: collapse; margin: .8rem 0; }
|
||||||
|
.sheet th, .sheet td { border: 1px solid var(--rule, #d8c4b0); padding: .35rem .6rem; text-align: left; }
|
||||||
|
.sheet th { background: var(--wash, #f7f1e8); }
|
||||||
|
.doc-list { list-style: none; padding: 0; }
|
||||||
|
.doc-list li { display: flex; gap: .8rem; padding: .35rem 0; border-bottom: 1px dashed var(--rule, #d8c4b0); }
|
||||||
|
.doc-list time { color: var(--ink-2, #6b6b6b); font-family: var(--mono, monospace); font-size: .85rem; white-space: nowrap; }
|
||||||
|
|
||||||
|
/* ── M11 world texture ── */
|
||||||
|
.handwrite { font-family: var(--hand, "Kaiti SC", "STKaiti", cursive); color: var(--stamp, #a11212); }
|
||||||
|
.hidden-text { background: #000; color: #000; user-select: text; }
|
||||||
|
.hidden-text::selection { color: #f00; background: #333; } /* visible only when selected */
|
||||||
|
.blurred { filter: blur(6px); user-select: none; } /* redaction is itself a clue */
|
||||||
|
.spacer { height: 180px; } /* whitespace as pacing */
|
||||||
|
|
||||||
|
/* ── Gate form (M2) ─────────────────────────────────────────────────────────────
|
||||||
|
The input names its field and stops there: a placeholder that carries the answer leaks it (R4).
|
||||||
|
Failure copy points at the source obliquely ("密码错误 🎂"), never at the derivation. */
|
||||||
|
.gate { max-width: 22rem; margin: 2rem auto; }
|
||||||
|
.gate label { display: block; font-family: var(--sans, system-ui, sans-serif); font-size: .85rem; margin: .9rem 0 .25rem; }
|
||||||
|
.gate input, .gate select {
|
||||||
|
width: 100%; padding: .45rem .6rem; font: inherit;
|
||||||
|
border: 1px solid var(--rule, #d8c4b0); background: var(--paper-2, #fffdf9); color: var(--ink, #4a4a4a);
|
||||||
|
}
|
||||||
|
.gate input:focus-visible, .gate select:focus-visible { outline: 2px solid var(--accent, #c1440e); outline-offset: 1px; }
|
||||||
|
.gate-error { color: var(--stamp, #a11212); font-size: .85rem; min-height: 1.2em; margin: .5rem 0 0; }
|
||||||
|
.btn {
|
||||||
|
display: inline-block; padding: .45rem 1.4rem; font: inherit; cursor: pointer;
|
||||||
|
border: 1px solid var(--accent, #c1440e); background: var(--accent, #c1440e); color: var(--paper, #f9ebde);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.btn:hover { filter: brightness(1.08); }
|
||||||
|
.btn.ghost { background: transparent; color: var(--accent, #c1440e); }
|
||||||
|
|
||||||
|
/* ── Entry shell (M9): the cover before the site ───────────────────────────────
|
||||||
|
No site header here — the player has not entered yet. Copy stays light: role assignment, the
|
||||||
|
system's own one-liner, and the start button. */
|
||||||
|
.entry { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 2rem 1rem; }
|
||||||
|
.entry h1 { font-size: 2rem; margin: 0 0 .6rem; letter-spacing: .04em; }
|
||||||
|
.entry .entry-lede { max-width: 30rem; color: var(--ink-2, #6b6b6b); margin: 0 0 2rem; }
|
||||||
|
.entry .entry-role { font-family: var(--sans, system-ui, sans-serif); font-size: .9rem; color: var(--ink-2, #6b6b6b); margin: -1rem 0 2rem; }
|
||||||
|
/* The entry's about link: quiet, below the start button. */
|
||||||
|
.entry .entry-meta { margin: 1.4rem 0 0; font-family: var(--sans, system-ui, sans-serif); font-size: .78rem; }
|
||||||
|
.entry .entry-meta a { color: var(--ink-2, #6b6b6b); }
|
||||||
|
|
||||||
|
/* ── About page: a plain column, no document pretense ── */
|
||||||
|
.about { max-width: 40rem; }
|
||||||
|
.about h2 { font-size: 1.05rem; margin: 1.8rem 0 .5rem; }
|
||||||
|
.about p { margin: .6rem 0; }
|
||||||
|
|
||||||
|
/* ── Staging (M12): full-page overlays own the viewport while they hold it ── */
|
||||||
|
.blackout {
|
||||||
|
position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center;
|
||||||
|
background: #000; color: #bbb; font-family: var(--mono, monospace); letter-spacing: .1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Small screens: the bar wraps, the column narrows, nothing clips ── */
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
header nav { gap: .2rem .7rem; }
|
||||||
|
header nav form { margin-left: 0; width: 100%; }
|
||||||
|
main { padding: 1.1rem .9rem 2.4rem; }
|
||||||
|
.sheet { padding: 1rem; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/* secret.css — M5 deep-layer skin. Linked by deep pages only (or applied via the session reskin
|
||||||
|
* token; references/structure/components.md §5). Retunes the custom properties base.css reads, so
|
||||||
|
* layout stays in base.css and the whole page shifts together: background, text, rules, accents.
|
||||||
|
* The fiction's own palette goes here — the values below are a placeholder for a cold archive mood. */
|
||||||
|
:root {
|
||||||
|
--ink: #9e9e9e; --ink-2: #6f6f6f;
|
||||||
|
--paper: #1a1a1c; --paper-2: #232326;
|
||||||
|
--rule: #3a3a3e; --wash: #202023;
|
||||||
|
--accent: #b3261e; --stamp: #db1400;
|
||||||
|
--bar: #141416;
|
||||||
|
}
|
||||||
|
body { background: #1a1a1c; color: #9e9e9e; } /* the visible whole-page shift (step 8) */
|
||||||
|
body.secret { background: #1a1a1c; color: #9e9e9e; } /* class applied by session.skin() at boot */
|
||||||
|
body.secret h2 { color: #db1400; }
|
||||||
|
body.secret a { color: #b3261e; }
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* surface.css — the public skin of a make-my-arg project.
|
||||||
|
*
|
||||||
|
* The ONE file that carries the fiction's palette and type; base.css above stays structure-only.
|
||||||
|
* Set these tokens from the system's own era and register (a county gazette, a hospital intranet, a
|
||||||
|
* 1990s BBS), then leave them alone — a page that needs its own colour is a page that has left the
|
||||||
|
* disguise. Deep pages link secret.css instead (M5); when the profile did not select M5, this file
|
||||||
|
* is the whole skin.
|
||||||
|
*
|
||||||
|
* Palette vocabulary, shared with the site-graph viewer so a generated graph reads as the same
|
||||||
|
* paper: --paper --ink --rule --stamp. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--paper: #f9ebde; /* page ground */
|
||||||
|
--paper-2: #fffdf9; /* document ground, one step off the page */
|
||||||
|
--wash: #f7f1e8; /* table headers, quiet fills */
|
||||||
|
--ink: #4a4a4a; /* body text */
|
||||||
|
--ink-2: #6b6b6b; /* secondary text, footer */
|
||||||
|
--rule: #d8c4b0; /* hairlines and borders */
|
||||||
|
--accent: #c1440e; /* links, buttons, the one warm accent */
|
||||||
|
--stamp: #a11212; /* seals, warnings, forbidden-state copy */
|
||||||
|
--bar: #f3e2d0; /* sticky header ground — must stay opaque */
|
||||||
|
|
||||||
|
--serif: "Songti SC", "SimSun", "Noto Serif CJK SC", Georgia, serif; /* prose */
|
||||||
|
--sans: "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", system-ui, sans-serif; /* nav, data, UI */
|
||||||
|
--mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; /* numbers, dockets */
|
||||||
|
--hand: "Kaiti SC", "STKaiti", "KaiTi", cursive; /* hand-copied notes */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--paper);
|
||||||
|
color: var(--ink);
|
||||||
|
font-family: var(--serif);
|
||||||
|
font-size: 16px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
a { color: var(--accent); }
|
||||||
|
a:hover { text-decoration: none; }
|
||||||
|
|
||||||
|
/* Headings sit in the same serif as the body: a real system does not set its own notices in a
|
||||||
|
display face. Weight and size carry the hierarchy instead. */
|
||||||
|
h1, h2, h3 { font-weight: 700; line-height: 1.35; }
|
||||||
|
|
||||||
|
/* Quiet, tabular figures anywhere the system prints a number: dockets, dates, counts. */
|
||||||
|
time, .num, .progress, .doc-no { font-family: var(--mono, monospace); font-variant-numeric: tabular-nums; }
|
||||||
|
|
||||||
|
/* Selection keeps the paper register; the M11 black-on-black text overrides it deliberately. */
|
||||||
|
::selection { background: var(--rule); color: var(--ink); }
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
// components.js — the shared kernel every page of a make-my-arg project loads.
|
||||||
|
//
|
||||||
|
// Loaded by every page BEFORE the vendored Alpine runtime (script order: base.md §2). It carries
|
||||||
|
// only what never changes between projects: the md5 primitive, the hash helper, the session store,
|
||||||
|
// and the readState store. The module components themselves are registered on alpine:init from the
|
||||||
|
// template files in the skill repo (assets/templates/js/modules/) — copy in ONLY the components
|
||||||
|
// docs/system-profile.md selected and delete the rest: shipping machinery the profile did not
|
||||||
|
// select is a module drift (references/common-mistakes.md §6). Never redefine hash / session /
|
||||||
|
// readState inside a copied body — this file is their single home.
|
||||||
|
//
|
||||||
|
// Hash contract (four sides, byte-identical — references/structure/tooling.md §3 item 4):
|
||||||
|
// this helper · tools/hash.mjs · tools/build-keywords.mjs · tools/check-credentials.mjs
|
||||||
|
// String(w).trim().toLowerCase() -> md5 -> base64
|
||||||
|
// Switching algorithms means editing all of them in one change, then re-generating every table
|
||||||
|
// built under the old rule (R3).
|
||||||
|
|
||||||
|
/* ── md5 (RFC 1321) ──────────────────────────────────────────────────────────────
|
||||||
|
Synchronous and dependency-free: WebCrypto has no MD5, so the primitive lives here. The output
|
||||||
|
is byte-identical to node:crypto's md5, which is what the tools above compute. */
|
||||||
|
const md5 = (input) => {
|
||||||
|
const msg = new TextEncoder().encode(String(input));
|
||||||
|
const blocks = Math.ceil((msg.length + 9) / 64); // 0x80 pad + 8-byte length
|
||||||
|
const buf = new Uint8Array(blocks * 64);
|
||||||
|
buf.set(msg);
|
||||||
|
buf[msg.length] = 0x80;
|
||||||
|
const view = new DataView(buf.buffer);
|
||||||
|
const bits = msg.length * 8; // safe below 2^53 bits
|
||||||
|
view.setUint32(buf.length - 8, bits >>> 0, true);
|
||||||
|
view.setUint32(buf.length - 4, Math.floor(bits / 4294967296), true);
|
||||||
|
|
||||||
|
// Per-step rotate amounts and the sine-derived constant table (RFC 1321 T[1..64]).
|
||||||
|
const S = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||||
|
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||||
|
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||||
|
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
|
||||||
|
const K = Array.from({ length: 64 }, (_, i) => Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296));
|
||||||
|
|
||||||
|
let a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
|
||||||
|
for (let off = 0; off < buf.length; off += 64) {
|
||||||
|
const M = Array.from({ length: 16 }, (_, i) => view.getUint32(off + i * 4, true));
|
||||||
|
let A = a0, B = b0, C = c0, D = d0;
|
||||||
|
for (let i = 0; i < 64; i++) {
|
||||||
|
let F, g;
|
||||||
|
if (i < 16) { F = (B & C) | (~B & D); g = i; }
|
||||||
|
else if (i < 32) { F = (D & B) | (~D & C); g = (5 * i + 1) % 16; }
|
||||||
|
else if (i < 48) { F = B ^ C ^ D; g = (3 * i + 5) % 16; }
|
||||||
|
else { F = C ^ (B | ~D); g = (7 * i) % 16; }
|
||||||
|
F = (F + A + K[i] + M[g]) | 0;
|
||||||
|
A = D; D = C; C = B;
|
||||||
|
B = (B + ((F << S[i]) | (F >>> (32 - S[i])))) | 0; // rotate left, then add
|
||||||
|
}
|
||||||
|
a0 = (a0 + A) | 0; b0 = (b0 + B) | 0; c0 = (c0 + C) | 0; d0 = (d0 + D) | 0;
|
||||||
|
}
|
||||||
|
const hex = [];
|
||||||
|
for (const v of [a0, b0, c0, d0])
|
||||||
|
for (let i = 0; i < 4; i++) hex.push(((v >>> (i * 8)) & 0xff).toString(16).padStart(2, '0'));
|
||||||
|
return hex.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── hash: the one helper gate inputs and keyword tables are keyed by ─────────────
|
||||||
|
Mirrors tools/hash.mjs exactly. md5 is hex here, base64 out — never mix the two forms. */
|
||||||
|
const hash = (w) => btoa(String.fromCharCode(...md5(String(w).trim().toLowerCase()).match(/../g).map((h) => parseInt(h, 16))));
|
||||||
|
|
||||||
|
/* ── Session store ──────────────────────────────────────────────────────────────
|
||||||
|
Access state MUST survive a result opened in a new tab (target="_blank"): per-tab
|
||||||
|
sessionStorage does not, so the state lives in a session cookie (no max-age/expires → cleared
|
||||||
|
when the browser closes) with sessionStorage as the private-mode fallback. Rename ACCESS_KEY
|
||||||
|
per project. Session state is technical, never explained to the player. */
|
||||||
|
const ACCESS_KEY = 'access', SKIN_KEY = 'skin';
|
||||||
|
const session = {
|
||||||
|
_read(key) {
|
||||||
|
const m = document.cookie.match(new RegExp('(?:^|; )' + key + '=([^;]*)'));
|
||||||
|
if (m) { try { return JSON.parse(decodeURIComponent(m[1])); } catch (e) { /* fall through */ } }
|
||||||
|
try { return JSON.parse(sessionStorage.getItem(key) || 'null'); } catch (e) { return null; }
|
||||||
|
},
|
||||||
|
_write(key, val) {
|
||||||
|
const v = encodeURIComponent(JSON.stringify(val));
|
||||||
|
document.cookie = `${key}=${v}; path=/; SameSite=Lax`; // session cookie: deliberately no expiry
|
||||||
|
try { sessionStorage.setItem(key, JSON.stringify(val)); } catch (e) { /* private mode */ }
|
||||||
|
},
|
||||||
|
access() { return this._read(ACCESS_KEY) || []; },
|
||||||
|
grant(ids) { this._write(ACCESS_KEY, [...new Set([...this.access(), ...ids])]); },
|
||||||
|
reskin(name) { if (name) this._write(SKIN_KEY, name); }, // optional: a skin token deep pages apply
|
||||||
|
skin() { return this._read(SKIN_KEY); },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── Read-state store (M14) ─────────────────────────────────────────────────
|
||||||
|
World-response state (references/guardrails.md R13): an entry appears once its source page was
|
||||||
|
read. Persist ONLY when docs/system-profile.md records the persistent/world-response contract
|
||||||
|
(references/structure/form-device.md §3); on a remote system swap localStorage for sessionStorage.
|
||||||
|
The checker-visible contract is the data-after attribute on the anchor — this store is what the
|
||||||
|
DOM render reads. Storage is technical, never explained to the player. */
|
||||||
|
const READ_KEY = 'read'; // rename per project
|
||||||
|
const readState = {
|
||||||
|
mark(page) {
|
||||||
|
try { const s = new Set(JSON.parse(localStorage.getItem(READ_KEY) || '[]')); s.add(page);
|
||||||
|
localStorage.setItem(READ_KEY, JSON.stringify([...s])); } catch (e) { /* private mode */ }
|
||||||
|
},
|
||||||
|
has(page) {
|
||||||
|
try { return JSON.parse(localStorage.getItem(READ_KEY) || '[]').includes(page); } catch (e) { return false; }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── Component registration ─────────────────────────────────────────────────────
|
||||||
|
The vendored runtime auto-starts right after its own script tag, so registrations queue on the
|
||||||
|
alpine:init event it dispatches before the DOM walk. Register ONLY what the profile selected;
|
||||||
|
each component body ships as a real file in the skill repo under assets/templates/js/modules/
|
||||||
|
(step 6a copies the selected bodies into this listener; mount markup: assets/templates/html/, rules:
|
||||||
|
references/structure/components.md):
|
||||||
|
search.js (M1) · x-data="search", data-index="data/keywords.surface.json"
|
||||||
|
gate.js (M2) · x-data="gate", data-expect-hash on each input, data-next / x-show="unlocked"
|
||||||
|
access.js (M3) · x-data="access", data-access on the protected page (references/structure/form-system.md)
|
||||||
|
staging.js (M12) · blackout / typewriter / reveal — timers released in destroy()
|
||||||
|
progress.js (M7) · footer span, data-page / data-total on <body>
|
||||||
|
read-state.js (M14) · x-data="markRead(page)" on the source page; anchors carry data-after
|
||||||
|
(references/structure/form-device.md §3–§4)
|
||||||
|
phone.js (device class phone) · x-data="phone" on the shell
|
||||||
|
M5 reskin needs no component here: deep pages link secret.css directly and session.reskin() above
|
||||||
|
carries the token. The stores above (hash / session / readState) are THIS file — copied bodies
|
||||||
|
depend on them; never redefine one inside a component. No component may route or switch scenes:
|
||||||
|
Alpine owns in-page lifecycle only (R1). */
|
||||||
|
/* Project registrations — docs/system-profile.md (Container A + nested D, pure-web):
|
||||||
|
search (M1) · gate (M2, shapes A/B/C incl. login-multi) · access (M3) · staging (M12) ·
|
||||||
|
progress (M7, SESSION MODE) · markRead (M14).
|
||||||
|
State contract: login = kernel session cookie; progress = none persistent — the M7 footer
|
||||||
|
numbering runs off a session-scoped seen list (the template's localStorage swapped to
|
||||||
|
sessionStorage per references/structure/form-device.md §3 / components.md §6: remote system,
|
||||||
|
cleared on browser close, no save/continue/wipe boot surface); world-response = kernel readState
|
||||||
|
store + markRead + data-after anchors (2 edges, landed in 6b).
|
||||||
|
Out of the game per the profile: phone.js (pure-web), forbidden.css (M6 off — data/forbidden.json
|
||||||
|
ships as an empty zero-entry table ONLY because the verbatim search.js fetches it unconditionally;
|
||||||
|
no forbidden state can fire and no M6 skin is linked). Bodies below are copied verbatim from
|
||||||
|
assets/templates/js/modules/ except the one sanctioned storage swap in progress.
|
||||||
|
No component routes or switches scenes (R1). */
|
||||||
|
document.addEventListener('alpine:init', () => {
|
||||||
|
// M1 search — Alpine component body. Depends on the starter kernel's hash()
|
||||||
|
// (assets/starter/assets/js/components.js, byte-identical to tools/hash.mjs).
|
||||||
|
// Copy into the project's components.js inside the alpine:init listener when the profile selects M1.
|
||||||
|
// Mount shape: assets/templates/html/search.html (layer scoping, R8; result titles are catalog
|
||||||
|
// entries, R9; see references/structure/components.md §2).
|
||||||
|
Alpine.data('search', () => ({
|
||||||
|
q: '',
|
||||||
|
state: 'empty', // empty | forbidden | hit | miss
|
||||||
|
results: [],
|
||||||
|
forbidden: null,
|
||||||
|
|
||||||
|
async init() { // Alpine lifecycle: runs before the component renders
|
||||||
|
this.q = new URLSearchParams(location.search).get('q')?.trim().toLowerCase() ?? '';
|
||||||
|
if (!this.q) return;
|
||||||
|
const enc = hash(this.q);
|
||||||
|
|
||||||
|
const forbiddenTable = await (await fetch('data/forbidden.json')).json();
|
||||||
|
const hit = Object.values(forbiddenTable).find(f => f.keywords.includes(enc));
|
||||||
|
if (hit) { // State 3: forbidden — full-page reskin (background/title/logo/footer)
|
||||||
|
this.forbidden = hit;
|
||||||
|
this.state = 'forbidden';
|
||||||
|
document.body.classList.add('body-forbidden');
|
||||||
|
document.querySelector('.progress').textContent = 'ex/36';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = await (await fetch(this.$el.dataset.index)).json();
|
||||||
|
this.results = map[enc] ?? [];
|
||||||
|
this.state = this.results.length ? 'hit' : 'miss';
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// M2 gate — Alpine component body. Depends on the starter kernel's hash() and session store
|
||||||
|
// (assets/starter/assets/js/components.js) — do NOT redefine them here; the session cookie helper
|
||||||
|
// and ACCESS_KEY/SKIN_KEY live in the kernel, shared with the access component (references/structure/components.md §3).
|
||||||
|
// Shapes and mount markup: assets/templates/html/gate-a.html (navigate on success),
|
||||||
|
// gate-b.html (unlock in place — the post-gate block must carry x-show="unlocked", the marker
|
||||||
|
// check-solvable.mjs keys on), login-multi.html (system containers, data-grants map).
|
||||||
|
// Credential triad + design rules (R3, R4): references/structure/components.md §3.
|
||||||
|
Alpine.data('gate', () => ({
|
||||||
|
error: '', busy: false, unlocked: false, successText: '', _t: null,
|
||||||
|
init() { this.successText = this.$el.dataset.successText || ''; },
|
||||||
|
submit() {
|
||||||
|
const el = this.$root; // data-* live on the component root; the submit event's target is the <form>
|
||||||
|
const inputs = Array.from(el.querySelectorAll('input[data-expect-hash],input[data-expect-any]'));
|
||||||
|
// Single-box multi-identity (login-multi.html). data-grants is a JSON map on the root:
|
||||||
|
// { "<account-hash>": { "id": "intern", "pw": ["<pw-hash>", …], "reskin": "secret" }, … }
|
||||||
|
// It resolves WHICH identity the typed account grants and validates the password against that identity,
|
||||||
|
// so one login box can serve several roles without splitting into several forms on the page. data-grant
|
||||||
|
// stays on the root as the flat list of identities check-solvable walks; data-grants is the runtime truth.
|
||||||
|
let ok, grantIds = [];
|
||||||
|
if (el.dataset.grants) {
|
||||||
|
const map = JSON.parse(el.dataset.grants);
|
||||||
|
const acc = inputs.find((i) => i.type !== 'password');
|
||||||
|
const pw = inputs.find((i) => i.type === 'password');
|
||||||
|
const entry = acc ? map[hash(acc.value || '')] : null;
|
||||||
|
ok = !!entry && (!pw || (entry.pw || []).includes(hash(pw.value || ''))); // account+password are paired
|
||||||
|
if (ok) { grantIds = entry.id ? [entry.id] : []; session.reskin(entry.reskin); }
|
||||||
|
} else {
|
||||||
|
// Legacy / multi-field gates: every data-expect-hash input must match; data-expect-any accepts any
|
||||||
|
// non-empty value (a declared wildcard, references/structure/form-device.md §4); data-grant is unconditional.
|
||||||
|
ok = inputs.length > 0 && inputs.every((inp) => {
|
||||||
|
if (inp.dataset.expectAny) return (inp.value || '').trim() !== '';
|
||||||
|
const expects = (inp.dataset.expectHash || '').split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
return expects.includes(hash(inp.value || ''));
|
||||||
|
});
|
||||||
|
grantIds = (el.dataset.grant || '').split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
if (grantIds.length) session.grant(grantIds);
|
||||||
|
const next = el.dataset.next;
|
||||||
|
const hold = parseInt(el.dataset.successHold || '0', 10);
|
||||||
|
const staged = this.successText && hold > 0;
|
||||||
|
const finish = () => { if (next) location.href = next; else { this.busy = false; this.unlocked = true; } };
|
||||||
|
if (staged) { this.busy = true; this._t = setTimeout(finish, hold); } else finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.error = el.dataset.failHint || 'Verification failed.'; // inline red text; a bare alert breaks the facade
|
||||||
|
el.animate([{ transform: 'translateX(0)' }, { transform: 'translateX(-6px)' },
|
||||||
|
{ transform: 'translateX(6px)' }, { transform: 'translateX(0)' }], 320);
|
||||||
|
},
|
||||||
|
destroy() { clearTimeout(this._t); },
|
||||||
|
}));
|
||||||
|
|
||||||
|
// M3 access — Alpine component body. Depends on the starter kernel's session store
|
||||||
|
// (assets/starter/assets/js/components.js — cookie-first, sessionStorage fallback in private mode);
|
||||||
|
// do NOT redefine session or hash here. Copy into the project's components.js inside the
|
||||||
|
// alpine:init listener when the profile selects M3 (system containers B/C/D).
|
||||||
|
// Mount shape: assets/templates/html/access-protected.html (references/structure/form-system.md §3).
|
||||||
|
// The x-show="unlocked" block is the marker check-solvable.mjs keys on; a page opens ONLY to the
|
||||||
|
// accounts it names — no privilege ladder (R10).
|
||||||
|
Alpine.data('access', () => ({
|
||||||
|
unlocked: false,
|
||||||
|
init() {
|
||||||
|
const held = session.access(); // kernel helper: cross-tab session cookie
|
||||||
|
const need = (this.$el.dataset.access || '').split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
this.unlocked = need.some((r) => (r === '*' ? held.length > 0 : held.includes(r)));
|
||||||
|
const skin = session.skin(); // optional reskin token granted at login (M5)
|
||||||
|
if (skin) document.body.classList.add(skin);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// M12 interaction staging — Alpine component bodies: blackout / typewriter / reveal.
|
||||||
|
// All timers and observers are registered in init() and released in destroy(), so a staged
|
||||||
|
// effect survives or dies with its component. Copy the selected bodies into the project's
|
||||||
|
// components.js inside the alpine:init listener when the profile selects M12.
|
||||||
|
// Sensory variants (audio puzzle, flashlight mask) live in the fiction's own page; the same
|
||||||
|
// lifecycle rule holds. references/design-playbook.md §2 M12; declare hardware on the entry page (step 8).
|
||||||
|
|
||||||
|
// "The system has noticed you": delayed blackout after unlock; optional `next` navigates 5s later.
|
||||||
|
Alpine.data('blackout', (delay = 3000, text = '', next = null) => ({
|
||||||
|
visible: false, text, showTimer: null, exitTimer: null,
|
||||||
|
init() {
|
||||||
|
this.showTimer = setTimeout(() => {
|
||||||
|
this.visible = true;
|
||||||
|
if (next) this.exitTimer = setTimeout(() => (location.href = next), 5000);
|
||||||
|
}, delay);
|
||||||
|
},
|
||||||
|
destroy() { clearTimeout(this.showTimer); clearTimeout(this.exitTimer); },
|
||||||
|
}));
|
||||||
|
// mount: <div class="blackout" x-data="blackout(4000, 'The system has noticed you')" x-show="visible" x-transition x-cloak x-text="text"></div>
|
||||||
|
|
||||||
|
// Delete the element's original text character by character, then retype newText — ceremonial effect.
|
||||||
|
Alpine.data('typewriter', (newText, speed = 60) => ({
|
||||||
|
output: '', timer: null,
|
||||||
|
init() {
|
||||||
|
const original = this.$el.textContent;
|
||||||
|
let i = original.length;
|
||||||
|
this.output = original;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.output = original.slice(0, --i);
|
||||||
|
if (i > 0) return;
|
||||||
|
clearInterval(this.timer);
|
||||||
|
let j = 0;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.output = newText.slice(0, ++j);
|
||||||
|
if (j >= newText.length) clearInterval(this.timer);
|
||||||
|
}, speed);
|
||||||
|
}, speed);
|
||||||
|
},
|
||||||
|
destroy() { clearInterval(this.timer); },
|
||||||
|
}));
|
||||||
|
// mount: <p x-data="typewriter('Access granted. Do not look back.')" x-text="output"></p>
|
||||||
|
|
||||||
|
// Scroll reveal: IntersectionObserver owned by the component, controlling reading pace.
|
||||||
|
Alpine.data('reveal', () => ({
|
||||||
|
shown: false, observer: null,
|
||||||
|
init() {
|
||||||
|
this.observer = new IntersectionObserver(([entry]) => {
|
||||||
|
if (!entry.isIntersecting) return;
|
||||||
|
this.shown = true;
|
||||||
|
this.observer.disconnect();
|
||||||
|
});
|
||||||
|
this.observer.observe(this.$el);
|
||||||
|
},
|
||||||
|
destroy() { this.observer?.disconnect(); },
|
||||||
|
}));
|
||||||
|
// mount: <div class="reveal" :class="{ shown }" x-data="reveal">…</div> .reveal{opacity:0;transition:opacity .6s}.reveal.shown{opacity:1}
|
||||||
|
|
||||||
|
// M14 read-state — the runtime side of world-response state (references/guardrails.md R13): an entry
|
||||||
|
// appears once its source page was read. The STORE (readState + READ_KEY) lives in the starter kernel
|
||||||
|
// (assets/starter/assets/js/components.js) — never redefine it here. This file adds only the mount
|
||||||
|
// component: the SOURCE page marks itself as read by carrying it.
|
||||||
|
// The CHECKER-visible contract is the data-after attribute on the entry anchor
|
||||||
|
// (assets/templates/html/read-entry.html): keep the page path written in data-after and in the
|
||||||
|
// markRead(...) argument identical, or the walk proves an order the DOM does not render
|
||||||
|
// (references/structure/form-device.md §4).
|
||||||
|
// Persist ONLY when docs/system-profile.md records the persistent/world-response contract; the kernel
|
||||||
|
// store is where `progress: session` swaps localStorage for sessionStorage.
|
||||||
|
//
|
||||||
|
// Mount on the SOURCE page's <main> so opening it marks the read:
|
||||||
|
// <main x-data="markRead('pages/forum/thread-fenglou.html')">
|
||||||
|
Alpine.data('markRead', (page) => ({ init() { readState.mark(page); } }));
|
||||||
|
|
||||||
|
// M7 collection progress — Alpine component body (assets/templates/js/modules/progress.js).
|
||||||
|
// State contract: NO persistence — this is the template's verbatim body with the single
|
||||||
|
// sanctioned storage swap localStorage -> sessionStorage (references/structure/form-device.md §3:
|
||||||
|
// session-only or none for a remote system; docs/system-profile.md records `none`).
|
||||||
|
// Mount on the shared footer: <footer x-data="progress"> … <span class="progress"
|
||||||
|
// x-text="seen.length + '/' + document.body.dataset.total">. <body data-page data-total> carries
|
||||||
|
// the numbering. The static span text stays as the no-JS / pre-boot fallback.
|
||||||
|
Alpine.data('progress', () => ({
|
||||||
|
seen: [],
|
||||||
|
init() {
|
||||||
|
this.seen = JSON.parse(sessionStorage.getItem('seen') ?? '[]');
|
||||||
|
const n = document.body.dataset.page;
|
||||||
|
if (n && !this.seen.includes(n)) this.seen.push(n);
|
||||||
|
sessionStorage.setItem('seen', JSON.stringify(this.seen));
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
});
|
||||||
Vendored
+21
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/surface.css">
|
||||||
|
<script defer src="assets/js/components.js"></script>
|
||||||
|
<script defer src="assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- M9 entry shell. 文案在步骤 7 定稿;6a 只落结构与中性占位。 -->
|
||||||
|
<main class="entry">
|
||||||
|
<h1>临川县正衡律师事务所</h1>
|
||||||
|
<p class="entry-lede">法律咨询、刑事辩护、民事代理与法律援助——江州省临川县司法行政机关核准设立的执业机构。</p>
|
||||||
|
<!-- M9 角色指派(占位文案,步骤 7 校订) -->
|
||||||
|
<p class="entry-role">所内通知:新聘律师助理于本日到所报到,先行熟悉法律援助案件卷宗。</p>
|
||||||
|
<!-- 本 profile 未选择规则面(无耳机/外部检索/时段要求),故此处无规则占位。 -->
|
||||||
|
<p><a class="btn" href="pages/home.html">进入</a></p>
|
||||||
|
<!-- 指向生成此项目的 skill 仓库的引用链接,请保留。 -->
|
||||||
|
<p class="entry-meta"><a href="https://github.com/XingfenD/novel2arg">github.com/XingfenD/novel2arg</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>关于正衡 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="2" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>关于正衡</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为机构简介栏位占位。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">2/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>法律援助专栏 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="6" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>法律援助专栏</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为法援专栏条目页模板占位。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">6/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>法律援助 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="5" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>法律援助</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<ul class="doc-list">
|
||||||
|
<li><a href="aid-case.html">法援专栏条目占位</a></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">5/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>释法案例 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="7" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>释法案例</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为公开生效文书中性档案栏位占位。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">7/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>联系我们 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="9" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>联系我们</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为联系方式栏位占位(地址、电话、办公时间)。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">9/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>首页 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="1" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>首页</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为所内首页栏位占位。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">1/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>卷库房 · 案卷目录 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../../assets/css/secret.css">
|
||||||
|
<script defer src="../../assets/js/components.js"></script>
|
||||||
|
<script defer src="../../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="15" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="login.html">卷库房</a>
|
||||||
|
<a href="query.html">案卷查询</a><a href="results.html">检索结果</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- 卷宗详情页模板(access-protected 形,assets/templates/html/access-protected.html):
|
||||||
|
6b 按触达表逐卷复制为 s01-dossier.html … 编号式不剧透文件名;
|
||||||
|
每页 data-access 只写该卷承办的账号,无级别阶梯(R10);正文=公文表格排版(R6)。 -->
|
||||||
|
<main x-data="access" data-access="handler">
|
||||||
|
<div x-show="!unlocked">
|
||||||
|
<p>本卷仅限承办人员查阅。请先登录。</p>
|
||||||
|
<p><a href="login.html">卷库房登录</a></p>
|
||||||
|
</div>
|
||||||
|
<div x-show="unlocked" x-cloak>
|
||||||
|
<h1>案卷目录</h1>
|
||||||
|
<article class="sheet">
|
||||||
|
<div class="doc-head"><span class="doc-no">〔档号占位〕</span><h2>〔案卷标题占位〕</h2><p class="doc-meta">立档单位:临川县正衡律师事务所 | 状态:在库</p></div>
|
||||||
|
<p>〔卷内文书清单占位——6b 按触达表填入,一文书一深页〕</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所 · 卷库房</span><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">15/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>卷库房 · 登录 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../../assets/css/secret.css">
|
||||||
|
<script defer src="../../assets/js/components.js"></script>
|
||||||
|
<script defer src="../../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="12" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="login.html">卷库房</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- Shape C 登录门(assets/templates/html/login-multi.html):一个登录框、多个身份,不拆两个表单(R10)。
|
||||||
|
data-grant = 走查看到的身份平列;data-grants = 运行时 账号→身份 映射。账号哈希与密码哈希由步骤 3/5 设计、
|
||||||
|
6b 填入;此处只落壳,不贴任何真实凭据(R3/R4)。账号格式可照实贴出,密码推导永不出现(R4)。 -->
|
||||||
|
<main x-data="gate" data-grant="handler,intern" data-next="query.html" data-fail-hint="账号或密码有误"
|
||||||
|
data-grants='{"<hash:account-1>":{"id":"handler","pw":["<hash:pw-1>"]},
|
||||||
|
"<hash:account-2>":{"id":"intern","pw":["<hash:pw-2>"],"reskin":"secret"}}'>
|
||||||
|
<form class="gate" @submit.prevent="submit">
|
||||||
|
<label for="u">账号</label><input id="u" type="text" name="account" placeholder="工号" data-expect-hash="<hash:account-1>,<hash:account-2>">
|
||||||
|
<label for="p">密码</label><input id="p" type="password" name="password" placeholder="密码" data-expect-hash="<hash:pw-1>,<hash:pw-2>">
|
||||||
|
<p class="gate-error" x-show="error" x-text="error" x-cloak></p>
|
||||||
|
<button type="submit">登录</button>
|
||||||
|
</form>
|
||||||
|
<p><a href="../home.html">返回所网首页</a></p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所 · 卷库房</span><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">12/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>卷库房 · 案卷查询 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../../assets/css/secret.css">
|
||||||
|
<script defer src="../../assets/js/components.js"></script>
|
||||||
|
<script defer src="../../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="13" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="login.html">卷库房</a>
|
||||||
|
<a href="query.html">案卷查询</a><a href="results.html">检索结果</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- 嵌套 D 查询壳(references/structure/form-system.md §4):多字段查询 = 多 data-expect-hash 门,
|
||||||
|
全部字段命中才提交到 results.html。各字段接受值(当事人/编号等)由步骤 3/5 设计、6b 填入;
|
||||||
|
输入只名其字段(R4)。表单 action 是检查器跟走的真实跳转(form-system.md §5)。 -->
|
||||||
|
<main x-data="gate" data-next="results.html" data-fail-hint="未检索到匹配案卷">
|
||||||
|
<form class="gate" action="results.html" method="get" @submit.prevent="submit">
|
||||||
|
<label for="party">当事人</label><input id="party" type="text" name="party" placeholder="当事人" data-expect-hash="<hash>">
|
||||||
|
<label for="docket">案号</label><input id="docket" type="text" name="docket" placeholder="案号" data-expect-hash="<hash>">
|
||||||
|
<label for="year">年度</label><input id="year" type="text" name="year" placeholder="立案年度" data-expect-hash="<hash>">
|
||||||
|
<p class="gate-error" x-show="error" x-text="error" x-cloak></p>
|
||||||
|
<button class="btn" type="submit">查询</button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所 · 卷库房</span><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">13/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>卷库房 · 检索结果 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../../assets/css/secret.css">
|
||||||
|
<script defer src="../../assets/js/components.js"></script>
|
||||||
|
<script defer src="../../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="14" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="login.html">卷库房</a>
|
||||||
|
<a href="query.html">案卷查询</a><a href="results.html">检索结果</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- 结果页(D 型):查询命中经账号矩阵解析(form-system.md §1/R10)——
|
||||||
|
每行目录条目 = 发文/立档单位 + 案卷类别 + 档号(R9,6b 填);
|
||||||
|
不可开行只出纯锁定告示,永不直接开文档(R8);
|
||||||
|
M14 补录行用 read-entry 形:锚点带 CONFIG.afterAttr 属性、值为来源页根相对路径,未读前该行整体不渲染
|
||||||
|
(assets/templates/html/read-entry.html,profile 记录共 2 条边,6b 定页)。 -->
|
||||||
|
<main x-data="access" data-access="*">
|
||||||
|
<h1>检索结果</h1>
|
||||||
|
<div x-show="!unlocked">
|
||||||
|
<p>请先登录。</p>
|
||||||
|
<p><a href="login.html">卷库房登录</a></p>
|
||||||
|
</div>
|
||||||
|
<div x-show="unlocked" x-cloak>
|
||||||
|
<table class="sheet">
|
||||||
|
<thead><tr><th>档号</th><th>案卷类别</th><th>状态</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>〔6b 填目录条目〕</td><td>—</td><td>—</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!-- M14 读后出现边(profile 记录 2 条,6b 定页):条目行用 read-entry.html 形——
|
||||||
|
锚点带 CONFIG.afterAttr 属性、值为来源页根相对路径,x-show 读 readState.has(同一页路径);
|
||||||
|
未读前该行整体不渲染。示例 markup 见 skill 的 assets/templates/html/read-entry.html。 -->
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所 · 卷库房</span><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">14/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>卷库房 · 扫描件 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../../assets/css/secret.css">
|
||||||
|
<script defer src="../../assets/js/components.js"></script>
|
||||||
|
<script defer src="../../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="16" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="login.html">卷库房</a>
|
||||||
|
<a href="query.html">案卷查询</a><a href="results.html">检索结果</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- 文书扫描件门(M2 Shape B,gate-b.html):就地解锁、不跳转;
|
||||||
|
解锁后正文块必须以 x-show="unlocked" 开头(check-solvable 的边界标记)。
|
||||||
|
密钥凭据三元组由步骤 3/5 设计(生活痕迹,永不随机串,R3);占位符只名字段(R4)。
|
||||||
|
6b 按 GDD 复制为各扫描件页;此模板页本身不接入触达链。 -->
|
||||||
|
<main x-data="gate">
|
||||||
|
<div class="sheet narrow" x-show="!unlocked">
|
||||||
|
<h1>扫描件调阅</h1>
|
||||||
|
<p>本件为已归档文书扫描件,调阅需验证密钥。</p>
|
||||||
|
<form class="gate" @submit.prevent="submit">
|
||||||
|
<label for="k">密钥</label>
|
||||||
|
<input id="k" type="password" name="key" placeholder="密钥" data-expect-hash="<hash>">
|
||||||
|
<p class="gate-error" x-show="error" x-text="error" x-cloak></p>
|
||||||
|
<button class="btn" type="submit">解锁</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div x-show="unlocked" x-cloak>
|
||||||
|
<article class="sheet narrow">
|
||||||
|
<div class="doc-head"><span class="doc-no">〔文号占位〕</span><h2>〔文书标题占位〕</h2><p class="doc-meta">〔发文单位 / 日期 / 主送 — 6b 填〕</p></div>
|
||||||
|
<p>〔扫描件文书正文占位,零形容词公文语域〕</p>
|
||||||
|
<p class="doc-seal">〔印〕</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所 · 卷库房</span><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">16/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>律师页面 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="4" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>律师页面</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<p>本页为律师个人页模板占位:公开名录字段(姓名、职务、执业年限、业务领域)。</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">4/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>信息公开 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="8" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>信息公开</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<ul class="doc-list">
|
||||||
|
<li><time datetime="2015-12-01">2015-12-01</time><span>通知公告条目占位</span></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">8/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>办结报告(一) — 临川县正衡律师事务所</title>
|
||||||
|
<!-- 结局甲页 骨架(M10)。正文文案在步骤 7 落;6a 只立壳,文件名不剧透。
|
||||||
|
本页暂不入任何导航/触达链,6b 按触达表接入(两结局文案各自复用刚读过的文书,尾声收第四面墙+作者链接)。 -->
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="17" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>办结报告(一)</h1>
|
||||||
|
<p>〔正文占位——步骤 7 定稿。〕</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">17/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>办结报告(二) — 临川县正衡律师事务所</title>
|
||||||
|
<!-- 结局乙页 骨架(M10)。正文文案在步骤 7 落;6a 只立壳,文件名不剧透。
|
||||||
|
本页暂不入任何导航/触达链,6b 按触达表接入(两结局文案各自复用刚读过的文书,尾声收第四面墙+作者链接)。 -->
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="18" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>办结报告(二)</h1>
|
||||||
|
<p>〔正文占位——步骤 7 定稿。〕</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">18/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>附记 — 临川县正衡律师事务所</title>
|
||||||
|
<!-- 尾声页(第四面墙收束+作者链接) 骨架(M10)。正文文案在步骤 7 落;6a 只立壳,文件名不剧透。
|
||||||
|
本页暂不入任何导航/触达链,6b 按触达表接入(两结局文案各自复用刚读过的文书,尾声收第四面墙+作者链接)。 -->
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="19" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>附记</h1>
|
||||||
|
<p>〔正文占位——步骤 7 定稿。〕</p>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">19/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>网站地图 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="10" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>网站地图</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<ul>
|
||||||
|
<li><a href="home.html">首页</a></li>
|
||||||
|
<li><a href="about.html">关于正衡</a></li>
|
||||||
|
<li><a href="team.html">律师团队</a></li>
|
||||||
|
<li><a href="aid.html">法律援助</a></li>
|
||||||
|
<li><a href="cases.html">释法案例</a></li>
|
||||||
|
<li><a href="notices.html">信息公开</a></li>
|
||||||
|
<li><a href="contact.html">联系我们</a></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">10/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>律师团队 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="../assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/surface.css">
|
||||||
|
<script defer src="../assets/js/components.js"></script>
|
||||||
|
<script defer src="../assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="3" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="home.html">首页</a><a href="about.html">关于正衡</a><a href="team.html">律师团队</a><a href="aid.html">法律援助</a><a href="cases.html">释法案例</a><a href="notices.html">信息公开</a><a href="contact.html">联系我们</a>
|
||||||
|
<form action="../search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h1>律师团队</h1>
|
||||||
|
<!-- 正文文案在 6b/步骤7 按 GDD 与触达表填入;6a 骨架不含剧情内容。 -->
|
||||||
|
<ul class="doc-list">
|
||||||
|
<li><a href="lawyer.html">律师名录条目占位</a></li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="juanfang/login.html">员工入口</a><a href="sitemap.html">网站地图</a><a href="contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">3/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>站内搜索 — 临川县正衡律师事务所</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/base.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/surface.css">
|
||||||
|
<script defer src="assets/js/components.js"></script>
|
||||||
|
<script defer src="assets/js/vendor/alpine.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body data-page="11" data-total="19">
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<a class="brand" href="pages/home.html">临川县正衡律师事务所</a>
|
||||||
|
<a href="pages/home.html">首页</a><a href="pages/about.html">关于正衡</a><a href="pages/team.html">律师团队</a><a href="pages/aid.html">法律援助</a><a href="pages/cases.html">释法案例</a><a href="pages/notices.html">信息公开</a><a href="pages/contact.html">联系我们</a>
|
||||||
|
<form action="search.html" method="get"><input type="search" name="q" placeholder="站内搜索"><button class="btn ghost" type="submit">搜索</button></form>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- M1 检索结果中枢。挂载 markup 依 assets/templates/html/search.html;表层公开索引(data/keywords.surface.json,R8)。
|
||||||
|
公开索引不承载 juanfang/ URL;命中不可开者经门或纯锁定告示(R8)。 -->
|
||||||
|
<main>
|
||||||
|
<h1>站内搜索</h1>
|
||||||
|
<form class="gate" action="search.html" method="get" style="max-width:34rem">
|
||||||
|
<label for="q">关键词</label>
|
||||||
|
<input id="q" type="search" name="q" placeholder="关键词">
|
||||||
|
<button class="btn" type="submit">检索</button>
|
||||||
|
</form>
|
||||||
|
<div id="results" x-data="search" data-index="data/keywords.surface.json" x-cloak>
|
||||||
|
<template x-if="state === 'forbidden'">
|
||||||
|
<span :class="forbidden.hidden ? 'hidden-text' : 'visible-text'" x-text="forbidden.text"></span>
|
||||||
|
</template>
|
||||||
|
<template x-if="state === 'hit'">
|
||||||
|
<div>
|
||||||
|
<p><b x-text="results.length"></b> 条检索结果:</p>
|
||||||
|
<ul class="doc-list">
|
||||||
|
<template x-for="r in results" :key="r.url">
|
||||||
|
<li><a :href="r.url" target="_blank" x-text="r.title"></a></li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template x-if="state === 'miss'"><p>未检索到与"<b x-text="q"></b>"相关的内容。</p></template>
|
||||||
|
<template x-if="state === 'empty'"><p>请输入检索关键词。</p></template>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer x-data="progress"><div class="footer-inner">
|
||||||
|
<span>© 2015 临川县正衡律师事务所</span><a href="pages/juanfang/login.html">员工入口</a><a href="pages/sitemap.html">网站地图</a><a href="pages/contact.html">联系我们</a><span class="progress" x-text="seen.length + '/' + document.body.dataset.total">11/19</span>
|
||||||
|
</div></footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user