[{"data":1,"prerenderedAt":2198},["ShallowReactive",2],{"\u002F2026-08-02":3,"\u002F2026-08-02-rel":962},{"id":4,"title":5,"body":6,"column":944,"date":945,"description":12,"extension":946,"hero_image":947,"meta":948,"navigation":949,"path":950,"seo":951,"series_id":947,"severity":947,"stem":952,"summary":953,"tags":954,"__hash__":961},"posts\u002F2026-08-02-首页改成八屏.md","首页改成八屏",{"type":7,"value":8,"toc":931},"minimark",[9,13,24,27,32,35,46,56,59,62,70,77,83,89,156,159,180,185,190,193,199,203,206,209,217,220,223,229,232,260,263,377,380,386,390,393,409,424,427,443,446,449,453,459,462,465,471,474,524,528,531,534,537,543,546,549,554,560,564,567,570,576,580,586,627,637,640,669,674,677,727,733,736,793,806,809,813,816,819,824,827,830,835,839,842,918,921,927],[10,11,12],"p",{},"这个博客的首页原来是连续滚动。改成了整屏分页，一页一件事，共八屏：",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","首屏 \u002F 自我介绍 01 \u002F 自我介绍 02 \u002F 做过的事 \u002F 精选复盘 \u002F 项目 \u002F 专栏 \u002F 联系\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"一屏容纳是逐档实测调出来的，不是估的。",[28,29,31],"h2",{"id":30},"一吸附用谁的","一、吸附用谁的",[10,33,34],{},"第一个决定是吸附用哪套机制。",[10,36,37,38,41,42,45],{},"原生有 ",[21,39,40],{},"scroll-snap","，CSS 几行就能写。但这里用不了——页面上的平滑滚动是 Lenis 提供的，它用 JS 驱动 ",[21,43,44],{},"scrollTop","：",[47,48,49],"blockquote",{},[10,50,51,52,55],{},"吸附用 Lenis 自带的 Snap 而不是 CSS scroll-snap——Lenis 是用 JS 驱动 scrollTop 的，和原生吸附会互相抢控制权、滚起来发抖。Snap 挂在同一个 Lenis 实例上没有这个问题，",[21,53,54],{},"onSnapComplete"," 还能驱动指示器。",[10,57,58],{},"两个机制都想控制滚动位置，结果就是抖。",[10,60,61],{},"挂在同一个实例上之后，吸附完成后还能拿到回调，右侧的页码指示器直接跟着它更新。",[28,63,65,66,69],{"id":64},"二mandatory-是错的","二、",[21,67,68],{},"mandatory"," 是错的",[10,71,72,73,76],{},"第一版用 ",[21,74,75],{},"type: 'mandatory'","。用户反馈手感是「被抢走了」。",[10,78,79,80,82],{},"原因是 ",[21,81,68],{}," 在用户还在滑的时候就强行把页面拽向最近一页。滑动过程被插手，手感不是吸附，是失控。",[10,84,85,86,45],{},"改成 ",[21,87,88],{},"proximity",[14,90,94],{"className":91,"code":92,"language":93,"meta":23,"style":23},"language-ts shiki shiki-themes github-light github-dark","type: 'proximity',\ndistanceThreshold: '30%',\nduration: 1.1,\ndebounce: 500,\n","ts",[21,95,96,116,129,143],{"__ignoreMap":23},[97,98,101,105,109,113],"span",{"class":99,"line":100},"line",1,[97,102,104],{"class":103},"sScJk","type",[97,106,108],{"class":107},"sVt8B",": ",[97,110,112],{"class":111},"sZZnC","'proximity'",[97,114,115],{"class":107},",\n",[97,117,119,122,124,127],{"class":99,"line":118},2,[97,120,121],{"class":103},"distanceThreshold",[97,123,108],{"class":107},[97,125,126],{"class":111},"'30%'",[97,128,115],{"class":107},[97,130,132,135,137,141],{"class":99,"line":131},3,[97,133,134],{"class":103},"duration",[97,136,108],{"class":107},[97,138,140],{"class":139},"sj4cs","1.1",[97,142,115],{"class":107},[97,144,146,149,151,154],{"class":99,"line":145},4,[97,147,148],{"class":103},"debounce",[97,150,108],{"class":107},[97,152,153],{"class":139},"500",[97,155,115],{"class":107},[10,157,158],{},"三条参数各管一件事：",[160,161,162,168,174],"ul",{},[163,164,165,167],"li",{},[21,166,88],{}," 只在停下来且已经接近边界时才轻推一把。",[163,169,170,173],{},[21,171,172],{},"distanceThreshold: '30%'"," 定义「接近」——落点离边界不到 30% 视口高才吸附，停在页面中间就让它停着。",[163,175,176,179],{},[21,177,178],{},"debounce: 500"," 等惯性完全停下来再判断，不在滑行中途插手。",[10,181,182,184],{},[21,183,148],{}," 从 220 提到 500：",[47,186,187],{},[10,188,189],{},"debounce 太小会在惯性还没停时就抢着吸附，手感发涩。",[10,191,192],{},"实测结果：",[14,194,197],{"className":195,"code":196,"language":19},[17],"滑动全程 0 反向帧（原来会被拽回）\n停在离边界 306px（超过 30% 阈值）时保持不动\n停在边界附近才对齐整页\n",[21,198,196],{"__ignoreMap":23},[28,200,202],{"id":201},"三一屏装不装得下","三、一屏装不装得下",[10,204,205],{},"分页的前提是内容能装进一屏。这一条逐档量过。",[10,207,208],{},"首屏原高 921px：",[160,210,211,214],{},[163,212,213],{},"1440×900 超出 89px",[163,215,216],{},"1366×768 超出 201px",[10,218,219],{},"精选复盘 788px，在 1366×768 超出 88px。",[10,221,222],{},"两个方向可选：砍内容，或者按档收紧间距。选了后者，分三档：",[14,224,227],{"className":225,"code":226,"language":19},[17],"min-width: 1024px and max-height: 820px   深压\nmin-width: 1024px and max-height: 900px   只压首屏\nmin-width: 1024px and min-height: 1000px  放大间距吃大屏留白\n",[21,228,226],{"__ignoreMap":23},[10,230,231],{},"中间那档的调整方式值得记一笔。为了挤出 27px，第一版直接砍了字号：",[14,233,237],{"className":234,"code":235,"language":236,"meta":23,"style":23},"language-css shiki shiki-themes github-light github-dark","font-size: clamp(46px, 9.2vw, 132px)  →  clamp(30px, 5.4vw, 62px)\n","css",[21,238,239],{"__ignoreMap":23},[97,240,241,245,248,251,254,257],{"class":99,"line":100},[97,242,244],{"class":243},"s9eBZ","font-size",[97,246,247],{"class":107},": clamp(46px, 9",[97,249,250],{"class":103},".2vw",[97,252,253],{"class":107},", 132px)  →  clamp(30px, 5",[97,255,256],{"class":103},".4vw",[97,258,259],{"class":107},", 62px)\n",[10,261,262],{},"砍掉一半多。首屏标题是整页的主体，砍字号会让整屏塌下来。改成全部从内边距要：",[14,264,266],{"className":234,"code":265,"language":236,"meta":23,"style":23},"@media (min-width: 1024px) and (max-height: 900px) {\n  .page-hero :deep(.hero) { padding-top: 20px; padding-bottom: 24px; }\n  .page-hero :deep(.hero-foot) { margin-top: 26px; }\n}\n",[21,267,268,309,349,372],{"__ignoreMap":23},[97,269,270,274,277,280,282,285,288,291,294,296,299,301,304,306],{"class":99,"line":100},[97,271,273],{"class":272},"szBVR","@media",[97,275,276],{"class":107}," (",[97,278,279],{"class":139},"min-width",[97,281,108],{"class":107},[97,283,284],{"class":139},"1024",[97,286,287],{"class":272},"px",[97,289,290],{"class":107},") ",[97,292,293],{"class":272},"and",[97,295,276],{"class":107},[97,297,298],{"class":139},"max-height",[97,300,108],{"class":107},[97,302,303],{"class":139},"900",[97,305,287],{"class":272},[97,307,308],{"class":107},") {\n",[97,310,311,314,317,320,323,326,328,331,333,336,339,341,344,346],{"class":99,"line":118},[97,312,313],{"class":103},"  .page-hero",[97,315,316],{"class":107}," :deep(",[97,318,319],{"class":103},".hero",[97,321,322],{"class":107},") { ",[97,324,325],{"class":139},"padding-top",[97,327,108],{"class":107},[97,329,330],{"class":139},"20",[97,332,287],{"class":272},[97,334,335],{"class":107},"; ",[97,337,338],{"class":139},"padding-bottom",[97,340,108],{"class":107},[97,342,343],{"class":139},"24",[97,345,287],{"class":272},[97,347,348],{"class":107},"; }\n",[97,350,351,353,355,358,360,363,365,368,370],{"class":99,"line":131},[97,352,313],{"class":103},[97,354,316],{"class":107},[97,356,357],{"class":103},".hero-foot",[97,359,322],{"class":107},[97,361,362],{"class":139},"margin-top",[97,364,108],{"class":107},[97,366,367],{"class":139},"26",[97,369,287],{"class":272},[97,371,348],{"class":107},[97,373,374],{"class":99,"line":145},[97,375,376],{"class":107},"}\n",[10,378,379],{},"只有 700px 可用高度那一档才让一档字号。三档实测：",[14,381,384],{"className":382,"code":383,"language":19},[17],"1440×900    132px\n1920×1080   132px\n1366×768     92.9px\n",[21,385,383],{"__ignoreMap":23},[28,387,389],{"id":388},"四检测判据写错过一次","四、检测判据写错过一次",[10,391,392],{},"判断页面有没有溢出，第一版用的是：",[14,394,396],{"className":91,"code":395,"language":93,"meta":23,"style":23},"scrollHeight > clientHeight\n",[21,397,398],{"__ignoreMap":23},[97,399,400,403,406],{"class":99,"line":100},[97,401,402],{"class":107},"scrollHeight ",[97,404,405],{"class":272},">",[97,407,408],{"class":107}," clientHeight\n",[10,410,411,412,415,416,419,420,423],{},"这个判据永远为假。页面用的是 ",[21,413,414],{},"min-height","，内容超了页面会自己长高——",[21,417,418],{},"scrollHeight"," 和 ",[21,421,422],{},"clientHeight"," 一起变大，比值不变。",[10,425,426],{},"改成直接比可用高度：",[14,428,430],{"className":91,"code":429,"language":93,"meta":23,"style":23},"\u002F\u002F 检测判据从 scrollHeight > clientHeight 改为直接比可用高度——\n\u002F\u002F 页面用的是 min-height，内容超了页面会自己长高，前者永远比不出溢出\n",[21,431,432,438],{"__ignoreMap":23},[97,433,434],{"class":99,"line":100},[97,435,437],{"class":436},"sJ8bj","\u002F\u002F 检测判据从 scrollHeight > clientHeight 改为直接比可用高度——\n",[97,439,440],{"class":99,"line":118},[97,441,442],{"class":436},"\u002F\u002F 页面用的是 min-height，内容超了页面会自己长高，前者永远比不出溢出\n",[10,444,445],{},"这个错误值得单独说：判据本身写错了，所以「三档零溢出」这个结论在修正之前是不成立的。",[10,447,448],{},"修正之后三档均为 8\u002F8 页零溢出。",[28,450,452],{"id":451},"五不分页的三种情况","五、不分页的三种情况",[14,454,457],{"className":455,"code":456,"language":19},[17],"窄屏（\u003C 1024px）\n矮视口（\u003C 620px）\n减弱动效（prefers-reduced-motion）\n",[21,458,456],{"__ignoreMap":23},[10,460,461],{},"三种都退回连续滚动。",[10,463,464],{},"前两种有实测依据：",[14,466,469],{"className":467,"code":468,"language":19},[17],"实测 390px 下自我介绍 1448px、精选复盘 1114px，强行一屏只会截断内容\n",[21,470,468],{"__ignoreMap":23},[10,472,473],{},"第三种是硬要求。整屏吸附会强制改变滚动位置，这和「尊重减弱动效偏好」直接冲突。CSS 里也要同步禁用：",[14,475,477],{"className":234,"code":476,"language":236,"meta":23,"style":23},"@media (prefers-reduced-motion: reduce) {\n  .page { min-height: 0; }\n  .pager { display: none; }\n}\n",[21,478,479,486,503,520],{"__ignoreMap":23},[97,480,481,483],{"class":99,"line":100},[97,482,273],{"class":272},[97,484,485],{"class":107}," (prefers-reduced-motion: reduce) {\n",[97,487,488,491,494,496,498,501],{"class":99,"line":118},[97,489,490],{"class":103},"  .page",[97,492,493],{"class":107}," { ",[97,495,414],{"class":139},[97,497,108],{"class":107},[97,499,500],{"class":139},"0",[97,502,348],{"class":107},[97,504,505,508,510,513,515,518],{"class":99,"line":131},[97,506,507],{"class":103},"  .pager",[97,509,493],{"class":107},[97,511,512],{"class":139},"display",[97,514,108],{"class":107},[97,516,517],{"class":139},"none",[97,519,348],{"class":107},[97,521,522],{"class":99,"line":145},[97,523,376],{"class":107},[28,525,527],{"id":526},"六分页之后每页只剩半屏内容","六、分页之后，每页只剩半屏内容",[10,529,530],{},"八屏分配完之后，出现一个反向问题：每页内容太少。",[10,532,533],{},"实测 1440×900 下多数页只填了 47%~63%，「做过的事」那页 63% 是空的。",[10,535,536],{},"既然分了页，每页就得撑得住。逐页补实：",[14,538,541],{"className":539,"code":540,"language":19},[17],"做过的事：加页首概览条（项目数\u002F时间跨度\u002F人手），每项补详细说明、关键指标与技术标签。308px → 548px\n项目：3 张卡补到 6 张，两行三列。362px → 511px\n联系：补引言、about 与 timeline 两个入口、站点信息带。452px → 791px\n专栏：每张卡补该栏最新一篇标题。512px → 597px\n自我介绍 01：四张能力卡各补首组的真实条目。504px → 596px\n自我介绍 02：补技术栈横带。522px → 603px\n",[21,542,540],{"__ignoreMap":23},[10,544,545],{},"填充率从 47%~63% 提到 61%~100%。",[10,547,548],{},"同一轮里把两个区块的入场动画也补了常驻循环，因为：",[47,550,551],{},[10,552,553],{},"实测入场动画本来就在跑（轴线 scaleX 0→0.99、圆点带回弹、卡片 clip-path 从 78% 揭到 4%、指标读数往上跳），问题是太隐蔽：一次性、1 秒内结束、触发点又在区块刚露头时，等正眼看过去通常已经播完。",[14,555,558],{"className":556,"code":557,"language":19},[17],"触发点 top 88% → 95%，轴线 0.9s → 1.25s，圆点与节点错峰 0.11 → 0.16\n",[21,559,557],{"__ignoreMap":23},[28,561,563],{"id":562},"七断点从-900-降到-820","七、断点从 900 降到 820",[10,565,566],{},"第一版把「矮视口」定在 900px 高。1440×900 的屏幕因此被当成矮视口，白白砍掉了列表摘要——精选复盘从 788px 缩到 389px。",[10,568,569],{},"而 1440×900 明明有空间。断点降到 820：",[14,571,574],{"className":572,"code":573,"language":19},[17],"820 以下   深压\n900 以下   只压首屏\n1000 以上  放大间距吃掉大屏留白\n",[21,575,573],{"__ignoreMap":23},[28,577,579],{"id":578},"八两个-css-优先级问题","八、两个 CSS 优先级问题",[10,581,582],{},[583,584,585],"strong",{},"首屏被挤成一个框。",[14,587,589],{"className":234,"code":588,"language":236,"meta":23,"style":23},".page { padding: 24px 0; }\n.page-hero { padding: 0; }\n",[21,590,591,612],{"__ignoreMap":23},[97,592,593,596,598,601,603,605,607,610],{"class":99,"line":100},[97,594,595],{"class":103},".page",[97,597,493],{"class":107},[97,599,600],{"class":139},"padding",[97,602,108],{"class":107},[97,604,343],{"class":139},[97,606,287],{"class":272},[97,608,609],{"class":139}," 0",[97,611,348],{"class":107},[97,613,614,617,619,621,623,625],{"class":99,"line":118},[97,615,616],{"class":103},".page-hero",[97,618,493],{"class":107},[97,620,600],{"class":139},[97,622,108],{"class":107},[97,624,500],{"class":139},[97,626,348],{"class":107},[10,628,629,632,633,636],{},[21,630,631],{},".page-hero { padding: 0 }"," 写在媒体查询之前，被后面同优先级的 ",[21,634,635],{},".page { padding: ... }"," 覆盖了。首屏于是四边都留出内边距，看起来像一个框。",[10,638,639],{},"修法是在每一档里重新声明一次：",[14,641,643],{"className":234,"code":642,"language":236,"meta":23,"style":23},"\u002F* 首屏必须满幅出血。这行不能省——上面那条 .page 同优先级且更靠后，\n   会把顶部声明的 .page-hero { padding: 0 } 覆盖掉。 *\u002F\n.page-hero { padding: 0; }\n",[21,644,645,650,655],{"__ignoreMap":23},[97,646,647],{"class":99,"line":100},[97,648,649],{"class":436},"\u002F* 首屏必须满幅出血。这行不能省——上面那条 .page 同优先级且更靠后，\n",[97,651,652],{"class":99,"line":118},[97,653,654],{"class":436},"   会把顶部声明的 .page-hero { padding: 0 } 覆盖掉。 *\u002F\n",[97,656,657,659,661,663,665,667],{"class":99,"line":131},[97,658,616],{"class":103},[97,660,493],{"class":107},[97,662,600],{"class":139},[97,664,108],{"class":107},[97,666,500],{"class":139},[97,668,348],{"class":107},[10,670,671],{},[583,672,673],{},"标题被大屏规则压小。",[10,675,676],{},"大屏档里有一条：",[14,678,680],{"className":234,"code":679,"language":236,"meta":23,"style":23},".page :deep(.big) { font-size: clamp(34px, 3.4vw, 56px); }\n",[21,681,682],{"__ignoreMap":23},[97,683,684,686,688,691,693,695,697,700,703,706,708,711,714,717,719,722,724],{"class":99,"line":100},[97,685,595],{"class":103},[97,687,316],{"class":107},[97,689,690],{"class":103},".big",[97,692,322],{"class":107},[97,694,244],{"class":139},[97,696,108],{"class":107},[97,698,699],{"class":139},"clamp",[97,701,702],{"class":107},"(",[97,704,705],{"class":139},"34",[97,707,287],{"class":272},[97,709,710],{"class":107},", ",[97,712,713],{"class":139},"3.4",[97,715,716],{"class":272},"vw",[97,718,710],{"class":107},[97,720,721],{"class":139},"56",[97,723,287],{"class":272},[97,725,726],{"class":107},"); }\n",[10,728,729,730,732],{},"首屏主标题的类名也是 ",[21,731,690],{},"。1920×1080 下它从 132px 被压到 56px，首屏整个塌掉。",[10,734,735],{},"改成排除首屏：",[14,737,739],{"className":234,"code":738,"language":236,"meta":23,"style":23},"\u002F* 必须排除首屏——hero 里的 .big 是那个巨大的主标题，\n   被这条命中会从 132px 压到 56px，整个首屏塌掉 *\u002F\n.page:not(.page-hero) :deep(.big) { font-size: clamp(34px, 3.4vw, 56px); }\n",[21,740,741,746,751],{"__ignoreMap":23},[97,742,743],{"class":99,"line":100},[97,744,745],{"class":436},"\u002F* 必须排除首屏——hero 里的 .big 是那个巨大的主标题，\n",[97,747,748],{"class":99,"line":118},[97,749,750],{"class":436},"   被这条命中会从 132px 压到 56px，整个首屏塌掉 *\u002F\n",[97,752,753,756,758,760,763,765,767,769,771,773,775,777,779,781,783,785,787,789,791],{"class":99,"line":131},[97,754,755],{"class":103},".page:not",[97,757,702],{"class":107},[97,759,616],{"class":103},[97,761,762],{"class":107},") :deep(",[97,764,690],{"class":103},[97,766,322],{"class":107},[97,768,244],{"class":139},[97,770,108],{"class":107},[97,772,699],{"class":139},[97,774,702],{"class":107},[97,776,705],{"class":139},[97,778,287],{"class":272},[97,780,710],{"class":107},[97,782,713],{"class":139},[97,784,716],{"class":272},[97,786,710],{"class":107},[97,788,721],{"class":139},[97,790,287],{"class":272},[97,792,726],{"class":107},[10,794,795,796,799,800,802,803,805],{},"两个问题的成因相同：",[583,797,798],{},"同一个类名在两种语义下被复用","。",[21,801,595],{}," 既是容器也是列表页的内边距来源，",[21,804,690],{}," 既是区块小标题也是首屏主标题。分开之后就没这个问题。",[10,807,808],{},"修完实测：hero 左 0、底 1、右 10（右侧那 10px 是站点滚动条本身的宽度），标题 132px 满值，8\u002F8 页仍零溢出。",[28,810,812],{"id":811},"九加载页期间的一个时序漏洞","九、加载页期间的一个时序漏洞",[10,814,815],{},"这个站有一个启动加载页，首次访问时盖几秒。加载页盖着的时候，入场动画被暂停了——否则动画会在遮挡期间自己跑完。",[10,817,818],{},"副作用是页面切换的幕布动画也被冻住：",[47,820,821],{},[10,822,823],{},"实测（加载页 3s 时点击）：t=745 路由已切到 \u002Fcolumns 而幕布 display:none，t=1406 幕布才出现并扫过。",[10,825,826],{},"路由在没有幕布的情况下切换了，等加载页结束、时间线恢复之后，被冻住的补间才补播。",[10,828,829],{},"结论是补一行守卫：加载页盖着时直接放行，不放幕布。理由是：",[47,831,832],{},[10,833,834],{},"真实用户点不到——加载页是全屏遮罩。但浏览器前进\u002F后退、程序化跳转仍可能在窗口内触发导航。",[28,836,838],{"id":837},"十整屏分页的账","十、整屏分页的账",[10,840,841],{},"这套改动最后收敛成一张数字表：",[843,844,845,858],"table",{},[846,847,848],"thead",{},[849,850,851,855],"tr",{},[852,853,854],"th",{},"项目",[852,856,857],{},"数值",[859,860,861,870,878,886,894,902,910],"tbody",{},[849,862,863,867],{},[864,865,866],"td",{},"页数",[864,868,869],{},"8",[849,871,872,875],{},[864,873,874],{},"吸附类型",[864,876,877],{},"proximity，阈值 30%，防抖 500ms",[849,879,880,883],{},[864,881,882],{},"吸附时长",[864,884,885],{},"1.1s",[849,887,888,891],{},[864,889,890],{},"分页生效条件",[864,892,893],{},"宽 ≥ 1024px 且 高 ≥ 620px 且 未开启减弱动效",[849,895,896,899],{},[864,897,898],{},"溢出",[864,900,901],{},"三档均 0\u002F8",[849,903,904,907],{},[864,905,906],{},"填充率",[864,908,909],{},"61%~100%（改前 47%~63%）",[849,911,912,915],{},[864,913,914],{},"首屏标题",[864,916,917],{},"132px（1440×900、1920×1080）、92.9px（1366×768）",[10,919,920],{},"每一条都有一次实测在后面。这类改动没别的办法——「一屏装不装得下」这件事只能量，量完才知道该压间距还是该砍内容。",[10,922,923,924,926],{},"中间还有两处是判断上的错，不是量的问题：一是用 ",[21,925,68],{}," 吸附（把用户的滑动抢走了），二是把「矮视口」定在 900（把有空间的屏幕当成没空间）。前者的修法是换模式，后者的修法是挪断点。",[928,929,930],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":23,"searchDepth":118,"depth":118,"links":932},[933,934,936,937,938,939,940,941,942,943],{"id":30,"depth":118,"text":31},{"id":64,"depth":118,"text":935},"二、mandatory 是错的",{"id":201,"depth":118,"text":202},{"id":388,"depth":118,"text":389},{"id":451,"depth":118,"text":452},{"id":526,"depth":118,"text":527},{"id":562,"depth":118,"text":563},{"id":578,"depth":118,"text":579},{"id":811,"depth":118,"text":812},{"id":837,"depth":118,"text":838},"工程手记","2026-08-02","md",null,{},true,"\u002F2026-08-02",{"title":5,"description":12},"2026-08-02-首页改成八屏","把博客首页从连续滚动改成八屏整屏分页。吸附用 Lenis 自带能力而不是 CSS 原生，断点按实测调整，每一档的溢出与填充率都有数字。",[955,956,957,958,959,960],"GSAP","Lenis","滚动吸附","响应式断点","页面结构","prefers-reduced-motion","2o9dKTJYPwCLxF__bLLN46O2x_0PMJoQgyZSxZpO428",[963,1160,1509],{"id":964,"title":965,"body":966,"column":944,"date":1145,"description":1146,"extension":946,"hero_image":947,"meta":1147,"navigation":949,"path":1148,"seo":1149,"series_id":947,"severity":947,"stem":1150,"summary":1151,"tags":1152,"__hash__":1159},"posts\u002F2026-09-10-Ai项目群的工程化分水岭.md","AI 项目做多以后，真正难的是把边界接起来",{"type":7,"value":967,"toc":1134},[968,975,978,981,985,990,1000,1003,1006,1022,1025,1029,1047,1050,1054,1063,1066,1070,1073,1076,1096,1099,1103,1106,1109,1112,1115,1118,1121,1125,1128,1131],[10,969,970,971,974],{},"这段时间回看 ",[21,972,973],{},"\u002FUsers\u002Fxingye\u002FAi"," 下的项目，最明显的变化不是项目数量增加，而是问题的重心变了。",[10,976,977],{},"早期问题集中在“功能能不能跑起来”：语音推理、网页生成、桌面端启动、小程序页面、视频服务。进入平台化阶段以后，问题变成了另一组：任务是否可追踪，失败是否能恢复，费用是否能对账，版本是否能回滚，用户是否知道发生了什么。",[10,979,980],{},"这是一条从功能开发走向系统工程的分水岭。",[28,982,984],{"id":983},"一项目群已经形成几条清晰的产品线","一、项目群已经形成几条清晰的产品线",[986,987,989],"h3",{"id":988},"agent-与多租户平台","Agent 与多租户平台",[10,991,992,995,996,999],{},[21,993,994],{},"yun-claw"," 与 ",[21,997,998],{},"yun-claude"," 的记录显示，平台主线已经覆盖用户容器、模型接入、画布工作流、代理渠道、后台管理和运营工具。近期提交涉及画布自绘 Select、资产大图预览、代理激活码申请，以及渠道注册页的差异化展示。",[10,1001,1002],{},"这些功能看起来分属不同页面，底层却共享一套问题：用户身份、资源权限、异步任务和状态反馈必须保持一致。只改页面，不补状态契约，功能就会在跨页面操作时暴露断点。",[986,1004,1005],{"id":1005},"内容生产流水线",[10,1007,1008,1011,1012,1011,1015,995,1018,1021],{},[21,1009,1010],{},"ai-write","、",[21,1013,1014],{},"instantlink-site",[21,1016,1017],{},"ai-auto-edit",[21,1019,1020],{},"ai-digital-human-workflow"," 分别覆盖写作、漫剧、自动剪辑和数字人视频。提交记录里反复出现几个关键词：任务网关、切片下载、共享卷、FFmpeg、提示词优化、失败提示和构建复现。",[10,1023,1024],{},"这说明内容生产已经不是单次调用模型，而是一条有中间产物的流水线。文本、图片、视频、音频和项目资产都有生命周期，任何一步的状态丢失都会让用户无法判断下一步该做什么。",[986,1026,1028],{"id":1027},"交付网关与桌面端","交付、网关与桌面端",[10,1030,1031,1011,1034,1011,1037,1011,1040,419,1043,1046],{},[21,1032,1033],{},"newCodex",[21,1035,1036],{},"new-openclaw",[21,1038,1039],{},"openclaw",[21,1041,1042],{},"服务器管理",[21,1044,1045],{},"SSHTS"," 的记录，集中在更新、恢复、远程诊断、单实例生命周期和离线环境上。这里的核心指标不是“新版本发布了没有”，而是安装失败能否定位、更新中断能否恢复、客户机器能否留下可审计证据。",[10,1048,1049],{},"这也是为什么桌面端工程会逐渐靠近运维工程：安装器、后台服务、日志、配置和回滚必须一起设计。",[986,1051,1053],{"id":1052},"网关计费与数据观察","网关、计费与数据观察",[10,1055,1056,995,1059,1062],{},[21,1057,1058],{},"NewAPI",[21,1060,1061],{},"new-api"," 的近期记录包括按模型统计 Token 消耗、扣费量、时间粒度聚合、昨日活跃用户和订单搜索。网关不只是转发请求，它还承担了模型选择、消耗记录、后台观察和问题追踪。",[10,1064,1065],{},"一旦计费数据进入用户体验，接口成功与业务成功就不再是同一个概念。请求返回 200，只能说明一次网络调用完成；扣费、产物落盘和任务终态是否一致，需要单独核对。",[28,1067,1069],{"id":1068},"二git-记录里最有价值的变化是边界被写出来了","二、Git 记录里最有价值的变化，是“边界被写出来了”",[10,1071,1072],{},"多个项目的文档都在补同一类内容：部署手册、验收清单、测试报告、故障复盘、模型环境快照和用户协议。",[10,1074,1075],{},"这类文档的价值不在于把代码再讲一遍，而在于把系统边界写清楚：",[160,1077,1078,1081,1084,1087,1090,1093],{},[163,1079,1080],{},"输入是什么，哪些字段必须存在；",[163,1082,1083],{},"状态有哪些，什么条件才能进入下一状态；",[163,1085,1086],{},"外部服务超时后，如何判断请求是否已经执行；",[163,1088,1089],{},"产物写入失败时，是否允许重试；",[163,1091,1092],{},"更新中断后，哪个版本可以安全恢复；",[163,1094,1095],{},"用户看到的成功，是否和后台记录的成功一致。",[10,1097,1098],{},"当这些问题没有答案时，系统只能靠人工经验维持。一旦项目数量增加，经验就会变成不可复制的隐性依赖。",[28,1100,1102],{"id":1101},"三下一阶段的重点不是再加一个入口","三、下一阶段的重点不是再加一个入口",[10,1104,1105],{},"从现有项目的提交和文档可以看到，下一阶段更值得投入的是四个基础能力。",[10,1107,1108],{},"第一，统一任务状态。不同项目都在处理异步任务，但状态命名、失败原因和重试策略不应各自发明。",[10,1110,1111],{},"第二，统一产物账本。一个任务产生了哪些文件、消耗了哪些资源、是否对用户可见，需要能从提交到终态完整追踪。",[10,1113,1114],{},"第三，统一故障出口。401、超时、部分成功、未知执行和版本回滚，应该有明确的用户反馈与后台证据。",[10,1116,1117],{},"第四，统一发布验收。构建通过不代表线上可用，应该把健康检查、代理路径、真实端口、关键日志和浏览器操作放到同一份验收清单里。",[10,1119,1120],{},"项目多以后，工程能力不再体现在“能写多少功能”，而体现在“能否让不同功能遵守同一套边界”。",[28,1122,1124],{"id":1123},"四我会保留的判断","四、我会保留的判断",[10,1126,1127],{},"AI 项目最容易被低估的部分，不是模型调用，而是模型调用之后的系统。",[10,1129,1130],{},"生成只是起点。身份、任务、费用、存储、交付、恢复和反馈，决定了一个功能能不能进入真实使用。",[10,1132,1133],{},"当 Git 提交开始频繁出现“补状态”“补恢复”“补验收”“补审计”时，这不是工程变慢了，而是产品从演示阶段进入了可运营阶段。",{"title":23,"searchDepth":118,"depth":118,"links":1135},[1136,1142,1143,1144],{"id":983,"depth":118,"text":984,"children":1137},[1138,1139,1140,1141],{"id":988,"depth":131,"text":989},{"id":1005,"depth":131,"text":1005},{"id":1027,"depth":131,"text":1028},{"id":1052,"depth":131,"text":1053},{"id":1068,"depth":118,"text":1069},{"id":1101,"depth":118,"text":1102},{"id":1123,"depth":118,"text":1124},"2026-09-10","这段时间回看 \u002FUsers\u002Fxingye\u002FAi 下的项目，最明显的变化不是项目数量增加，而是问题的重心变了。",{},"\u002F2026-09-10-ai",{"title":965,"description":1146},"2026-09-10-Ai项目群的工程化分水岭","从多个 AI 项目的 Git 记录和设计文档看，工程难点已经从“能不能生成”转向任务、计费、交付、恢复与用户反馈能否形成闭环。",[1153,1154,1155,1156,1157,1158],"AI 平台","Agent","内容生产","网关","桌面端","工程化","DzOpOX92EsanPp8pLNTusbzBmKH3WOTf3qcaZAFmhM8",{"id":1161,"title":1162,"body":1163,"column":944,"date":1496,"description":1167,"extension":946,"hero_image":947,"meta":1497,"navigation":949,"path":1498,"seo":1499,"series_id":947,"severity":947,"stem":1500,"summary":1501,"tags":1502,"__hash__":1508},"posts\u002F2026-08-15-上线前我把整个仓库审了一遍.md","上线前，我把整个仓库审了一遍",{"type":7,"value":1164,"toc":1485},[1165,1168,1171,1175,1178,1199,1202,1205,1209,1212,1215,1218,1221,1225,1228,1231,1291,1294,1300,1304,1309,1312,1315,1322,1326,1329,1340,1343,1347,1350,1356,1359,1372,1375,1378,1382,1385,1392,1398,1401,1405,1408,1413,1430,1435,1449,1452,1455,1459,1462,1482],[10,1166,1167],{},"上线前做了一次全量静态审查。",[10,1169,1170],{},"本文只讲方法和结论分布。审查报告本身不入库，也不在这里复述——那份报告带内网拓扑、密钥存放位置和尚未修复问题的文件行号证据，公开它就等于公开一份攻击者的地图。",[28,1172,1174],{"id":1173},"一审查怎么进行","一、审查怎么进行",[10,1176,1177],{},"三个部分：",[1179,1180,1181,1187,1193],"ol",{},[163,1182,1183,1186],{},[583,1184,1185],{},"人工列清单。"," 先按「一个系统可能从哪些地方被打穿」列出检查面，而不是按目录扫。",[163,1188,1189,1192],{},[583,1190,1191],{},"四个并行只读探查代理。"," 每个代理负责一组检查面，只读，不允许改任何文件。",[163,1194,1195,1198],{},[583,1196,1197],{},"关键证据人工逐条复核。"," 代理解释现象可以，但「这条算不算风险、算多重」由人定。",[10,1200,1201],{},"第三步是必要的。代理很容易把「写法不理想」报成「高危」，也很容易漏掉需要跨文件才能看出的问题。它适合做的是穷举和取证，不适合定级。",[10,1203,1204],{},"审查方式记为：静态审查（人工 + 4 个并行只读探查代理）+ 关键证据人工逐条复核。",[28,1206,1208],{"id":1207},"二只读约束的作用","二、只读约束的作用",[10,1210,1211],{},"让探查代理只能读，有两个好处。",[10,1213,1214],{},"一是没有副作用。审查过程中仓库保持原样，报告的结论可以对着同一个 commit 复现。",[10,1216,1217],{},"二是省掉了「代理顺手改一下」的干扰。审查和修改混在一起，会出现「报告说有问题的地方已经被改过」的状态，后续复核就无法对着证据走。",[10,1219,1220],{},"修改是审查之后单独一轮的事。",[28,1222,1224],{"id":1223},"三20-条与四个级别","三、20 条与四个级别",[10,1226,1227],{},"产出 20 条风险，编号 R01 到 R20。每一条有：位置、触发条件、影响、修复优先级。",[10,1229,1230],{},"按修复优先级分四档：",[843,1232,1233,1246],{},[846,1234,1235],{},[849,1236,1237,1240,1243],{},[852,1238,1239],{},"优先级",[852,1241,1242],{},"条数",[852,1244,1245],{},"排期",[859,1247,1248,1259,1270,1281],{},[849,1249,1250,1253,1256],{},[864,1251,1252],{},"P0",[864,1254,1255],{},"2",[864,1257,1258],{},"本周内",[849,1260,1261,1264,1267],{},[864,1262,1263],{},"P1",[864,1265,1266],{},"6",[864,1268,1269],{},"两周内",[849,1271,1272,1275,1278],{},[864,1273,1274],{},"P2",[864,1276,1277],{},"5",[864,1279,1280],{},"一个月内",[849,1282,1283,1286,1289],{},[864,1284,1285],{},"P3",[864,1287,1288],{},"7",[864,1290,1280],{},[10,1292,1293],{},"另外有一个独立的「严重级别」列，按严重 \u002F 高 \u002F 中 \u002F 低分：1 \u002F 4 \u002F 7 \u002F 8。两列不对应是有意的——「有多严重」和「该多快修」是两件事。一条严重但需要停机才能修的问题，排期上反而不能放在本周。",[10,1295,1296,1297,799],{},"整体评级：",[583,1298,1299],{},"高",[28,1301,1303],{"id":1302},"四结论里最值得记的一句","四、结论里最值得记的一句",[47,1305,1306],{},[10,1307,1308],{},"未发现 SQL 注入、越权与 IDOR、存储型 XSS 等直接可利用的应用漏洞。",[10,1310,1311],{},"这一句和「整体评级高」摆在一起看，是这个月里最值得记的一条工程结论。",[10,1313,1314],{},"风险分布和直觉不一致。业务代码那一侧没查出可直接利用的漏洞——那部分每天在改、每天在跑、每天有人看。问题更多集中在业务代码之外：构建、部署、配置、凭据流转、依赖引入、运行时加固。",[10,1316,1317,1318,1321],{},"这些东西的共同点是",[583,1319,1320],{},"平时不跑","。部署脚本一天执行几次，Dockerfile 改一次放很久，集群配置是照着文档抄的。没有日常反馈的东西，问题会一直留在那里，而且不会有人因为「它今天没出事」而去核对它。",[28,1323,1325],{"id":1324},"五修复的排期方式","五、修复的排期方式",[10,1327,1328],{},"路线图按三档排：P0 本周、P1 两周内、P2\u002FP3 一个月内。",[10,1330,1331,1332,1335,1336,1339],{},"排期依据不是严重级别，是两件事：",[583,1333,1334],{},"能不能立即修","，和",[583,1337,1338],{},"修它会不会动到线上","。一条严重但需要停机或改基础设施配置的问题，得等一个发布窗口；一条中等但改动只在一处代码里的问题，可以马上做。P2\u002FP3 是加固项，攒到下一个版本一起走。",[10,1341,1342],{},"这样做的好处是排期可执行。「两个月内修完 20 条」听起来完整，实际会卡在第一条需要停机的问题上。",[28,1344,1346],{"id":1345},"六报告为什么不入库","六、报告为什么不入库",[10,1348,1349],{},"仓库的忽略规则里专门有一条：",[14,1351,1354],{"className":1352,"code":1353,"language":19},[17],"# 安全审查报告：含内网拓扑、密钥存放路径与未修复漏洞的 file:line 证据，绝不入库\n",[21,1355,1353],{"__ignoreMap":23},[10,1357,1358],{},"三条理由，最后一条最关键：",[160,1360,1361,1364,1367],{},[163,1362,1363],{},"内网拓扑",[163,1365,1366],{},"密钥存放位置",[163,1368,1369],{},[583,1370,1371],{},"未修复漏洞的精确定位",[10,1373,1374],{},"前两条是常识。第三条是这类文档的特例：一份「待修复清单」在修完之前，本身就是攻击说明书。而它又必须有精确定位，否则修不动。",[10,1376,1377],{},"所以这份文档的流转方式和代码相反。代码进仓库、留历史；它不进仓库，修完一条删一条，修完的结论改记到提交信息里。",[28,1379,1381],{"id":1380},"七审查之后同一天修的东西","七、审查之后，同一天修的东西",[10,1383,1384],{},"审查和修复放在同一天做，这是这次的一个做法。",[10,1386,1387,1388,1391],{},"当天修的一批，主题集中在一类：",[583,1389,1390],{},"不报错的失败","。队列投递端断线后不重连、依赖返回的包装形状读错导致判定恒为假、入队不带权重插到最前面、参数格式不对被上游全部拒收、重试链被自己的去重压短、管理操作的幂等键缺失、用户可控的文件名直接进对象存储键、唯一漏掉来源校验的 IPC handler、并发删除后返回假成功、过期验证码被复活、临时文件不清理。",[10,1393,1394,1395,799],{},"这些已经有单独一篇记录，这里只提它们的共性：",[583,1396,1397],{},"都不抛异常",[10,1399,1400],{},"审查报告里那些「不进仓库」的条目和这些「当天修完」的条目，其实是同一批检查过出来的。区别只在于前者需要动基础设施、要等窗口，后者可以立刻改。",[28,1402,1404],{"id":1403},"八静态审查的边界","八、静态审查的边界",[10,1406,1407],{},"这一轮做的是静态审查，能覆盖和不能覆盖的东西都比较明确。",[10,1409,1410],{},[583,1411,1412],{},"能覆盖：",[160,1414,1415,1418,1421,1424,1427],{},[163,1416,1417],{},"配置与依赖：镜像标签、依赖来源、加密参数、权限配置。",[163,1419,1420],{},"错误处理：异常的吞掉与上抛、失败路径有没有留痕。",[163,1422,1423],{},"凭据流转：什么地方读、什么地方写、会不会进构建产物。",[163,1425,1426],{},"部署清单：运行用户、资源限制、网络策略、滚动更新参数。",[163,1428,1429],{},"代码层面的模式：路径拼接、命令拼接、输入校验的缺失。",[10,1431,1432],{},[583,1433,1434],{},"覆盖不到：",[160,1436,1437,1440,1443,1446],{},[163,1438,1439],{},"运行时竞态。两个请求同时到、并发删除、租约过期，这些要在真实并发下才暴露。",[163,1441,1442],{},"真实的流量特征。哪些入口真的被外部打到、参数实际长什么样，静态看只能推测。",[163,1444,1445],{},"业务逻辑漏洞。越权取数据这类问题需要理解「这个人该不该看到这条」，而业务意图不在代码里。",[163,1447,1448],{},"组合链。单看每一步都合规，串起来才是问题——这类要动态测试或者真实对抗。",[10,1450,1451],{},"第三类是最容易被静态审查漏掉的：代码里所有校验都写了，但校验的规则和业务规则不一致。",[10,1453,1454],{},"所以静态审查的产出不是「系统安全了」，而是「在它能看见的那一面里，还剩哪些要做」。",[28,1456,1458],{"id":1457},"九一次审查真正留下的东西","九、一次审查真正留下的东西",[10,1460,1461],{},"审完当天修掉的那批，留下的是代码。报告里那些没修的，留下的是三样东西：",[1179,1463,1464,1470,1476],{},[163,1465,1466,1469],{},[583,1467,1468],{},"一份带精确定位和优先级的清单","，不进仓库，逐条消掉。",[163,1471,1472,1475],{},[583,1473,1474],{},"一个判断","：风险集中的地方和日常注意力集中的地方不一样。业务代码天天改，反而干净；构建部署配置平时没人动。",[163,1477,1478,1481],{},[583,1479,1480],{},"一次复核的习惯","：审查的结论要能对着一个 commit 复现，所以探查只读、修改单独一轮。",[10,1483,1484],{},"第 2 条比第 1 条重要。清单会修完，但「平时不跑的东西没人核对」这件事不会自己消失。",{"title":23,"searchDepth":118,"depth":118,"links":1486},[1487,1488,1489,1490,1491,1492,1493,1494,1495],{"id":1173,"depth":118,"text":1174},{"id":1207,"depth":118,"text":1208},{"id":1223,"depth":118,"text":1224},{"id":1302,"depth":118,"text":1303},{"id":1324,"depth":118,"text":1325},{"id":1345,"depth":118,"text":1346},{"id":1380,"depth":118,"text":1381},{"id":1403,"depth":118,"text":1404},{"id":1457,"depth":118,"text":1458},"2026-08-15",{},"\u002F2026-08-15",{"title":1162,"description":1167},"2026-08-15-上线前我把整个仓库审了一遍","一次上线前的静态安全审查：四个并行只读代理加人工逐条复核，产出 20 条分级风险与三档修复路线图。本文记录方法，不复述条目。",[1503,1504,1505,1506,1507],"安全审查","代码审查","风险分级","修复排期","Agent 协作","bnkC_2bMe8OfgB00HIfDUS9bdRwLUZb_WqDBvXtq__M",{"id":4,"title":5,"body":1510,"column":944,"date":945,"description":12,"extension":946,"hero_image":947,"meta":2195,"navigation":949,"path":950,"seo":2196,"series_id":947,"severity":947,"stem":952,"summary":953,"tags":2197,"__hash__":961},{"type":7,"value":1511,"toc":2183},[1512,1514,1519,1521,1523,1525,1531,1537,1539,1541,1545,1549,1553,1557,1601,1603,1617,1621,1625,1627,1632,1634,1636,1638,1644,1646,1648,1653,1655,1673,1675,1763,1765,1770,1772,1774,1786,1794,1796,1808,1810,1812,1814,1819,1821,1823,1828,1830,1872,1874,1876,1878,1880,1885,1887,1889,1893,1898,1900,1902,1904,1909,1911,1915,1951,1957,1959,1985,1989,1991,2031,2035,2037,2089,2097,2099,2101,2103,2105,2109,2111,2113,2117,2119,2121,2175,2177,2181],[10,1513,12],{},[14,1515,1517],{"className":1516,"code":18,"language":19},[17],[21,1518,18],{"__ignoreMap":23},[10,1520,26],{},[28,1522,31],{"id":30},[10,1524,34],{},[10,1526,37,1527,41,1529,45],{},[21,1528,40],{},[21,1530,44],{},[47,1532,1533],{},[10,1534,51,1535,55],{},[21,1536,54],{},[10,1538,58],{},[10,1540,61],{},[28,1542,65,1543,69],{"id":64},[21,1544,68],{},[10,1546,72,1547,76],{},[21,1548,75],{},[10,1550,79,1551,82],{},[21,1552,68],{},[10,1554,85,1555,45],{},[21,1556,88],{},[14,1558,1559],{"className":91,"code":92,"language":93,"meta":23,"style":23},[21,1560,1561,1571,1581,1591],{"__ignoreMap":23},[97,1562,1563,1565,1567,1569],{"class":99,"line":100},[97,1564,104],{"class":103},[97,1566,108],{"class":107},[97,1568,112],{"class":111},[97,1570,115],{"class":107},[97,1572,1573,1575,1577,1579],{"class":99,"line":118},[97,1574,121],{"class":103},[97,1576,108],{"class":107},[97,1578,126],{"class":111},[97,1580,115],{"class":107},[97,1582,1583,1585,1587,1589],{"class":99,"line":131},[97,1584,134],{"class":103},[97,1586,108],{"class":107},[97,1588,140],{"class":139},[97,1590,115],{"class":107},[97,1592,1593,1595,1597,1599],{"class":99,"line":145},[97,1594,148],{"class":103},[97,1596,108],{"class":107},[97,1598,153],{"class":139},[97,1600,115],{"class":107},[10,1602,158],{},[160,1604,1605,1609,1613],{},[163,1606,1607,167],{},[21,1608,88],{},[163,1610,1611,173],{},[21,1612,172],{},[163,1614,1615,179],{},[21,1616,178],{},[10,1618,1619,184],{},[21,1620,148],{},[47,1622,1623],{},[10,1624,189],{},[10,1626,192],{},[14,1628,1630],{"className":1629,"code":196,"language":19},[17],[21,1631,196],{"__ignoreMap":23},[28,1633,202],{"id":201},[10,1635,205],{},[10,1637,208],{},[160,1639,1640,1642],{},[163,1641,213],{},[163,1643,216],{},[10,1645,219],{},[10,1647,222],{},[14,1649,1651],{"className":1650,"code":226,"language":19},[17],[21,1652,226],{"__ignoreMap":23},[10,1654,231],{},[14,1656,1657],{"className":234,"code":235,"language":236,"meta":23,"style":23},[21,1658,1659],{"__ignoreMap":23},[97,1660,1661,1663,1665,1667,1669,1671],{"class":99,"line":100},[97,1662,244],{"class":243},[97,1664,247],{"class":107},[97,1666,250],{"class":103},[97,1668,253],{"class":107},[97,1670,256],{"class":103},[97,1672,259],{"class":107},[10,1674,262],{},[14,1676,1677],{"className":234,"code":265,"language":236,"meta":23,"style":23},[21,1678,1679,1709,1739,1759],{"__ignoreMap":23},[97,1680,1681,1683,1685,1687,1689,1691,1693,1695,1697,1699,1701,1703,1705,1707],{"class":99,"line":100},[97,1682,273],{"class":272},[97,1684,276],{"class":107},[97,1686,279],{"class":139},[97,1688,108],{"class":107},[97,1690,284],{"class":139},[97,1692,287],{"class":272},[97,1694,290],{"class":107},[97,1696,293],{"class":272},[97,1698,276],{"class":107},[97,1700,298],{"class":139},[97,1702,108],{"class":107},[97,1704,303],{"class":139},[97,1706,287],{"class":272},[97,1708,308],{"class":107},[97,1710,1711,1713,1715,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735,1737],{"class":99,"line":118},[97,1712,313],{"class":103},[97,1714,316],{"class":107},[97,1716,319],{"class":103},[97,1718,322],{"class":107},[97,1720,325],{"class":139},[97,1722,108],{"class":107},[97,1724,330],{"class":139},[97,1726,287],{"class":272},[97,1728,335],{"class":107},[97,1730,338],{"class":139},[97,1732,108],{"class":107},[97,1734,343],{"class":139},[97,1736,287],{"class":272},[97,1738,348],{"class":107},[97,1740,1741,1743,1745,1747,1749,1751,1753,1755,1757],{"class":99,"line":131},[97,1742,313],{"class":103},[97,1744,316],{"class":107},[97,1746,357],{"class":103},[97,1748,322],{"class":107},[97,1750,362],{"class":139},[97,1752,108],{"class":107},[97,1754,367],{"class":139},[97,1756,287],{"class":272},[97,1758,348],{"class":107},[97,1760,1761],{"class":99,"line":145},[97,1762,376],{"class":107},[10,1764,379],{},[14,1766,1768],{"className":1767,"code":383,"language":19},[17],[21,1769,383],{"__ignoreMap":23},[28,1771,389],{"id":388},[10,1773,392],{},[14,1775,1776],{"className":91,"code":395,"language":93,"meta":23,"style":23},[21,1777,1778],{"__ignoreMap":23},[97,1779,1780,1782,1784],{"class":99,"line":100},[97,1781,402],{"class":107},[97,1783,405],{"class":272},[97,1785,408],{"class":107},[10,1787,411,1788,415,1790,419,1792,423],{},[21,1789,414],{},[21,1791,418],{},[21,1793,422],{},[10,1795,426],{},[14,1797,1798],{"className":91,"code":429,"language":93,"meta":23,"style":23},[21,1799,1800,1804],{"__ignoreMap":23},[97,1801,1802],{"class":99,"line":100},[97,1803,437],{"class":436},[97,1805,1806],{"class":99,"line":118},[97,1807,442],{"class":436},[10,1809,445],{},[10,1811,448],{},[28,1813,452],{"id":451},[14,1815,1817],{"className":1816,"code":456,"language":19},[17],[21,1818,456],{"__ignoreMap":23},[10,1820,461],{},[10,1822,464],{},[14,1824,1826],{"className":1825,"code":468,"language":19},[17],[21,1827,468],{"__ignoreMap":23},[10,1829,473],{},[14,1831,1832],{"className":234,"code":476,"language":236,"meta":23,"style":23},[21,1833,1834,1840,1854,1868],{"__ignoreMap":23},[97,1835,1836,1838],{"class":99,"line":100},[97,1837,273],{"class":272},[97,1839,485],{"class":107},[97,1841,1842,1844,1846,1848,1850,1852],{"class":99,"line":118},[97,1843,490],{"class":103},[97,1845,493],{"class":107},[97,1847,414],{"class":139},[97,1849,108],{"class":107},[97,1851,500],{"class":139},[97,1853,348],{"class":107},[97,1855,1856,1858,1860,1862,1864,1866],{"class":99,"line":131},[97,1857,507],{"class":103},[97,1859,493],{"class":107},[97,1861,512],{"class":139},[97,1863,108],{"class":107},[97,1865,517],{"class":139},[97,1867,348],{"class":107},[97,1869,1870],{"class":99,"line":145},[97,1871,376],{"class":107},[28,1873,527],{"id":526},[10,1875,530],{},[10,1877,533],{},[10,1879,536],{},[14,1881,1883],{"className":1882,"code":540,"language":19},[17],[21,1884,540],{"__ignoreMap":23},[10,1886,545],{},[10,1888,548],{},[47,1890,1891],{},[10,1892,553],{},[14,1894,1896],{"className":1895,"code":557,"language":19},[17],[21,1897,557],{"__ignoreMap":23},[28,1899,563],{"id":562},[10,1901,566],{},[10,1903,569],{},[14,1905,1907],{"className":1906,"code":573,"language":19},[17],[21,1908,573],{"__ignoreMap":23},[28,1910,579],{"id":578},[10,1912,1913],{},[583,1914,585],{},[14,1916,1917],{"className":234,"code":588,"language":236,"meta":23,"style":23},[21,1918,1919,1937],{"__ignoreMap":23},[97,1920,1921,1923,1925,1927,1929,1931,1933,1935],{"class":99,"line":100},[97,1922,595],{"class":103},[97,1924,493],{"class":107},[97,1926,600],{"class":139},[97,1928,108],{"class":107},[97,1930,343],{"class":139},[97,1932,287],{"class":272},[97,1934,609],{"class":139},[97,1936,348],{"class":107},[97,1938,1939,1941,1943,1945,1947,1949],{"class":99,"line":118},[97,1940,616],{"class":103},[97,1942,493],{"class":107},[97,1944,600],{"class":139},[97,1946,108],{"class":107},[97,1948,500],{"class":139},[97,1950,348],{"class":107},[10,1952,1953,632,1955,636],{},[21,1954,631],{},[21,1956,635],{},[10,1958,639],{},[14,1960,1961],{"className":234,"code":642,"language":236,"meta":23,"style":23},[21,1962,1963,1967,1971],{"__ignoreMap":23},[97,1964,1965],{"class":99,"line":100},[97,1966,649],{"class":436},[97,1968,1969],{"class":99,"line":118},[97,1970,654],{"class":436},[97,1972,1973,1975,1977,1979,1981,1983],{"class":99,"line":131},[97,1974,616],{"class":103},[97,1976,493],{"class":107},[97,1978,600],{"class":139},[97,1980,108],{"class":107},[97,1982,500],{"class":139},[97,1984,348],{"class":107},[10,1986,1987],{},[583,1988,673],{},[10,1990,676],{},[14,1992,1993],{"className":234,"code":679,"language":236,"meta":23,"style":23},[21,1994,1995],{"__ignoreMap":23},[97,1996,1997,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027,2029],{"class":99,"line":100},[97,1998,595],{"class":103},[97,2000,316],{"class":107},[97,2002,690],{"class":103},[97,2004,322],{"class":107},[97,2006,244],{"class":139},[97,2008,108],{"class":107},[97,2010,699],{"class":139},[97,2012,702],{"class":107},[97,2014,705],{"class":139},[97,2016,287],{"class":272},[97,2018,710],{"class":107},[97,2020,713],{"class":139},[97,2022,716],{"class":272},[97,2024,710],{"class":107},[97,2026,721],{"class":139},[97,2028,287],{"class":272},[97,2030,726],{"class":107},[10,2032,729,2033,732],{},[21,2034,690],{},[10,2036,735],{},[14,2038,2039],{"className":234,"code":738,"language":236,"meta":23,"style":23},[21,2040,2041,2045,2049],{"__ignoreMap":23},[97,2042,2043],{"class":99,"line":100},[97,2044,745],{"class":436},[97,2046,2047],{"class":99,"line":118},[97,2048,750],{"class":436},[97,2050,2051,2053,2055,2057,2059,2061,2063,2065,2067,2069,2071,2073,2075,2077,2079,2081,2083,2085,2087],{"class":99,"line":131},[97,2052,755],{"class":103},[97,2054,702],{"class":107},[97,2056,616],{"class":103},[97,2058,762],{"class":107},[97,2060,690],{"class":103},[97,2062,322],{"class":107},[97,2064,244],{"class":139},[97,2066,108],{"class":107},[97,2068,699],{"class":139},[97,2070,702],{"class":107},[97,2072,705],{"class":139},[97,2074,287],{"class":272},[97,2076,710],{"class":107},[97,2078,713],{"class":139},[97,2080,716],{"class":272},[97,2082,710],{"class":107},[97,2084,721],{"class":139},[97,2086,287],{"class":272},[97,2088,726],{"class":107},[10,2090,795,2091,799,2093,802,2095,805],{},[583,2092,798],{},[21,2094,595],{},[21,2096,690],{},[10,2098,808],{},[28,2100,812],{"id":811},[10,2102,815],{},[10,2104,818],{},[47,2106,2107],{},[10,2108,823],{},[10,2110,826],{},[10,2112,829],{},[47,2114,2115],{},[10,2116,834],{},[28,2118,838],{"id":837},[10,2120,841],{},[843,2122,2123,2131],{},[846,2124,2125],{},[849,2126,2127,2129],{},[852,2128,854],{},[852,2130,857],{},[859,2132,2133,2139,2145,2151,2157,2163,2169],{},[849,2134,2135,2137],{},[864,2136,866],{},[864,2138,869],{},[849,2140,2141,2143],{},[864,2142,874],{},[864,2144,877],{},[849,2146,2147,2149],{},[864,2148,882],{},[864,2150,885],{},[849,2152,2153,2155],{},[864,2154,890],{},[864,2156,893],{},[849,2158,2159,2161],{},[864,2160,898],{},[864,2162,901],{},[849,2164,2165,2167],{},[864,2166,906],{},[864,2168,909],{},[849,2170,2171,2173],{},[864,2172,914],{},[864,2174,917],{},[10,2176,920],{},[10,2178,923,2179,926],{},[21,2180,68],{},[928,2182,930],{},{"title":23,"searchDepth":118,"depth":118,"links":2184},[2185,2186,2187,2188,2189,2190,2191,2192,2193,2194],{"id":30,"depth":118,"text":31},{"id":64,"depth":118,"text":935},{"id":201,"depth":118,"text":202},{"id":388,"depth":118,"text":389},{"id":451,"depth":118,"text":452},{"id":526,"depth":118,"text":527},{"id":562,"depth":118,"text":563},{"id":578,"depth":118,"text":579},{"id":811,"depth":118,"text":812},{"id":837,"depth":118,"text":838},{},{"title":5,"description":12},[955,956,957,958,959,960],1789212572876]