demo-books.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import { Book, BookEntry, BookEntryKind, BookSection } from "@/types/book";
  2. function makeEntries(sectionId: string, kind: BookEntryKind, titles: string[][]): BookEntry[] {
  3. return titles.map(([title, ...content], index) => ({
  4. id: `${sectionId}-${index + 1}`,
  5. title,
  6. kind,
  7. order: index + 1,
  8. content
  9. }));
  10. }
  11. function makeSection(
  12. id: string,
  13. title: string,
  14. kind: BookEntryKind,
  15. order: number,
  16. titles: string[][]
  17. ): BookSection {
  18. return {
  19. id,
  20. title,
  21. kind,
  22. order,
  23. entries: makeEntries(id, kind, titles)
  24. };
  25. }
  26. export const demoBooks: Book[] = [
  27. {
  28. id: "memory-catcher",
  29. title: "记忆捕手",
  30. author: "墨尘",
  31. category: "悬疑",
  32. description:
  33. "一间只在深夜营业的记忆事务所,一群替人打捞记忆碎片的人,以及越来越像真实案件的委托记录。",
  34. wordCount: "约 6 万字(演示)",
  35. coverStyle: "linear-gradient(135deg, #7089a8, #2e405a)",
  36. sections: [
  37. makeSection("mc-lore-world", "世界观设定", "lore", 1, [
  38. [
  39. "资料一 事务所规则",
  40. "记忆事务所只在每天凌晨零点到五点营业,所有委托都需要经过双重验证,且不得跨越自然死亡边界回收记忆。",
  41. "每次进入记忆深层前,执行者必须留下现实锚点,否则在高损耗片段中极易失去自我定位。"
  42. ],
  43. [
  44. "资料二 回收协议",
  45. "记忆回收分为取样、修复、归还三个阶段,资料类委托和正文类案件都必须标记来源与完整度。",
  46. "当完整度低于百分之三十时,系统会自动建议多人协同解析。"
  47. ]
  48. ]),
  49. makeSection("mc-lore-roles", "人物档案", "lore", 2, [
  50. [
  51. "资料三 林夜",
  52. "林夜是事务所的主检索员,擅长在受损记忆中建立临时路径。",
  53. "他最大的弱点是不擅长拒绝任何一份“看起来还能救回来”的委托。"
  54. ],
  55. [
  56. "资料四 深洲事务所",
  57. "事务所位于深洲旧城区地下一层,常年由蓝色工作灯照明,主工作台正对一面由十五块屏幕拼成的监控墙。",
  58. "多数外来委托都会先被收录为资料条目,再决定是否转入正式案件。"
  59. ]
  60. ]),
  61. makeSection("mc-novel-volume-1", "正文 第一卷", "novel", 100, [
  62. [
  63. "第一章 数据废墟",
  64. "深洲事务所的地下工作室被蓝色光芒覆盖,十五块屏幕全部亮起,数据流在每一个像素中涌动。",
  65. "林夜坐在工作台中央,接口头环已经戴好,眼前是一份来自匿名客户的委托:在七十二小时内找回一段被人为删除的童年记忆。",
  66. "这类工作本该只是技术活,可委托附件里多出了一段视频。视频结尾,有一只沾着雨水的手按住镜头,像是隔着时间朝他们打招呼。",
  67. "林夜把视频倒回三遍,每一次都在同一帧停住。那只手的背面有一条细长的旧疤,从食指根部一直延伸到手腕内侧。他在档案库里查了两个小时,没有找到任何匹配记录。",
  68. "助手阿洲从副屏那边探过头来,把一杯加了太多糖的速溶咖啡推到他手边。阿洲的全名是深洲,是前任所长留下的程序遗产,运行在事务所最老的那台主机里,声音一直带着轻微的电流底噪。",
  69. "委托标注的删除时间是十七年前,记忆主体的年龄标签显示为七岁。林夜见过各种各样的委托,但主动删除自己七岁记忆的案例,他只遇到过两次,而那两次都以失踪告终。",
  70. "他把委托文件重新读了一遍,这次注意到了一个细节:委托人在自我描述一栏填写的不是职业,而是一句话——我不记得自己是谁,但我记得那天下的雨很冷。",
  71. "阿洲把周边环境数据调出来投到主屏。十七年前的深洲旧城区,梅雨季,平均气温十八度,湿度百分之八十九。数据是对的,雨确实很冷。",
  72. "接入协议在凌晨一点十分完成,桥接通道稳定在百分之七十三。林夜把头环频率调低两档,告诉自己这只是一次普通的技术回收,不需要带入情绪。",
  73. "进入深层的第一秒,他感到一阵轻微的耳鸣。这是正常现象,每次穿越时间密度超过十五年的记忆节点,神经接口都会有短暂的校准延迟。他等那阵嗡鸣过去,睁开眼睛。",
  74. "眼前是一条砖墙巷子,地面是湿的,空气里有青苔和积水混合的气味。巷子的尽头挂着一盏昏黄的路灯,灯芯在雨里微微颤动,像是随时会熄灭,又始终没有熄灭。",
  75. "系统提示记忆完整度为百分之四十一,低于标准阈值,但还在可回收范围内。林夜沿着巷子向前走,注意到地面的青砖缝里长着一种深绿色的苔藓,每走一步都会发出细碎的轻响。",
  76. "他在路灯下停住。灯柱底部贴着一张褪色的寻人启事,纸张已经被雨水浸透,字迹模糊到只剩一个名字还能辨认:沈宁,女,七岁,失踪于本年六月十四日。",
  77. "林夜把这个名字发回给阿洲,让她在现实端同步检索。回复几乎是即时的:深洲民政记录中有两个同名条目,但其中一个的档案在八年前被标记为封存,无法访问。",
  78. "他继续往巷子深处走。记忆的密度在增加,四周的色彩开始从灰蓝色向暖黄色偏移,像是有人在用力把这段经历拉回到更早的时间里。林夜知道这是删除补丁留下的痕迹——当一段记忆被反复清除,边缘会产生色温偏差,就像照片被过度曝光之后留下的光晕。",
  79. "巷子的尽头出现了一扇半开的木门,门里透出温暖的灯光,还有锅里食物沸腾的声音。林夜站在门外,没有立刻进去。他知道记忆里的场景如果设置了访问壁垒,强行进入会触发清除反应,把这段残余也一并抹掉。",
  80. "他在门外等了大约三十秒,确认没有阻断信号之后才轻轻推开木门。室内是一间狭小但整洁的厨房,一个女人正背对着他站在炉灶前,长发用发绳随意绑着,颈侧有一块淡淡的胎记。",
  81. "女人没有回头,只是把火调小了一档,用很平静的声音说:你来了,就知道你会来。",
  82. "林夜没有回答。在记忆深层,对话必须谨慎,任何一个错误的字都可能成为触发清除的关键词。他绕到女人侧面,想看清她的脸,却发现她的面部细节正在以肉眼可见的速度消融,像是一张被水泡过的相片,在他将要看清的瞬间,悄悄变得模糊。",
  83. "阿洲在他耳机里提示:通道稳定性下降,当前百分之五十八,建议开始打包可回收片段。",
  84. "林夜扫了一眼周围,记下了三件东西:灶台上的白色搪瓷锅、窗台上放着的一双儿童雨鞋、以及冰箱上用磁贴压着的一张手绘地图。他把三个物件的坐标依次上传,转身从门口退出。",
  85. "记忆在他离开的瞬间开始收缩,巷子两侧的砖墙迅速向中间靠拢,路灯的光也跟着暗下去。林夜加快脚步,在通道完全闭合之前找到了出口锚点,用力踩下去。",
  86. "回到现实的过程像是从水里浮出来,有短暂的失重感,然后是所有感官同时回归的那种细密刺痛。他把头环摘下来,放在桌上,看着主屏上跳出的回收摘要。三个坐标,完整度分别是百分之七十一、八十三和九十四。",
  87. "阿洲把数据整理好推过来,在最后加了一句备注:冰箱上的手绘地图里,有一条街道的名字和封存档案里的最后一次登记地址完全一致。",
  88. "林夜盯着那个地址看了很久。深洲旧城区,合兴里十七号,楼上是一家早就停业的裁缝铺,楼下早年间开过一家小诊所,后来被并入了街道卫生站,所有历史档案按规定封存了二十年。",
  89. "他在键盘上敲了几个字,又删掉,重新敲,还是删掉。阿洲在旁边安静地等着,没有催他。事务所有一条不成文的规矩:遇到不确定要不要继续的案子,所有人都可以沉默五分钟,不需要解释。",
  90. "五分钟之后,林夜打开了一份新的申请表,在委托类型一栏勾选了实地核查,在附注里写下了三个字:谨慎推进。",
  91. "阿洲把申请表收录进当日工作日志,顺手把那杯已经凉掉的咖啡推远了一点。窗外,深洲旧城区的天光刚刚开始泛蓝,一辆早班公共汽车喘着气驶过街角,带起一阵潮湿的风。",
  92. "新的一天从这里开始,也从这里变得复杂起来。",
  93. "林夜在出发前最后看了一眼委托档案。委托时间戳是昨晚十一点五十七分,距离事务所开门整整差三分钟。他不确定这意味着什么,但他一向相信时间点不会是巧合。",
  94. "合兴里离事务所步行大约二十分钟,穿过旧城区的菜市场和两条窄得只能侧身通过的小巷。林夜出门时带了一把折叠伞,天气预报说上午有小雨,和十七年前那天一样。",
  95. "他走到菜市场边上的时候,雨真的落下来了。细密的雨丝打在雨伞上,发出连续的轻响。林夜把脚步放慢,在摊位前停了一下,买了两个刚出炉的茶叶蛋,一个给自己,一个装进外套口袋里,备用。",
  96. "合兴里十七号到了。楼上的裁缝铺招牌还挂着,漆已经掉了一半,另一半用透明胶带粘着,看上去在这个位置待了很多年。楼下的诊所已经改成了一家五金店,门口堆着各种管件和工具,老板坐在里面看手机,没有抬头。",
  97. "林夜在门口站了一会儿,然后走进五金店,用一种很随意的语气问老板:请问这里之前是个诊所对吧,老板您知不知道以前有个姓沈的医生?",
  98. "老板终于抬起头,看了他一眼,说:你是第几个来问这个问题的人?",
  99. "林夜没想到会得到这个答案。他在心里把今天的进展整理了一下,重新给阿洲发了条消息:这个案子比我以为的要复杂,先把等级从三级调到二级,多备一条回收通道。",
  100. "老板把手机扣在桌上,来了兴致。他说,大概每隔两三年,就会有人来问沈医生的事情,有时候是年轻人,有时候是上了年纪的,上一个来的是个穿黑色风衣的女人,大概半年前,问完就走了,什么都没留下。",
  101. "林夜在心里把半年前的时间点记住,然后问:那位女士有没有说她是谁,或者留下什么联系方式?",
  102. "老板摇摇头,说她来的时候是下午,雨也很大,问完转身就走,连个名字都没说。然后老板顿了顿,补充道:不过她走的时候,在门口那根电线杆上贴了一张纸。他说这话的时候朝门口努了努嘴,那根电线杆就在斜对面,生了锈,缠着几根废弃的网线。",
  103. "林夜走过去,在电线杆靠里侧的位置找到了一张已经被雨水反复浸泡、边缘开始卷翘的便利贴。纸上只有一行字,用极细的铅笔写成,笔压很轻,像是刻意不想留下太深的印记:记忆可以删,但地址不会改变。",
  104. "他把便利贴用镊子夹起来,装进随身携带的取证袋里。雨重新开始下,比刚才更密,整条合兴里在雨幕里变得灰白而遥远。林夜打开伞,站在电线杆边上,把这行字重新在脑子里过了一遍,觉得它不像是一句提示,更像是一句等了很久才说出口的承诺。",
  105. "他重新拨通了阿洲的通讯频道,说:准备第二次深层接入,目标时间节点往前推三年,从删除起点开始。阿洲沉默了两秒,回复说:收到,等你回来。"
  106. ],
  107. [
  108. "第二章 深网潜入",
  109. "林夜坐在工作台中央,接口头环已经戴好,双手悬停在键盘上方,等待深网桥接程序完成最后一次校验。",
  110. "任务目标是一段被藏进深层节点的私人记忆。它没有文件名,也没有索引,像一枚被扔进黑潮里的钉子,只能靠微弱的访问残响定位。",
  111. "副屏上,系统给出了三条不太乐观的提示:来源匿名、时间戳错乱、回收通道不稳定。"
  112. ],
  113. [
  114. "第三章 雨巷回声",
  115. "连接建立后,第一段回收到的不是画面,而是一阵潮湿的雨声。",
  116. "雨点顺着一条老旧的石板巷子落下,远处有人撑着黑伞停在路灯下,像在等谁,又像已经等了很久。",
  117. "系统记录显示,这段记忆在过去七年里被人为删除过四次,但每次都会在不同的深夜重新出现。"
  118. ],
  119. [
  120. "第四章 失真坐标",
  121. "他们终于在失真层里找到了坐标,却发现坐标指向的是一座早已拆除的旧车站。",
  122. "记忆里的广播还在播报过期的列车时刻,玻璃穹顶上方积着雨,候车厅里坐满了看不清脸的人。"
  123. ],
  124. [
  125. "第五章 双重委托",
  126. "清晨六点,他们刚从深层断开,门铃便响了。",
  127. "来的人戴着深灰色帽子,把同样一份委托重新放在桌上,连措辞都和昨晚的一字不差,只在签名处多了一个新的名字。"
  128. ],
  129. [
  130. "第六章 无名乘客",
  131. "第二次进入时,他们看见候车厅角落多了一个孩子。",
  132. "孩子穿着被雨打湿的校服,手里攥着半张车票,车票背面写着一行几乎被水泡开的字:不要相信站台上的广播。"
  133. ],
  134. [
  135. "第七章 镜像证词",
  136. "为了验证委托来源,他们调出了另一位委托人的证词记录。",
  137. "两份证词在前半段完全一致,只在最后三分钟出现了分叉。"
  138. ],
  139. [
  140. "第八章 灯箱之后",
  141. "林夜在站台广告灯箱后找到了一扇隐藏维护门。",
  142. "门后不是管道井,而是一间布满旧磁带和纸质档案的小房间。"
  143. ],
  144. [
  145. "第九章 回收协议",
  146. "事务所服务器在当晚收到一份未签名的回收协议。",
  147. "协议条款极短,只要求他们在二十四小时内交出目标记忆的完整副本。"
  148. ],
  149. [
  150. "第十章 七号出口",
  151. "他们决定在现实中去找那座被封闭的七号出口。",
  152. "清晨的围挡后满是积水和灰尘,可在围挡缝隙后,林夜看见了一盏仍旧亮着的引导灯。"
  153. ]
  154. ])
  155. ]
  156. },
  157. {
  158. id: "lan-archive",
  159. title: "局域网夜航",
  160. author: "程未远",
  161. category: "近未来",
  162. description: "一座封闭园区、几台常亮的旧电脑,以及深夜里不断跳出的陌生消息。",
  163. wordCount: "12.8 万字",
  164. coverStyle: "linear-gradient(135deg, #b86143, #5c2d23)",
  165. sections: [
  166. makeSection("lan-lore", "设定资料", "lore", 1, [
  167. ["资料一 园区网络", "园区网络与公网完全隔离,所有访问记录由本地交换系统统一收敛。"],
  168. ["资料二 旧机房", "二层最靠里的旧机房仍保留着上一代局域网控制台与部分废弃机柜。"]
  169. ]),
  170. makeSection("lan-novel", "正文", "novel", 100, [
  171. ["第一章 机房尽头的灯", "凌晨两点十三分,园区办公楼里只剩下二层最靠里的机房还亮着灯。"],
  172. ["第二章 没有登记的设备", "他先检查 DHCP 记录,又翻了交换机最近一小时的地址分配表,结果一片空白。"],
  173. ["第三章 反向截图", "对方没有回答,只发来一张刚刚拍下的机房照片。"]
  174. ])
  175. ]
  176. },
  177. {
  178. id: "tea-house",
  179. title: "旧书铺茶事",
  180. author: "沈南絮",
  181. category: "日常",
  182. description: "一间开在老街口的旧书铺,白天卖书,傍晚煮茶。",
  183. wordCount: "8.4 万字",
  184. coverStyle: "linear-gradient(135deg, #8f6a3d, #314438)",
  185. sections: [
  186. makeSection("tea-lore", "店铺札记", "lore", 1, [
  187. ["资料一 书铺门铃", "门铃是一枚老铜铃,雨天响起来比晴天更轻一些。"]
  188. ]),
  189. makeSection("tea-novel", "正文", "novel", 100, [
  190. ["第一章 雨落在旧街口", "傍晚五点,雨刚刚落下来,青石板路被洗得发亮。"]
  191. ])
  192. ]
  193. },
  194. {
  195. id: "star-mail",
  196. title: "星港来信",
  197. author: "林观海",
  198. category: "科幻",
  199. description: "远航舰队返程之前,地球收到一封延迟了十九年的星际邮件。",
  200. wordCount: "15.1 万字",
  201. coverStyle: "linear-gradient(135deg, #355c7d, #121c2e)",
  202. sections: [
  203. makeSection("star-lore", "星港档案", "lore", 1, [
  204. ["资料一 深空信道", "民用深空信道十九年未曾亮起,因此被默认彻底沉寂。"]
  205. ]),
  206. makeSection("star-novel", "正文", "novel", 100, [
  207. ["第一章 邮件抵达前夜", "海港城的夜总是比内陆亮一些。"]
  208. ])
  209. ]
  210. },
  211. {
  212. id: "north-city",
  213. title: "北城旧事簿",
  214. author: "顾行舟",
  215. category: "都市",
  216. description: "一座旧城的黄昏、几条重复经过的街道,以及那些看似平常却慢慢累积出重量的日子。",
  217. wordCount: "10.2 万字",
  218. coverStyle: "linear-gradient(135deg, #7c5a43, #28384f)",
  219. sections: [
  220. makeSection("north-lore", "旧城资料", "lore", 1, [
  221. ["资料一 北城地标", "天桥、报刊亭和旧商场灯牌,是北城许多故事共同的入口。"]
  222. ]),
  223. makeSection("north-novel", "正文", "novel", 100, [
  224. ["第一章 天桥下的晚风", "晚高峰刚过,天桥下的人群还没散完。"]
  225. ])
  226. ]
  227. }
  228. ];
  229. export function getBookById(bookId: string) {
  230. return demoBooks.find((book) => book.id === bookId);
  231. }