| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- import { Book, BookEntry, BookEntryKind, BookSection } from "@/types/book";
- function makeEntries(sectionId: string, kind: BookEntryKind, titles: string[][]): BookEntry[] {
- return titles.map(([title, ...content], index) => ({
- id: `${sectionId}-${index + 1}`,
- title,
- kind,
- order: index + 1,
- content
- }));
- }
- function makeSection(
- id: string,
- title: string,
- kind: BookEntryKind,
- order: number,
- titles: string[][]
- ): BookSection {
- return {
- id,
- title,
- kind,
- order,
- entries: makeEntries(id, kind, titles)
- };
- }
- export const demoBooks: Book[] = [
- {
- id: "memory-catcher",
- title: "记忆捕手",
- author: "墨尘",
- category: "悬疑",
- description:
- "一间只在深夜营业的记忆事务所,一群替人打捞记忆碎片的人,以及越来越像真实案件的委托记录。",
- wordCount: "约 6 万字(演示)",
- coverStyle: "linear-gradient(135deg, #7089a8, #2e405a)",
- sections: [
- makeSection("mc-lore-world", "世界观设定", "lore", 1, [
- [
- "资料一 事务所规则",
- "记忆事务所只在每天凌晨零点到五点营业,所有委托都需要经过双重验证,且不得跨越自然死亡边界回收记忆。",
- "每次进入记忆深层前,执行者必须留下现实锚点,否则在高损耗片段中极易失去自我定位。"
- ],
- [
- "资料二 回收协议",
- "记忆回收分为取样、修复、归还三个阶段,资料类委托和正文类案件都必须标记来源与完整度。",
- "当完整度低于百分之三十时,系统会自动建议多人协同解析。"
- ]
- ]),
- makeSection("mc-lore-roles", "人物档案", "lore", 2, [
- [
- "资料三 林夜",
- "林夜是事务所的主检索员,擅长在受损记忆中建立临时路径。",
- "他最大的弱点是不擅长拒绝任何一份“看起来还能救回来”的委托。"
- ],
- [
- "资料四 深洲事务所",
- "事务所位于深洲旧城区地下一层,常年由蓝色工作灯照明,主工作台正对一面由十五块屏幕拼成的监控墙。",
- "多数外来委托都会先被收录为资料条目,再决定是否转入正式案件。"
- ]
- ]),
- makeSection("mc-novel-volume-1", "正文 第一卷", "novel", 100, [
- [
- "第一章 数据废墟",
- "深洲事务所的地下工作室被蓝色光芒覆盖,十五块屏幕全部亮起,数据流在每一个像素中涌动。",
- "林夜坐在工作台中央,接口头环已经戴好,眼前是一份来自匿名客户的委托:在七十二小时内找回一段被人为删除的童年记忆。",
- "这类工作本该只是技术活,可委托附件里多出了一段视频。视频结尾,有一只沾着雨水的手按住镜头,像是隔着时间朝他们打招呼。",
- "林夜把视频倒回三遍,每一次都在同一帧停住。那只手的背面有一条细长的旧疤,从食指根部一直延伸到手腕内侧。他在档案库里查了两个小时,没有找到任何匹配记录。",
- "助手阿洲从副屏那边探过头来,把一杯加了太多糖的速溶咖啡推到他手边。阿洲的全名是深洲,是前任所长留下的程序遗产,运行在事务所最老的那台主机里,声音一直带着轻微的电流底噪。",
- "委托标注的删除时间是十七年前,记忆主体的年龄标签显示为七岁。林夜见过各种各样的委托,但主动删除自己七岁记忆的案例,他只遇到过两次,而那两次都以失踪告终。",
- "他把委托文件重新读了一遍,这次注意到了一个细节:委托人在自我描述一栏填写的不是职业,而是一句话——我不记得自己是谁,但我记得那天下的雨很冷。",
- "阿洲把周边环境数据调出来投到主屏。十七年前的深洲旧城区,梅雨季,平均气温十八度,湿度百分之八十九。数据是对的,雨确实很冷。",
- "接入协议在凌晨一点十分完成,桥接通道稳定在百分之七十三。林夜把头环频率调低两档,告诉自己这只是一次普通的技术回收,不需要带入情绪。",
- "进入深层的第一秒,他感到一阵轻微的耳鸣。这是正常现象,每次穿越时间密度超过十五年的记忆节点,神经接口都会有短暂的校准延迟。他等那阵嗡鸣过去,睁开眼睛。",
- "眼前是一条砖墙巷子,地面是湿的,空气里有青苔和积水混合的气味。巷子的尽头挂着一盏昏黄的路灯,灯芯在雨里微微颤动,像是随时会熄灭,又始终没有熄灭。",
- "系统提示记忆完整度为百分之四十一,低于标准阈值,但还在可回收范围内。林夜沿着巷子向前走,注意到地面的青砖缝里长着一种深绿色的苔藓,每走一步都会发出细碎的轻响。",
- "他在路灯下停住。灯柱底部贴着一张褪色的寻人启事,纸张已经被雨水浸透,字迹模糊到只剩一个名字还能辨认:沈宁,女,七岁,失踪于本年六月十四日。",
- "林夜把这个名字发回给阿洲,让她在现实端同步检索。回复几乎是即时的:深洲民政记录中有两个同名条目,但其中一个的档案在八年前被标记为封存,无法访问。",
- "他继续往巷子深处走。记忆的密度在增加,四周的色彩开始从灰蓝色向暖黄色偏移,像是有人在用力把这段经历拉回到更早的时间里。林夜知道这是删除补丁留下的痕迹——当一段记忆被反复清除,边缘会产生色温偏差,就像照片被过度曝光之后留下的光晕。",
- "巷子的尽头出现了一扇半开的木门,门里透出温暖的灯光,还有锅里食物沸腾的声音。林夜站在门外,没有立刻进去。他知道记忆里的场景如果设置了访问壁垒,强行进入会触发清除反应,把这段残余也一并抹掉。",
- "他在门外等了大约三十秒,确认没有阻断信号之后才轻轻推开木门。室内是一间狭小但整洁的厨房,一个女人正背对着他站在炉灶前,长发用发绳随意绑着,颈侧有一块淡淡的胎记。",
- "女人没有回头,只是把火调小了一档,用很平静的声音说:你来了,就知道你会来。",
- "林夜没有回答。在记忆深层,对话必须谨慎,任何一个错误的字都可能成为触发清除的关键词。他绕到女人侧面,想看清她的脸,却发现她的面部细节正在以肉眼可见的速度消融,像是一张被水泡过的相片,在他将要看清的瞬间,悄悄变得模糊。",
- "阿洲在他耳机里提示:通道稳定性下降,当前百分之五十八,建议开始打包可回收片段。",
- "林夜扫了一眼周围,记下了三件东西:灶台上的白色搪瓷锅、窗台上放着的一双儿童雨鞋、以及冰箱上用磁贴压着的一张手绘地图。他把三个物件的坐标依次上传,转身从门口退出。",
- "记忆在他离开的瞬间开始收缩,巷子两侧的砖墙迅速向中间靠拢,路灯的光也跟着暗下去。林夜加快脚步,在通道完全闭合之前找到了出口锚点,用力踩下去。",
- "回到现实的过程像是从水里浮出来,有短暂的失重感,然后是所有感官同时回归的那种细密刺痛。他把头环摘下来,放在桌上,看着主屏上跳出的回收摘要。三个坐标,完整度分别是百分之七十一、八十三和九十四。",
- "阿洲把数据整理好推过来,在最后加了一句备注:冰箱上的手绘地图里,有一条街道的名字和封存档案里的最后一次登记地址完全一致。",
- "林夜盯着那个地址看了很久。深洲旧城区,合兴里十七号,楼上是一家早就停业的裁缝铺,楼下早年间开过一家小诊所,后来被并入了街道卫生站,所有历史档案按规定封存了二十年。",
- "他在键盘上敲了几个字,又删掉,重新敲,还是删掉。阿洲在旁边安静地等着,没有催他。事务所有一条不成文的规矩:遇到不确定要不要继续的案子,所有人都可以沉默五分钟,不需要解释。",
- "五分钟之后,林夜打开了一份新的申请表,在委托类型一栏勾选了实地核查,在附注里写下了三个字:谨慎推进。",
- "阿洲把申请表收录进当日工作日志,顺手把那杯已经凉掉的咖啡推远了一点。窗外,深洲旧城区的天光刚刚开始泛蓝,一辆早班公共汽车喘着气驶过街角,带起一阵潮湿的风。",
- "新的一天从这里开始,也从这里变得复杂起来。",
- "林夜在出发前最后看了一眼委托档案。委托时间戳是昨晚十一点五十七分,距离事务所开门整整差三分钟。他不确定这意味着什么,但他一向相信时间点不会是巧合。",
- "合兴里离事务所步行大约二十分钟,穿过旧城区的菜市场和两条窄得只能侧身通过的小巷。林夜出门时带了一把折叠伞,天气预报说上午有小雨,和十七年前那天一样。",
- "他走到菜市场边上的时候,雨真的落下来了。细密的雨丝打在雨伞上,发出连续的轻响。林夜把脚步放慢,在摊位前停了一下,买了两个刚出炉的茶叶蛋,一个给自己,一个装进外套口袋里,备用。",
- "合兴里十七号到了。楼上的裁缝铺招牌还挂着,漆已经掉了一半,另一半用透明胶带粘着,看上去在这个位置待了很多年。楼下的诊所已经改成了一家五金店,门口堆着各种管件和工具,老板坐在里面看手机,没有抬头。",
- "林夜在门口站了一会儿,然后走进五金店,用一种很随意的语气问老板:请问这里之前是个诊所对吧,老板您知不知道以前有个姓沈的医生?",
- "老板终于抬起头,看了他一眼,说:你是第几个来问这个问题的人?",
- "林夜没想到会得到这个答案。他在心里把今天的进展整理了一下,重新给阿洲发了条消息:这个案子比我以为的要复杂,先把等级从三级调到二级,多备一条回收通道。",
- "老板把手机扣在桌上,来了兴致。他说,大概每隔两三年,就会有人来问沈医生的事情,有时候是年轻人,有时候是上了年纪的,上一个来的是个穿黑色风衣的女人,大概半年前,问完就走了,什么都没留下。",
- "林夜在心里把半年前的时间点记住,然后问:那位女士有没有说她是谁,或者留下什么联系方式?",
- "老板摇摇头,说她来的时候是下午,雨也很大,问完转身就走,连个名字都没说。然后老板顿了顿,补充道:不过她走的时候,在门口那根电线杆上贴了一张纸。他说这话的时候朝门口努了努嘴,那根电线杆就在斜对面,生了锈,缠着几根废弃的网线。",
- "林夜走过去,在电线杆靠里侧的位置找到了一张已经被雨水反复浸泡、边缘开始卷翘的便利贴。纸上只有一行字,用极细的铅笔写成,笔压很轻,像是刻意不想留下太深的印记:记忆可以删,但地址不会改变。",
- "他把便利贴用镊子夹起来,装进随身携带的取证袋里。雨重新开始下,比刚才更密,整条合兴里在雨幕里变得灰白而遥远。林夜打开伞,站在电线杆边上,把这行字重新在脑子里过了一遍,觉得它不像是一句提示,更像是一句等了很久才说出口的承诺。",
- "他重新拨通了阿洲的通讯频道,说:准备第二次深层接入,目标时间节点往前推三年,从删除起点开始。阿洲沉默了两秒,回复说:收到,等你回来。"
- ],
- [
- "第二章 深网潜入",
- "林夜坐在工作台中央,接口头环已经戴好,双手悬停在键盘上方,等待深网桥接程序完成最后一次校验。",
- "任务目标是一段被藏进深层节点的私人记忆。它没有文件名,也没有索引,像一枚被扔进黑潮里的钉子,只能靠微弱的访问残响定位。",
- "副屏上,系统给出了三条不太乐观的提示:来源匿名、时间戳错乱、回收通道不稳定。"
- ],
- [
- "第三章 雨巷回声",
- "连接建立后,第一段回收到的不是画面,而是一阵潮湿的雨声。",
- "雨点顺着一条老旧的石板巷子落下,远处有人撑着黑伞停在路灯下,像在等谁,又像已经等了很久。",
- "系统记录显示,这段记忆在过去七年里被人为删除过四次,但每次都会在不同的深夜重新出现。"
- ],
- [
- "第四章 失真坐标",
- "他们终于在失真层里找到了坐标,却发现坐标指向的是一座早已拆除的旧车站。",
- "记忆里的广播还在播报过期的列车时刻,玻璃穹顶上方积着雨,候车厅里坐满了看不清脸的人。"
- ],
- [
- "第五章 双重委托",
- "清晨六点,他们刚从深层断开,门铃便响了。",
- "来的人戴着深灰色帽子,把同样一份委托重新放在桌上,连措辞都和昨晚的一字不差,只在签名处多了一个新的名字。"
- ],
- [
- "第六章 无名乘客",
- "第二次进入时,他们看见候车厅角落多了一个孩子。",
- "孩子穿着被雨打湿的校服,手里攥着半张车票,车票背面写着一行几乎被水泡开的字:不要相信站台上的广播。"
- ],
- [
- "第七章 镜像证词",
- "为了验证委托来源,他们调出了另一位委托人的证词记录。",
- "两份证词在前半段完全一致,只在最后三分钟出现了分叉。"
- ],
- [
- "第八章 灯箱之后",
- "林夜在站台广告灯箱后找到了一扇隐藏维护门。",
- "门后不是管道井,而是一间布满旧磁带和纸质档案的小房间。"
- ],
- [
- "第九章 回收协议",
- "事务所服务器在当晚收到一份未签名的回收协议。",
- "协议条款极短,只要求他们在二十四小时内交出目标记忆的完整副本。"
- ],
- [
- "第十章 七号出口",
- "他们决定在现实中去找那座被封闭的七号出口。",
- "清晨的围挡后满是积水和灰尘,可在围挡缝隙后,林夜看见了一盏仍旧亮着的引导灯。"
- ],
- [
- "第十一章 静止的镜子",
- "他们将旧车站的档案带回了事务所。危文件夹里存了三年前的矫正申请表,日期和委托单上的日期差了六天。",
- "阶洲把文件夹摆在桌上,说:有人先我们一步到了这里。林夜翻开第一页,记录栏里写着如实的声冬坐标:排前三个当事人的名字,其中一个叫陈七。",
- "胶片第二天才完成滴透,林夜均一夜没有离开样本上方的察察无眠,升得天亮时终于找到了一个可以切入的参照点。"
- ],
- [
- "第十二章 陈七的地址",
- "林夜在显示屏上定位出了陈七最近一次上线的系统时间:三个月前,凌晨三点七分。",
- "地址指向一幢老式公寓楼,目前登记至少四个不同的租户,其中一个展示的个人信息与单上的外形很相近。",
- "阶洲想个和他联系,林夜拦下他:先别动。我们先确认那个地址现在的情况。"
- ],
- [
- "第十三章 四楼的灯",
- "公寓楼四楼的走廊里,单数号房间的灯全圆了,只剩一盏走廊尽头的应急灯在闪烁。",
- "驾驶师说四楼自上月就没人住了,不过最近两个星期每夜都有人在走廊里径直走进去。",
- "林夜把耳机谑连开,强制自己呈展正常是观察者而不是个人身份提假的感觉,然后慢慢推开了最靠里的一片房门。"
- ],
- [
- "第十四章 未完成的删除",
- "房间里的机器还开着。不是旧型的负载终端,而是一台全内置模块的振器设备,摄了半空。上面还连着一根漏出几段内容的错误日志。",
- "阶洲读了日志的副本,摇首说:这不是普通的删除指令,有人在运行过程中拼强中断了。它起杀一半,剩下一半的内容还浮在缓冲区里。",
- "林夜思考了一下,说:我们再进去一次,把剩下的内容取出来,无论那个内容是什么。"
- ],
- [
- "第十五章 缓冲里的声音",
- "他们第三次进入目标节点。不同于前两次,这次剩下的记忆碎片完全是音频形式:整段太长,节奏极不均匀,像一个夜里均眠时拥着被子说话的人。",
- "林夜第一次听完第一段,说这声音我认得出。胶片的时候,日志里有一个签名,到现在我终于确定那是谁的声音了。"
- ],
- [
- "第十六章 三年前的规则",
- "数月和以前第一次出现的委托线索相互印证了:三年前阳鸾律所确实举报过一件非法记忆处理案件,当事人之一正是陈七。",
- "但陈七的当年稽诉记录却显示他全程配合调查,最终结案时被判定为无远证据的误报。那份案卷就此封档,没有任何后续记录。",
- "阶洲把档案平平展开,轻声说:所以就算有人提前封锁记忆,法律也不会查下去。林夜没有回答。"
- ],
- [
- "第十七章 陈七的说法",
- "二十小时后,陈七自己上门了。他比林夜想象中更年轻,头发很长。",
- "陈七说,我主动封存那段记忆是因为我对自己的判断不清楚。我不确定自己记忆里的内容是真实发生过的。",
- "林夜问:那你现在想展开封锁了吗?陈七摇摇头:我来看看你们从里面取出了什么。"
- ],
- [
- "第十八章 当事人的记忆",
- "陈七第一次建立连接时非常居漯。他不习惯深层接入,应激反应很强,年夜都没有完全选择放弃连接。",
- "阶洲在外面抱着臂说:白天不要来找我。如果不是林夜,我会直接评估为过度危险的委托。",
- "连接第二层时,陈七突然平静下来。林夜操控台上的湛悲值开始延伸,一直延伸到三年前的节点。"
- ],
- [
- "第十九章 道路的尽头",
- "他们在陈七的记忆里看见了三年前的陈七自己。年轻一点,头发同样很长,站在一段志从未地上的地下通道入口。",
- "通道里有一打开的记忆屏障,屏障里面有歪步声和采制设备的电子声过来,就在这时进来了。",
- "林夜与先进入的那个人交错而过,天交过脉的一刹间,林夜看清楚了对方的脸。"
- ],
- [
- "第二十章 重开的档案",
- "林夜小心地把七号出口的标志拦截点之前的历史连接工作全部完成了。文件完整度至此回升到百分之六十七。",
- "丽泉承论了代远第一次审阅该案件全部内容,降造了很久才抬起头说:这个案子是真实的。这不是误报。",
- "陈七坐在山尽头的居简里,一言不发地盯着下雨。门边假期节的灯就着,林夜把那份档案正式确认重开,写上了新的日期。"
- ]
- ])
- ]
- },
- {
- id: "lan-archive",
- title: "局域网夜航",
- author: "程未远",
- category: "近未来",
- description: "一座封闭园区、几台常亮的旧电脑,以及深夜里不断跳出的陌生消息。",
- wordCount: "12.8 万字",
- coverStyle: "linear-gradient(135deg, #b86143, #5c2d23)",
- sections: [
- makeSection("lan-lore", "设定资料", "lore", 1, [
- ["资料一 园区网络", "园区网络与公网完全隔离,所有访问记录由本地交换系统统一收敛。"],
- ["资料二 旧机房", "二层最靠里的旧机房仍保留着上一代局域网控制台与部分废弃机柜。"]
- ]),
- makeSection("lan-novel", "正文", "novel", 100, [
- ["第一章 机房尽头的灯", "凌晨两点十三分,园区办公楼里只剩下二层最靠里的机房还亮着灯。"],
- ["第二章 没有登记的设备", "他先检查 DHCP 记录,又翻了交换机最近一小时的地址分配表,结果一片空白。"],
- ["第三章 反向截图", "对方没有回答,只发来一张刚刚拍下的机房照片。"]
- ])
- ]
- },
- {
- id: "tea-house",
- title: "旧书铺茶事",
- author: "沈南絮",
- category: "日常",
- description: "一间开在老街口的旧书铺,白天卖书,傍晚煮茶。",
- wordCount: "8.4 万字",
- coverStyle: "linear-gradient(135deg, #8f6a3d, #314438)",
- sections: [
- makeSection("tea-lore", "店铺札记", "lore", 1, [
- ["资料一 书铺门铃", "门铃是一枚老铜铃,雨天响起来比晴天更轻一些。"]
- ]),
- makeSection("tea-novel", "正文", "novel", 100, [
- ["第一章 雨落在旧街口", "傍晚五点,雨刚刚落下来,青石板路被洗得发亮。"]
- ])
- ]
- },
- {
- id: "star-mail",
- title: "星港来信",
- author: "林观海",
- category: "科幻",
- description: "远航舰队返程之前,地球收到一封延迟了十九年的星际邮件。",
- wordCount: "15.1 万字",
- coverStyle: "linear-gradient(135deg, #355c7d, #121c2e)",
- sections: [
- makeSection("star-lore", "星港档案", "lore", 1, [
- ["资料一 深空信道", "民用深空信道十九年未曾亮起,因此被默认彻底沉寂。"]
- ]),
- makeSection("star-novel", "正文", "novel", 100, [
- ["第一章 邮件抵达前夜", "海港城的夜总是比内陆亮一些。"]
- ])
- ]
- },
- {
- id: "north-city",
- title: "北城旧事簿",
- author: "顾行舟",
- category: "都市",
- description: "一座旧城的黄昏、几条重复经过的街道,以及那些看似平常却慢慢累积出重量的日子。",
- wordCount: "10.2 万字",
- coverStyle: "linear-gradient(135deg, #7c5a43, #28384f)",
- sections: [
- makeSection("north-lore", "旧城资料", "lore", 1, [
- ["资料一 北城地标", "天桥、报刊亭和旧商场灯牌,是北城许多故事共同的入口。"]
- ]),
- makeSection("north-novel", "正文", "novel", 100, [
- ["第一章 天桥下的晚风", "晚高峰刚过,天桥下的人群还没散完。"]
- ])
- ]
- }
- ];
- export function getBookById(bookId: string) {
- return demoBooks.find((book) => book.id === bookId);
- }
|