Initial k1412 research blog
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.astro
|
||||||
|
.git
|
||||||
|
.research-private
|
||||||
|
.env*
|
||||||
|
npm-debug.log*
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.astro/
|
||||||
|
.research-private/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
.DS_Store
|
||||||
|
npm-debug.log*
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM node:24-alpine AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.29-alpine
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/dist /usr/share/nginx/html
|
||||||
|
RUN find /usr/share/nginx/html -type d -exec chmod 0755 {} + \
|
||||||
|
&& find /usr/share/nginx/html -type f -exec chmod 0644 {} +
|
||||||
|
EXPOSE 8080
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD wget -q -O /dev/null http://127.0.0.1:8080/healthz || exit 1
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 wuyang
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# K1412 Research Blog
|
||||||
|
|
||||||
|
The public source for [blog.k1412.top](https://blog.k1412.top): technical
|
||||||
|
research articles, experiment reports, and a catalog of standalone interactive
|
||||||
|
research sites.
|
||||||
|
|
||||||
|
## Local development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run check
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The production build is static:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Public Markdown lives in `content/posts/` and `content/sites/`. Raw research,
|
||||||
|
private evidence, credentials, and internal material must remain outside this
|
||||||
|
repository or under the ignored `.research-private/` directory.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Code and original public content in this repository are available under the MIT
|
||||||
|
License unless an individual asset or linked external site states otherwise.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { defineConfig } from "astro/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
site: "https://blog.k1412.top",
|
||||||
|
output: "static",
|
||||||
|
build: {
|
||||||
|
format: "directory"
|
||||||
|
},
|
||||||
|
markdown: {
|
||||||
|
shikiConfig: {
|
||||||
|
theme: "github-dark"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "技术研究如何形成可验证的公开成果",
|
||||||
|
"summary": "从问题地图、证据台账和边界实验出发,把资料收集转化为可理解、可复现、可维护的公开研究。",
|
||||||
|
"date": "2026-08-10",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "research-method",
|
||||||
|
"tags": ["evidence", "experiments", "publishing", "research"],
|
||||||
|
"kind": "article",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://blog.k1412.top/articles/research-publishing-method/",
|
||||||
|
"sourceRepo": "https://git.k1412.top/wuyang/research-blog"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
# 技术研究如何形成可验证的公开成果
|
||||||
|
|
||||||
|
## 研究对象先于资料目录
|
||||||
|
|
||||||
|
一个技术主题通常同时包含历史、机制、工程实现、实验结果和产品判断。直接从搜索结果开始写,文章很容易变成来源列表:材料很多,读者却不知道它们共同回答了什么问题。
|
||||||
|
|
||||||
|
研究开始时应先建立问题地图。至少写清楚研究对象、时间快照、适用边界、预期读者,以及研究最终要支持的理解或决策。宽泛领域再拆成少量相互关联的专题,每个专题都有自己的核心问题和完成证据。
|
||||||
|
|
||||||
|
问题地图不是固定目录。新证据可以改变专题关系,也可以推翻最初假设;但每次改变都要能解释原因。
|
||||||
|
|
||||||
|
## 证据台账连接判断与来源
|
||||||
|
|
||||||
|
文章中的重要判断需要一条可追溯链路。证据台账为每条判断记录来源、版本、时间、直接支持范围、限制和公开状态。它还要区分不同证据类型:
|
||||||
|
|
||||||
|
| 证据类型 | 能说明什么 | 不能替代什么 |
|
||||||
|
|---|---|---|
|
||||||
|
| 论文、规范与官方文档 | 作者或机构正式声明的设计、方法和结果 | 当前实现与真实运行状态 |
|
||||||
|
| 源码与版本记录 | 某一版本实际实现的接口和路径 | 线上部署已启用该实现 |
|
||||||
|
| 运行与实验记录 | 特定环境、输入和配置下发生的结果 | 未覆盖场景和长期生产表现 |
|
||||||
|
| 二手分析 | 背景、线索和不同解释 | 关键事实的一手证明 |
|
||||||
|
| 推断与方案 | 基于证据形成的判断或下一步 | 已经验证的事实 |
|
||||||
|
|
||||||
|
这层区分能避免常见错位:把设计文档写成现网事实、把静态检查写成端到端结果、把一个配置的成绩推广成整个系统的能力。
|
||||||
|
|
||||||
|
## 机制解释建立因果模型
|
||||||
|
|
||||||
|
技术文章需要回答“为什么”,而不只回答“是什么”。有效的机制解释通常包含四部分:输入条件、内部变化、可观察结果和失败条件。
|
||||||
|
|
||||||
|
例如,讨论推理优化时,单独给出吞吐提升比例并不足够。还需要说明优化改变了计算、内存、调度还是量化路径;基线与候选使用什么硬件、模型和请求形态;延迟边界从哪里开始计算;质量是否使用同一数据与指标。只有这些条件固定后,读者才能判断结果能否迁移到自己的场景。
|
||||||
|
|
||||||
|
具体例子和图可以显著降低理解成本。架构图适合说明组件关系,时序图适合说明事件顺序,状态图适合说明恢复和边界,统计图适合说明规模与变化。概念插画可以建立直觉,但精确标签、数字和方向仍要由正文或确定性图形承载。
|
||||||
|
|
||||||
|
## 比较之前先统一口径
|
||||||
|
|
||||||
|
技术比较最容易在表格完成后失真。不同系统可能使用不同模型、数据、预算、重试次数、成功定义或统计分母。把这些数字放进同一排行,会产生一种并不存在的精确性。
|
||||||
|
|
||||||
|
比较前应固定以下维度:
|
||||||
|
|
||||||
|
1. 任务与成功定义;
|
||||||
|
2. 模型、工具和系统边界;
|
||||||
|
3. 数据版本与污染假设;
|
||||||
|
4. 提示、预算、重试和随机种子;
|
||||||
|
5. 指标、分母、聚合方式与不确定性;
|
||||||
|
6. 硬件、延迟边界和成本边界。
|
||||||
|
|
||||||
|
无法统一时,应解释差异并停止排名。承认“目前不可直接比较”比给出误导性的顺序更有价值。
|
||||||
|
|
||||||
|
## 小样本实验验证链路和评分
|
||||||
|
|
||||||
|
能实验的问题不应停在方案。第一次实验的目标通常不是取得最终成绩,而是确认输入、执行、记录、评分和归因链路都可信。
|
||||||
|
|
||||||
|
实验协议应在运行前定义:假设、基线、候选、控制变量、代表样本、指标、通过阈值、环境版本、停止条件和扩大规则。对评测或 Agent 任务,先运行少量能覆盖关键路径的案例;保留失败样本和原始过程,而不是只保留平均分。
|
||||||
|
|
||||||
|
结果写作也要保持边界。小样本只能证明该样本与环境下的现象;烟测只能证明链路工作;外部服务耗时要与系统自身耗时分开。未运行的部分继续标为计划或估计。
|
||||||
|
|
||||||
|
## 编辑把完整性转化为可读性
|
||||||
|
|
||||||
|
完整研究不等于把所有材料放进正文。正文应保留形成因果模型所需的证据、例子、比较和限制;搜索记录、长摘录、中间脚本和原始输出进入研究工作区。
|
||||||
|
|
||||||
|
深度文章可以很长,但每节只推进一个问题。标题直接描述内容,不使用教学口号或作者的内部工作提示。新术语在第一次出现时解释,关键数字带上单位、分母、版本和时间。读者不需要经历研究者的所有过程,但必须能复核研究者的关键判断。
|
||||||
|
|
||||||
|
## 发布是一个可验证的版本
|
||||||
|
|
||||||
|
公开发布至少要建立一条版本映射:
|
||||||
|
|
||||||
|
```text
|
||||||
|
公开源码提交 → 成功构建 → 不可变镜像 → 线上部署 → 页面内容标记
|
||||||
|
```
|
||||||
|
|
||||||
|
在这条链路之外,还需要内容边界审查、来源链接检查、桌面和移动端阅读检查、图形渲染检查、HTTPS 和关键资源检查。HTTP 状态码只能证明服务器返回了内容,不能证明文章、视觉和源码发布正确。
|
||||||
|
|
||||||
|
当研究需要独立的交互地图、课程或实验室时,可以使用单独站点;中央博客保留清晰目录和来源入口。普通文章则保持稳定的 Markdown 源和文章 URL,让后续证据更新可以在同一条研究脉络中继续发生。
|
||||||
|
|
||||||
|
## 限制与维护
|
||||||
|
|
||||||
|
公开文章永远是某个时间点的证据快照。软件版本、排行榜、价格和组织状态会变化;实验也可能只覆盖有限环境。文章应保留更新时间和适用条件,在证据改变时更新、补充新版本,或明确标记旧结论已经被取代。
|
||||||
|
|
||||||
|
一项研究真正完成的标志,不是材料被收集完,而是读者能理解判断如何形成、在哪些条件下成立、如何复核,以及下一项最有价值的不确定性是什么。
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "Agent 到底凭什么说“完成了”",
|
||||||
|
"summary": "从任务结果、执行轨迹和失败行为出发,解释 Agent 完成声明需要哪些可验证证据。",
|
||||||
|
"date": "2026-07-27",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "agent-systems",
|
||||||
|
"tags": ["agents", "evaluation", "evidence"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://agent-eval.k1412.top",
|
||||||
|
"sourceRepo": "https://git.k1412.top/wuyang/agent",
|
||||||
|
"preview": "/previews/agent-eval.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立专题站。中央博客保留索引,交互内容以原站为准。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "Agent 系统综述 2026:边界、构件、前沿与落地",
|
||||||
|
"summary": "以系统边界、核心构件、研究前沿和工程落地为主线组织 Agent 领域全貌。",
|
||||||
|
"date": "2026-07-28",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "agent-systems",
|
||||||
|
"tags": ["agents", "architecture", "landscape"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://agent-landscape-2026.k1412.top",
|
||||||
|
"preview": "/previews/agent-landscape-2026.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立专题站。历史源码尚未迁入中央公开仓库。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "Agent 系统研究综述 2026:系统、生态、前沿与实践",
|
||||||
|
"summary": "连接 Agent 系统架构、生态项目、研究方向和实践约束的长篇研究综述。",
|
||||||
|
"date": "2026-07-28",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "agent-systems",
|
||||||
|
"tags": ["agents", "ecosystem", "research"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://agent-review-2026.k1412.top",
|
||||||
|
"preview": "/previews/agent-review-2026.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立专题站。历史源码尚未迁入中央公开仓库。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "Astra 的十项数学结果:答案、正确性与理解开始脱钩",
|
||||||
|
"summary": "从十项数学结果、验证流程和人类理解成本分析 AI 辅助数学研究的新阶段。",
|
||||||
|
"date": "2026-08-06",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "ai-research",
|
||||||
|
"tags": ["ai-math", "astra", "verification"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://astra-math.k1412.top",
|
||||||
|
"preview": "/previews/astra-math.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立研究文章站。中央博客保留统一入口。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "27B 推理优化:从基线到生产配置",
|
||||||
|
"summary": "记录 27B 模型在 llama.cpp 路径上的方案变化、三项速度指标和生产配置取舍。",
|
||||||
|
"date": "2026-07-28",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "llm-systems",
|
||||||
|
"tags": ["inference", "llama.cpp", "optimization"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://llama-cpp-report.k1412.top",
|
||||||
|
"preview": "/previews/llama-cpp-report.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立报告站。历史源码尚未迁入中央公开仓库。
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "LLM 技术图谱:从发展脉络到关键机制",
|
||||||
|
"summary": "以 Kimi K3 技术报告为锚点,组织语言模型发展、训练、推理和 DeepSeek 系列的专题课程。",
|
||||||
|
"date": "2026-07-30",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "llm-systems",
|
||||||
|
"tags": ["deepseek", "history", "llm"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://llm-atlas.k1412.top",
|
||||||
|
"sourceRepo": "https://git.k1412.top/wuyang/llm-atlas",
|
||||||
|
"preview": "/previews/llm-atlas.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立课程站。源码在单独的公开仓库中持续维护。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
"title": "模型记忆的分水岭,不是容量,而是写入权",
|
||||||
|
"summary": "从写入权、更新语义和长期状态治理重新理解模型记忆系统的关键边界。",
|
||||||
|
"date": "2026-08-07",
|
||||||
|
"updated": "2026-08-10",
|
||||||
|
"topic": "llm-systems",
|
||||||
|
"tags": ["agents", "architecture", "memory"],
|
||||||
|
"kind": "site",
|
||||||
|
"status": "published",
|
||||||
|
"visibility": "public",
|
||||||
|
"canonicalUrl": "https://llm-memory.k1412.top",
|
||||||
|
"preview": "/previews/llm-memory.jpg"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
独立研究文章站。中央博客保留统一入口。
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location = /healthz {
|
||||||
|
access_log off;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200 'ok\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
location /_astro/ {
|
||||||
|
try_files $uri =404;
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ $uri/index.html =404;
|
||||||
|
add_header Cache-Control "public, max-age=300";
|
||||||
|
}
|
||||||
|
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||||
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "k1412-research-blog",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev --host 127.0.0.1 --port 4321",
|
||||||
|
"preview": "astro preview --host 127.0.0.1 --port 4321",
|
||||||
|
"validate": "node scripts/validate-content.mjs",
|
||||||
|
"check": "npm run validate && astro check",
|
||||||
|
"build": "npm run check && astro build && node scripts/verify-dist.mjs"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "7.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@astrojs/check": "0.9.10",
|
||||||
|
"typescript": "6.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||||
|
<rect width="64" height="64" rx="32" fill="#172429"/>
|
||||||
|
<path d="M18 15v34h7V35l14 14h9L31 32l16-17h-9L25 29V15z" fill="#f0ede5"/>
|
||||||
|
<circle cx="47" cy="17" r="5" fill="#b2482d"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 250 B |
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630">
|
||||||
|
<rect width="1200" height="630" fill="#f0ede5"/>
|
||||||
|
<path d="M0 505C260 380 340 590 603 426s372-155 597-77v281H0z" fill="#172429"/>
|
||||||
|
<circle cx="970" cy="150" r="93" fill="none" stroke="#b2482d" stroke-width="2"/>
|
||||||
|
<circle cx="970" cy="150" r="42" fill="#b2482d"/>
|
||||||
|
<path d="M79 93h315" stroke="#172429" opacity=".25"/>
|
||||||
|
<text x="80" y="205" fill="#b2482d" font-family="Arial, sans-serif" font-size="25" letter-spacing="7">K1412 / OPEN RESEARCH</text>
|
||||||
|
<text x="80" y="310" fill="#172429" font-family="Georgia, serif" font-size="86">K1412 Research</text>
|
||||||
|
<text x="80" y="379" fill="#627077" font-family="Arial, sans-serif" font-size="30">技术研究、实验记录与可验证的公开成果</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 779 B |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 78 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://blog.k1412.top/sitemap.xml
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { access, readFile, readdir } from "node:fs/promises";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
const root = new URL("../", import.meta.url);
|
||||||
|
const contentRoot = new URL("../content/", import.meta.url);
|
||||||
|
const required = ["title", "summary", "date", "updated", "topic", "kind", "status", "visibility", "canonicalUrl"];
|
||||||
|
const forbidden = [
|
||||||
|
[/\/(?:Users|home|mnt)\/[^\s)\]>'"]+/, "private absolute path"],
|
||||||
|
[/\b(?:10\.(?:\d{1,3}\.){2}\d{1,3}|192\.168\.(?:\d{1,3}\.)\d{1,3}|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.(?:\d{1,3}\.)\d{1,3})\b/, "private IPv4"],
|
||||||
|
[/(?:git\.n\.xiaomi\.com|coro\.doc|\.codex\/sessions|<codex_internal_context)/, "internal material"],
|
||||||
|
[/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/, "private key"],
|
||||||
|
[/"?sourcePaths"?\s*:/, "Knowledge Hub private metadata"]
|
||||||
|
];
|
||||||
|
|
||||||
|
async function files(dir) {
|
||||||
|
const entries = await readdir(dir, { withFileTypes: true });
|
||||||
|
const base = fileURLToPath(dir);
|
||||||
|
return entries.flatMap((entry) => entry.isFile() && entry.name.endsWith(".md") ? [join(base, entry.name)] : []);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(text, path) {
|
||||||
|
const match = text.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
||||||
|
if (!match) throw new Error(`${path}: invalid frontmatter delimiters`);
|
||||||
|
return { data: JSON.parse(match[1]), body: match[2].trim() };
|
||||||
|
}
|
||||||
|
|
||||||
|
const paths = [...await files(new URL("posts/", contentRoot)), ...await files(new URL("sites/", contentRoot))];
|
||||||
|
const errors = [];
|
||||||
|
const urls = new Set();
|
||||||
|
for (const path of paths) {
|
||||||
|
const text = await readFile(path, "utf8");
|
||||||
|
let parsed;
|
||||||
|
try { parsed = parse(text, path); } catch (error) { errors.push(error.message); continue; }
|
||||||
|
for (const key of required) if (!parsed.data[key]) errors.push(`${path}: missing ${key}`);
|
||||||
|
if (parsed.data.visibility !== "public") errors.push(`${path}: visibility must be public`);
|
||||||
|
if (!/^https:\/\//.test(parsed.data.canonicalUrl ?? "")) errors.push(`${path}: canonicalUrl must use HTTPS`);
|
||||||
|
if (urls.has(parsed.data.canonicalUrl)) errors.push(`${path}: duplicate canonicalUrl`);
|
||||||
|
urls.add(parsed.data.canonicalUrl);
|
||||||
|
if (parsed.data.kind === "article" && !parsed.body) errors.push(`${path}: empty article body`);
|
||||||
|
if (parsed.data.preview?.startsWith("/")) {
|
||||||
|
try { await access(new URL(`../public${parsed.data.preview}`, import.meta.url)); }
|
||||||
|
catch { errors.push(`${path}: missing preview asset ${parsed.data.preview}`); }
|
||||||
|
}
|
||||||
|
for (const [pattern, label] of forbidden) if (pattern.test(text)) errors.push(`${path}: forbidden ${label}`);
|
||||||
|
}
|
||||||
|
if (errors.length) {
|
||||||
|
console.error(errors.join("\n"));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(`valid: ${paths.length} public content entries at ${root.pathname}`);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { access, readFile, readdir } from "node:fs/promises";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
const root = new URL("../dist/", import.meta.url);
|
||||||
|
const required = ["index.html", "archive/index.html", "method/index.html", "articles/research-publishing-method/index.html", "rss.xml", "sitemap.xml"];
|
||||||
|
for (const path of required) await access(new URL(path, root));
|
||||||
|
|
||||||
|
async function walk(path) {
|
||||||
|
const entries = await readdir(path, { withFileTypes: true });
|
||||||
|
const found = [];
|
||||||
|
for (const entry of entries) {
|
||||||
|
const child = join(path, entry.name);
|
||||||
|
if (entry.isDirectory()) found.push(...await walk(child));
|
||||||
|
else if (/\.(?:html|xml|css|js|svg)$/.test(entry.name)) found.push(child);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = await walk(root.pathname);
|
||||||
|
const forbidden = /(?:git\.n\.xiaomi\.com|coro\.doc|\.codex\/sessions|<codex_internal_context|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----)/;
|
||||||
|
for (const file of files) {
|
||||||
|
const text = await readFile(file, "utf8");
|
||||||
|
if (forbidden.test(text)) throw new Error(`private material found in ${file}`);
|
||||||
|
}
|
||||||
|
const index = await readFile(new URL("index.html", root), "utf8");
|
||||||
|
if (!index.includes("K1412-RESEARCH-BLOG-20260810")) throw new Error("release marker missing");
|
||||||
|
console.log(`verified: ${files.length} rendered text assets and release marker`);
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
interface Item {
|
||||||
|
id: string;
|
||||||
|
data: {
|
||||||
|
title: string;
|
||||||
|
summary: string;
|
||||||
|
topic: string;
|
||||||
|
date: string;
|
||||||
|
canonicalUrl: string;
|
||||||
|
preview?: string;
|
||||||
|
sourceRepo?: string;
|
||||||
|
kind: "article" | "site";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props { items: Item[] }
|
||||||
|
const { items } = Astro.props;
|
||||||
|
const topicLabels: Record<string, string> = {
|
||||||
|
"agent-systems": "Agent 系统",
|
||||||
|
"llm-systems": "LLM 系统",
|
||||||
|
"ai-research": "AI 研究",
|
||||||
|
"research-method": "研究方法"
|
||||||
|
};
|
||||||
|
const topics = [...new Set(items.map((item) => item.data.topic))];
|
||||||
|
const linkFor = (item: Item) => item.data.kind === "article" ? `/articles/${item.id}/` : item.data.canonicalUrl;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class="research-shell" id="research">
|
||||||
|
<aside class="research-rail">
|
||||||
|
<p class="eyebrow">INDEX / 2026</p>
|
||||||
|
<h2>研究索引</h2>
|
||||||
|
<p>文章、实验报告与独立交互专题按同一条研究脉络组织。</p>
|
||||||
|
<div class="topic-filters" role="group" aria-label="按主题筛选">
|
||||||
|
<button class="topic-filter is-active" data-topic="all" type="button">全部 <span>{items.length}</span></button>
|
||||||
|
{topics.map((topic) => (
|
||||||
|
<button class="topic-filter" data-topic={topic} type="button">
|
||||||
|
{topicLabels[topic] ?? topic}
|
||||||
|
<span>{items.filter((item) => item.data.topic === topic).length}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="research-list" aria-live="polite">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<article class="research-entry" data-entry data-topic={item.data.topic}>
|
||||||
|
<span class="entry-number">{String(index + 1).padStart(2, "0")}</span>
|
||||||
|
<div class="entry-copy">
|
||||||
|
<div class="entry-meta">
|
||||||
|
<span>{topicLabels[item.data.topic] ?? item.data.topic}</span>
|
||||||
|
<span>{item.data.date}</span>
|
||||||
|
<span>{item.data.kind === "site" ? "独立专题" : "文章"}</span>
|
||||||
|
</div>
|
||||||
|
<h3><a href={linkFor(item)}>{item.data.title}</a></h3>
|
||||||
|
<p>{item.data.summary}</p>
|
||||||
|
<div class="entry-links">
|
||||||
|
<a class="text-link" href={linkFor(item)}>打开研究 <span aria-hidden="true">↗</span></a>
|
||||||
|
{item.data.sourceRepo && <a class="muted-link" href={item.data.sourceRepo}>源码</a>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{item.data.preview ? (
|
||||||
|
<a class="entry-preview" href={linkFor(item)} aria-label={`打开 ${item.data.title}`}>
|
||||||
|
<img src={item.data.preview} alt="" loading="lazy" width="640" height="360" />
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a class={`entry-abstract topic-${item.data.topic}`} href={linkFor(item)} aria-hidden="true" tabindex="-1">
|
||||||
|
<span></span><span></span><span></span>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
<p class="empty-state" hidden data-empty>这个主题暂时没有公开内容。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const buttons = Array.from(document.querySelectorAll<HTMLButtonElement>("[data-topic].topic-filter"));
|
||||||
|
const entries = Array.from(document.querySelectorAll<HTMLElement>("[data-entry]"));
|
||||||
|
const empty = document.querySelector<HTMLElement>("[data-empty]");
|
||||||
|
for (const button of buttons) {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const topic = button.dataset.topic ?? "all";
|
||||||
|
for (const candidate of buttons) candidate.classList.toggle("is-active", candidate === button);
|
||||||
|
let visible = 0;
|
||||||
|
for (const entry of entries) {
|
||||||
|
const show = topic === "all" || entry.dataset.topic === topic;
|
||||||
|
entry.hidden = !show;
|
||||||
|
if (show) visible += 1;
|
||||||
|
}
|
||||||
|
if (empty) empty.hidden = visible !== 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<div class="orbit" aria-label="研究从问题、证据、实验到公开成果的闭环示意图">
|
||||||
|
<svg viewBox="0 0 720 520" role="img" aria-labelledby="orbit-title orbit-desc">
|
||||||
|
<title id="orbit-title">研究闭环</title>
|
||||||
|
<desc id="orbit-desc">问题地图连接来源核验、机制解释、实验比较和公开成果,验证结果再回到问题地图。</desc>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="arc" x1="0" x2="1">
|
||||||
|
<stop offset="0" stop-color="#b2482d" />
|
||||||
|
<stop offset="1" stop-color="#183c49" />
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
|
||||||
|
<feGaussianBlur stdDeviation="8" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<circle class="orbit-glow" cx="360" cy="258" r="150" />
|
||||||
|
<ellipse class="orbit-path path-one" cx="360" cy="258" rx="272" ry="160" />
|
||||||
|
<ellipse class="orbit-path path-two" cx="360" cy="258" rx="168" ry="264" transform="rotate(58 360 258)" />
|
||||||
|
<path class="feedback" d="M150 360 C155 458 315 486 398 430" />
|
||||||
|
<path class="feedback-arrow" d="m392 418 9 13-15 4" />
|
||||||
|
|
||||||
|
<g class="orbit-core">
|
||||||
|
<circle cx="360" cy="258" r="92" />
|
||||||
|
<text x="360" y="247" text-anchor="middle">可验证的</text>
|
||||||
|
<text x="360" y="278" text-anchor="middle">公开研究</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g class="orbit-node node-a" transform="translate(104 124)">
|
||||||
|
<circle r="7" />
|
||||||
|
<text x="17" y="6">问题地图</text>
|
||||||
|
</g>
|
||||||
|
<g class="orbit-node node-b" transform="translate(564 117)">
|
||||||
|
<circle r="7" />
|
||||||
|
<text x="-17" y="6" text-anchor="end">来源核验</text>
|
||||||
|
</g>
|
||||||
|
<g class="orbit-node node-c" transform="translate(628 327)">
|
||||||
|
<circle r="7" />
|
||||||
|
<text x="-17" y="6" text-anchor="end">机制解释</text>
|
||||||
|
</g>
|
||||||
|
<g class="orbit-node node-d" transform="translate(400 474)">
|
||||||
|
<circle r="7" />
|
||||||
|
<text x="17" y="6">实验比较</text>
|
||||||
|
</g>
|
||||||
|
<g class="orbit-node node-e" transform="translate(122 373)">
|
||||||
|
<circle r="7" />
|
||||||
|
<text x="17" y="6">边界与反例</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { defineCollection } from "astro:content";
|
||||||
|
import { glob } from "astro/loaders";
|
||||||
|
import { z } from "astro/zod";
|
||||||
|
|
||||||
|
const common = z.object({
|
||||||
|
title: z.string(),
|
||||||
|
summary: z.string(),
|
||||||
|
date: z.string(),
|
||||||
|
updated: z.string(),
|
||||||
|
topic: z.string(),
|
||||||
|
tags: z.array(z.string()).default([]),
|
||||||
|
status: z.enum(["draft", "published", "archived"]),
|
||||||
|
visibility: z.literal("public"),
|
||||||
|
canonicalUrl: z.url(),
|
||||||
|
sourceRepo: z.url().optional(),
|
||||||
|
preview: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
const posts = defineCollection({
|
||||||
|
loader: glob({ pattern: "**/*.md", base: "./content/posts" }),
|
||||||
|
schema: common.extend({ kind: z.literal("article") })
|
||||||
|
});
|
||||||
|
|
||||||
|
const sites = defineCollection({
|
||||||
|
loader: glob({ pattern: "**/*.md", base: "./content/sites" }),
|
||||||
|
schema: common.extend({ kind: z.literal("site") })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { posts, sites };
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
import "../styles/global.css";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
canonical?: string;
|
||||||
|
article?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title = "K1412 Research",
|
||||||
|
description = "技术研究、实验记录与可验证的公开成果。",
|
||||||
|
canonical = Astro.url.href,
|
||||||
|
article = false
|
||||||
|
} = Astro.props;
|
||||||
|
const pageTitle = title === "K1412 Research" ? title : `${title} · K1412 Research`;
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<meta name="theme-color" content="#f0ede5" />
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
<link rel="canonical" href={canonical} />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<link rel="alternate" type="application/rss+xml" title="K1412 Research" href="/rss.xml" />
|
||||||
|
<meta property="og:type" content={article ? "article" : "website"} />
|
||||||
|
<meta property="og:title" content={pageTitle} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:url" content={canonical} />
|
||||||
|
<meta property="og:image" content="https://blog.k1412.top/og-default.svg" />
|
||||||
|
<title>{pageTitle}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="site-grain" aria-hidden="true"></div>
|
||||||
|
<header class="site-header">
|
||||||
|
<a class="brand" href="/" aria-label="K1412 Research 首页">
|
||||||
|
<span class="brand-mark" aria-hidden="true">K</span>
|
||||||
|
<span>K1412 <b>Research</b></span>
|
||||||
|
</a>
|
||||||
|
<nav aria-label="主导航">
|
||||||
|
<a href="/#research">研究</a>
|
||||||
|
<a href="/archive/">索引</a>
|
||||||
|
<a href="/method/">方法</a>
|
||||||
|
<a href="https://git.k1412.top/wuyang/research-blog">源码</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
<footer class="site-footer">
|
||||||
|
<p>K1412 Research</p>
|
||||||
|
<p>公开研究、可复现实验与独立专题站的统一入口。</p>
|
||||||
|
<p><a href="https://git.k1412.top/wuyang/research-blog">Source on git.k1412.top</a></p>
|
||||||
|
</footer>
|
||||||
|
<div class="release-marker" data-release="K1412-RESEARCH-BLOG-20260810" hidden></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
import { getCollection } from "astro:content";
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
|
||||||
|
const posts = await getCollection("posts");
|
||||||
|
const sites = await getCollection("sites");
|
||||||
|
const rows = [...posts, ...sites].sort((a, b) => b.data.date.localeCompare(a.data.date));
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title="公开研究索引" description="K1412 Research 的文章、实验报告与独立专题站索引。">
|
||||||
|
<section class="archive-page">
|
||||||
|
<header class="page-intro">
|
||||||
|
<p class="eyebrow">ARCHIVE</p>
|
||||||
|
<h1>公开研究索引</h1>
|
||||||
|
<p>按发布日期记录文章与独立专题站。草稿保留在源码中,不进入公开索引。</p>
|
||||||
|
</header>
|
||||||
|
<div class="archive-table">
|
||||||
|
{rows.filter((row) => row.data.status === "published").map((row) => (
|
||||||
|
<a class="archive-row" href={row.data.canonicalUrl}>
|
||||||
|
<time>{row.data.date}</time>
|
||||||
|
<strong>{row.data.title}</strong>
|
||||||
|
<span>{row.data.kind === "site" ? "独立专题" : "文章"}</span>
|
||||||
|
<i aria-hidden="true">↗</i>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</BaseLayout>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
import { getCollection, render, type CollectionEntry } from "astro:content";
|
||||||
|
import BaseLayout from "../../layouts/BaseLayout.astro";
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getCollection("posts", ({ data }) => data.status === "published");
|
||||||
|
return posts.map((post) => ({ params: { id: post.id }, props: { post } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props { post: CollectionEntry<"posts"> }
|
||||||
|
const { post } = Astro.props;
|
||||||
|
const { Content, headings } = await render(post);
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title={post.data.title} description={post.data.summary} canonical={post.data.canonicalUrl} article>
|
||||||
|
<article class="article-page">
|
||||||
|
<header class="article-header">
|
||||||
|
<a class="back-link" href="/">← 返回研究索引</a>
|
||||||
|
<div class="article-kicker"><span>{post.data.topic}</span><span>{post.data.date}</span><span>更新 {post.data.updated}</span></div>
|
||||||
|
<h1>{post.data.title}</h1>
|
||||||
|
<p>{post.data.summary}</p>
|
||||||
|
<div class="tag-row">{post.data.tags.map((tag) => <span>{tag}</span>)}</div>
|
||||||
|
</header>
|
||||||
|
<div class="article-grid">
|
||||||
|
<aside class="article-outline" aria-label="文章目录">
|
||||||
|
<p>本文目录</p>
|
||||||
|
<ol>
|
||||||
|
{headings.filter((heading) => heading.depth === 2).map((heading) => (
|
||||||
|
<li><a href={`#${heading.slug}`}>{heading.text}</a></li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</aside>
|
||||||
|
<div class="prose"><Content /></div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</BaseLayout>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
import { getCollection } from "astro:content";
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import ResearchOrbit from "../components/ResearchOrbit.astro";
|
||||||
|
import ResearchIndex from "../components/ResearchIndex.astro";
|
||||||
|
|
||||||
|
const posts = await getCollection("posts", ({ data }) => data.status === "published");
|
||||||
|
const sites = await getCollection("sites", ({ data }) => data.status === "published");
|
||||||
|
const items = [...posts, ...sites].sort((a, b) => b.data.date.localeCompare(a.data.date));
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout>
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-copy">
|
||||||
|
<p class="eyebrow">K1412 / OPEN RESEARCH ARCHIVE</p>
|
||||||
|
<h1>技术研究,<br /><em>从证据走向理解。</em></h1>
|
||||||
|
<p class="hero-intro">这里收录经过来源核验、机制梳理与实验检验的技术文章,也保留需要独立交互形态的专题研究站。</p>
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a class="primary-action" href="#research">浏览研究</a>
|
||||||
|
<a class="secondary-action" href="/method/">研究方法</a>
|
||||||
|
</div>
|
||||||
|
<dl class="hero-stats">
|
||||||
|
<div><dt>{items.length}</dt><dd>公开成果</dd></div>
|
||||||
|
<div><dt>{new Set(items.map((item) => item.data.topic)).size}</dt><dd>研究主题</dd></div>
|
||||||
|
<div><dt>2026</dt><dd>当前快照</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<ResearchOrbit />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="thesis-band" aria-label="研究原则">
|
||||||
|
<p>问题地图</p><span>→</span><p>一手来源</p><span>→</span><p>机制解释</p><span>→</span><p>边界实验</p><span>→</span><p>公开成果</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<ResearchIndex items={items} />
|
||||||
|
|
||||||
|
<section class="method-callout">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">METHOD / EVIDENCE</p>
|
||||||
|
<h2>文章不是搜索结果的重排。</h2>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>每项研究先建立问题与证据台账,再区分来源声称、源码观察、运行证据、实验结果和推断。能实验的问题先做小样本验证;不能验证的部分保留边界和不确定性。</p>
|
||||||
|
<a class="text-link" href="/articles/research-publishing-method/">阅读全文 <span aria-hidden="true">→</span></a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</BaseLayout>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title="研究与发布方法" description="K1412 Research 的证据、实验、编辑、可视化与公开发布方法。">
|
||||||
|
<article class="method-page">
|
||||||
|
<header class="page-intro">
|
||||||
|
<p class="eyebrow">METHOD</p>
|
||||||
|
<h1>研究与发布方法</h1>
|
||||||
|
<p>公开内容从问题地图开始,经来源核验、机制解释、边界实验和编辑审查后发布。</p>
|
||||||
|
</header>
|
||||||
|
<div class="method-steps">
|
||||||
|
<section><span>01</span><h2>问题地图</h2><p>先定义要支持的理解或决策,再划分主题、边界、时间快照和成功证据。</p></section>
|
||||||
|
<section><span>02</span><h2>证据台账</h2><p>为关键判断记录来源、版本、支持范围、限制和公开状态。搜索结果不直接成为文章结构。</p></section>
|
||||||
|
<section><span>03</span><h2>机制与比较</h2><p>先用普通语言建立因果模型,再在统一任务、版本、分母和指标下比较方案。</p></section>
|
||||||
|
<section><span>04</span><h2>边界实验</h2><p>能实验的问题先做小规模代表验证,保存环境、命令、原始结果、失败样本和归因。</p></section>
|
||||||
|
<section><span>05</span><h2>编辑与视觉</h2><p>正文完整但易读;图分别承担拓扑、顺序、状态、比较、数量或原理直觉中的一种任务。</p></section>
|
||||||
|
<section><span>06</span><h2>公开发布</h2><p>内容审查、构建、桌面与移动验收、公开源码、不可变镜像、HTTPS 和内容标记共同组成发布证据。</p></section>
|
||||||
|
</div>
|
||||||
|
<a class="primary-action" href="/articles/research-publishing-method/">阅读完整方法文章</a>
|
||||||
|
</article>
|
||||||
|
</BaseLayout>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { getCollection } from "astro:content";
|
||||||
|
|
||||||
|
function escapeXml(value: string) {
|
||||||
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const posts = await getCollection("posts", ({ data }) => data.status === "published");
|
||||||
|
const items = posts
|
||||||
|
.sort((a, b) => b.data.date.localeCompare(a.data.date))
|
||||||
|
.map((post) => `<item><title>${escapeXml(post.data.title)}</title><link>${escapeXml(post.data.canonicalUrl)}</link><guid>${escapeXml(post.data.canonicalUrl)}</guid><pubDate>${new Date(`${post.data.date}T00:00:00+08:00`).toUTCString()}</pubDate><description>${escapeXml(post.data.summary)}</description></item>`)
|
||||||
|
.join("");
|
||||||
|
const xml = `<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>K1412 Research</title><link>https://blog.k1412.top</link><description>技术研究、实验记录与可验证的公开成果。</description>${items}</channel></rss>`;
|
||||||
|
return new Response(xml, { headers: { "Content-Type": "application/rss+xml; charset=utf-8" } });
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { getCollection } from "astro:content";
|
||||||
|
|
||||||
|
const localPages = ["/", "/archive/", "/method/"];
|
||||||
|
|
||||||
|
function escapeXml(value: string) {
|
||||||
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
const posts = await getCollection("posts", ({ data }) => data.status === "published");
|
||||||
|
const urls = [
|
||||||
|
...localPages.map((path) => ({ loc: new URL(path, "https://blog.k1412.top").href, lastmod: "2026-08-10" })),
|
||||||
|
...posts.map((post) => ({ loc: post.data.canonicalUrl, lastmod: post.data.updated }))
|
||||||
|
];
|
||||||
|
const entries = urls.map(({ loc, lastmod }) => `<url><loc>${escapeXml(loc)}</loc><lastmod>${escapeXml(lastmod)}</lastmod></url>`).join("");
|
||||||
|
const xml = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${entries}</urlset>`;
|
||||||
|
return new Response(xml, { headers: { "Content-Type": "application/xml; charset=utf-8" } });
|
||||||
|
}
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--paper: #f0ede5;
|
||||||
|
--paper-deep: #e4dfd2;
|
||||||
|
--ink: #172429;
|
||||||
|
--muted: #627077;
|
||||||
|
--line: rgba(23, 36, 41, 0.18);
|
||||||
|
--rust: #b2482d;
|
||||||
|
--teal: #183c49;
|
||||||
|
--acid: #d3e2a4;
|
||||||
|
--max: 1440px;
|
||||||
|
--serif: Iowan Old Style, Songti SC, STSong, Noto Serif CJK SC, Georgia, serif;
|
||||||
|
--sans: Inter, SF Pro Display, PingFang SC, Microsoft YaHei, system-ui, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
html { scroll-behavior: smooth; background: var(--paper); }
|
||||||
|
body { margin: 0; color: var(--ink); background: var(--paper); font-family: var(--sans); line-height: 1.65; }
|
||||||
|
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
|
||||||
|
img, svg { display: block; max-width: 100%; }
|
||||||
|
button, input { font: inherit; }
|
||||||
|
::selection { background: var(--acid); color: var(--ink); }
|
||||||
|
|
||||||
|
.site-grain { position: fixed; inset: 0; pointer-events: none; opacity: .18; z-index: 10; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.18'/%3E%3C/svg%3E"); mix-blend-mode: multiply; }
|
||||||
|
.site-header { width: min(calc(100% - 48px), var(--max)); margin: 0 auto; min-height: 88px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--line); }
|
||||||
|
.brand { display: inline-flex; align-items: center; gap: 12px; font-size: 13px; letter-spacing: .12em; text-decoration: none; text-transform: uppercase; }
|
||||||
|
.brand b { color: var(--rust); font-weight: 600; }
|
||||||
|
.brand-mark { display: grid; place-items: center; width: 32px; height: 32px; border: 1px solid var(--ink); border-radius: 50%; font-family: var(--serif); font-size: 19px; font-style: italic; }
|
||||||
|
.site-header nav { display: flex; gap: 28px; }
|
||||||
|
.site-header nav a { color: var(--muted); font-size: 13px; text-decoration: none; }
|
||||||
|
.site-header nav a:hover, .site-header nav a:focus-visible { color: var(--rust); }
|
||||||
|
.eyebrow { margin: 0 0 20px; color: var(--rust); font-size: 11px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; }
|
||||||
|
|
||||||
|
.hero { width: min(calc(100% - 48px), var(--max)); min-height: 720px; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) minmax(420px, .9fr); align-items: center; gap: 36px; }
|
||||||
|
.hero-copy { padding: 76px 0 64px; }
|
||||||
|
.hero h1 { max-width: 780px; margin: 0; font-family: var(--serif); font-size: clamp(62px, 7vw, 112px); font-weight: 400; line-height: .94; letter-spacing: -.055em; }
|
||||||
|
.hero h1 em { color: var(--rust); font-weight: 400; }
|
||||||
|
.hero h1 em { white-space: nowrap; }
|
||||||
|
.hero-intro { max-width: 610px; margin: 42px 0 0; color: var(--muted); font-size: 18px; }
|
||||||
|
.hero-actions { display: flex; gap: 14px; margin-top: 40px; }
|
||||||
|
.primary-action, .secondary-action { display: inline-flex; min-height: 48px; align-items: center; justify-content: center; padding: 0 24px; border: 1px solid var(--ink); text-decoration: none; font-size: 13px; font-weight: 700; letter-spacing: .08em; }
|
||||||
|
.primary-action { color: var(--paper); background: var(--ink); }
|
||||||
|
.primary-action:hover { background: var(--rust); border-color: var(--rust); }
|
||||||
|
.secondary-action:hover { color: var(--paper); background: var(--teal); border-color: var(--teal); }
|
||||||
|
.hero-stats { display: flex; gap: 42px; margin: 66px 0 0; }
|
||||||
|
.hero-stats div { min-width: 92px; }
|
||||||
|
.hero-stats dt { font-family: var(--serif); font-size: 34px; line-height: 1; }
|
||||||
|
.hero-stats dd { margin: 8px 0 0; color: var(--muted); font-size: 12px; }
|
||||||
|
|
||||||
|
.orbit { position: relative; min-height: 580px; display: grid; place-items: center; }
|
||||||
|
.orbit svg { width: min(100%, 680px); overflow: visible; }
|
||||||
|
.orbit-glow { fill: rgba(178, 72, 45, .12); filter: url(#glow); }
|
||||||
|
.orbit-path { fill: none; stroke: url(#arc); stroke-width: 1.2; stroke-dasharray: 3 8; opacity: .65; }
|
||||||
|
.path-two { stroke-dasharray: 1 9; }
|
||||||
|
.feedback, .feedback-arrow { fill: none; stroke: var(--rust); stroke-width: 1.6; }
|
||||||
|
.orbit-core circle { fill: var(--ink); }
|
||||||
|
.orbit-core text { fill: var(--paper); font-family: var(--serif); font-size: 22px; }
|
||||||
|
.orbit-node circle { fill: var(--rust); }
|
||||||
|
.orbit-node text { fill: var(--ink); font-size: 15px; letter-spacing: .04em; }
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.path-one { animation: dash 28s linear infinite; }
|
||||||
|
.path-two { animation: dash 34s linear infinite reverse; }
|
||||||
|
.orbit-core { transform-origin: 360px 258px; animation: pulse 7s ease-in-out infinite; }
|
||||||
|
}
|
||||||
|
@keyframes dash { to { stroke-dashoffset: -220; } }
|
||||||
|
@keyframes pulse { 50% { transform: scale(1.035); } }
|
||||||
|
|
||||||
|
.thesis-band { min-height: 100px; display: flex; align-items: center; justify-content: center; gap: clamp(12px, 3vw, 42px); padding: 24px; color: var(--paper); background: var(--ink); overflow: hidden; }
|
||||||
|
.thesis-band p { margin: 0; white-space: nowrap; font-family: var(--serif); font-size: clamp(15px, 1.5vw, 21px); }
|
||||||
|
.thesis-band span { color: var(--rust); }
|
||||||
|
|
||||||
|
.research-shell { width: min(calc(100% - 48px), var(--max)); margin: 0 auto; padding: 120px 0 140px; display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: clamp(40px, 7vw, 112px); }
|
||||||
|
.research-rail, .research-list { min-width: 0; }
|
||||||
|
.research-rail { position: sticky; top: 34px; align-self: start; }
|
||||||
|
.research-rail h2 { margin: 0; font-family: var(--serif); font-size: 46px; font-weight: 400; }
|
||||||
|
.research-rail > p:not(.eyebrow) { color: var(--muted); font-size: 14px; }
|
||||||
|
.topic-filters { margin-top: 36px; border-top: 1px solid var(--line); }
|
||||||
|
.topic-filter { width: 100%; display: flex; justify-content: space-between; gap: 16px; padding: 13px 0; border: 0; border-bottom: 1px solid var(--line); color: var(--muted); background: transparent; text-align: left; cursor: pointer; }
|
||||||
|
.topic-filter span { font-family: var(--serif); }
|
||||||
|
.topic-filter:hover, .topic-filter.is-active { color: var(--rust); }
|
||||||
|
.research-entry { display: grid; grid-template-columns: 44px minmax(0, 1fr) minmax(220px, 34%); gap: 24px; align-items: center; min-height: 270px; padding: 32px 0; border-top: 1px solid var(--line); }
|
||||||
|
.research-entry:last-of-type { border-bottom: 1px solid var(--line); }
|
||||||
|
.entry-number { align-self: start; color: var(--rust); font-family: var(--serif); font-size: 18px; }
|
||||||
|
.entry-meta { display: flex; flex-wrap: wrap; gap: 12px; color: var(--muted); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; }
|
||||||
|
.entry-meta span:not(:last-child)::after { content: "/"; padding-left: 12px; color: var(--line); }
|
||||||
|
.entry-copy h3 { max-width: 720px; margin: 15px 0 10px; font-family: var(--serif); font-size: clamp(28px, 3vw, 44px); font-weight: 400; line-height: 1.08; letter-spacing: -.025em; }
|
||||||
|
.entry-copy h3 a { text-decoration: none; }
|
||||||
|
.entry-copy h3 a:hover { color: var(--rust); }
|
||||||
|
.entry-copy > p { max-width: 690px; margin: 0; color: var(--muted); font-size: 14px; }
|
||||||
|
.entry-links { display: flex; align-items: center; gap: 22px; margin-top: 22px; }
|
||||||
|
.text-link, .muted-link { font-size: 12px; font-weight: 700; letter-spacing: .06em; }
|
||||||
|
.text-link { color: var(--rust); }
|
||||||
|
.muted-link { color: var(--muted); }
|
||||||
|
.entry-preview, .entry-abstract { min-height: 180px; border: 1px solid var(--line); overflow: hidden; text-decoration: none; }
|
||||||
|
.entry-preview img { width: 100%; height: 100%; min-height: 180px; object-fit: cover; filter: saturate(.78) contrast(.96); transition: transform .5s ease, filter .5s ease; }
|
||||||
|
.entry-preview:hover img { transform: scale(1.025); filter: saturate(1); }
|
||||||
|
.entry-abstract { position: relative; display: block; background: var(--paper-deep); }
|
||||||
|
.entry-abstract span { position: absolute; display: block; border: 1px solid currentColor; border-radius: 50%; opacity: .7; }
|
||||||
|
.entry-abstract span:nth-child(1) { width: 160px; height: 160px; left: 15%; top: 12%; }
|
||||||
|
.entry-abstract span:nth-child(2) { width: 100px; height: 100px; right: 12%; bottom: 8%; }
|
||||||
|
.entry-abstract span:nth-child(3) { width: 1px; height: 130%; left: 56%; top: -15%; background: currentColor; transform: rotate(28deg); }
|
||||||
|
.topic-agent-systems { color: var(--rust); background: #dfd6c7; }
|
||||||
|
.topic-llm-systems { color: var(--teal); background: #cbd6d1; }
|
||||||
|
.topic-ai-research { color: #6d6548; background: #dad5bd; }
|
||||||
|
.topic-research-method { color: #4e5962; background: #d8d9d5; }
|
||||||
|
.empty-state { color: var(--muted); }
|
||||||
|
|
||||||
|
.method-callout { width: min(calc(100% - 48px), var(--max)); margin: 0 auto 130px; padding: 70px; display: grid; grid-template-columns: 1fr 1fr; gap: 80px; color: var(--paper); background: var(--teal); }
|
||||||
|
.method-callout .eyebrow { color: var(--acid); }
|
||||||
|
.method-callout h2 { max-width: 560px; margin: 0; font-family: var(--serif); font-size: clamp(40px, 5vw, 72px); font-weight: 400; line-height: 1; }
|
||||||
|
.method-callout > div:last-child { align-self: end; }
|
||||||
|
.method-callout > div:last-child p { max-width: 560px; color: rgba(240, 237, 229, .76); }
|
||||||
|
.method-callout .text-link { color: var(--acid); }
|
||||||
|
|
||||||
|
.site-footer { width: min(calc(100% - 48px), var(--max)); min-height: 150px; margin: 0 auto; display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 24px; align-items: center; border-top: 1px solid var(--line); color: var(--muted); font-size: 12px; }
|
||||||
|
.site-footer p:last-child { text-align: right; }
|
||||||
|
|
||||||
|
.page-intro { max-width: 900px; padding: 110px 0 72px; }
|
||||||
|
.page-intro h1 { margin: 0; font-family: var(--serif); font-size: clamp(56px, 7vw, 100px); font-weight: 400; line-height: .95; }
|
||||||
|
.page-intro > p:last-child { max-width: 700px; margin-top: 30px; color: var(--muted); font-size: 18px; }
|
||||||
|
.archive-page, .method-page { width: min(calc(100% - 48px), 1180px); margin: 0 auto 130px; }
|
||||||
|
.archive-table { border-top: 1px solid var(--line); }
|
||||||
|
.archive-row { display: grid; grid-template-columns: 130px 1fr 100px 24px; gap: 24px; align-items: center; min-height: 92px; border-bottom: 1px solid var(--line); text-decoration: none; }
|
||||||
|
.archive-row time, .archive-row span { color: var(--muted); font-size: 12px; }
|
||||||
|
.archive-row strong { font-family: var(--serif); font-size: 22px; font-weight: 400; }
|
||||||
|
.archive-row:hover strong { color: var(--rust); }
|
||||||
|
.method-steps { display: grid; grid-template-columns: repeat(2, 1fr); border-top: 1px solid var(--line); border-left: 1px solid var(--line); margin-bottom: 50px; }
|
||||||
|
.method-steps section { min-height: 280px; padding: 40px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
|
||||||
|
.method-steps section > span { color: var(--rust); font-family: var(--serif); }
|
||||||
|
.method-steps h2 { margin: 48px 0 12px; font-family: var(--serif); font-size: 32px; font-weight: 400; }
|
||||||
|
.method-steps p { color: var(--muted); }
|
||||||
|
|
||||||
|
.article-page { width: min(calc(100% - 48px), 1320px); margin: 0 auto; }
|
||||||
|
.article-header { max-width: 1060px; padding: 80px 0 90px; }
|
||||||
|
.back-link { color: var(--muted); font-size: 12px; }
|
||||||
|
.article-kicker { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 60px; color: var(--rust); font-size: 10px; font-weight: 700; letter-spacing: .15em; text-transform: uppercase; }
|
||||||
|
.article-header h1 { margin: 26px 0 24px; font-family: var(--serif); font-size: clamp(54px, 7vw, 104px); font-weight: 400; line-height: .96; letter-spacing: -.045em; }
|
||||||
|
.article-header > p { max-width: 800px; color: var(--muted); font-size: 20px; }
|
||||||
|
.tag-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 30px; }
|
||||||
|
.tag-row span { padding: 6px 10px; border: 1px solid var(--line); font-size: 10px; }
|
||||||
|
.article-grid { display: grid; grid-template-columns: 220px minmax(0, 820px); gap: clamp(50px, 8vw, 110px); justify-content: center; padding: 78px 0 140px; border-top: 1px solid var(--line); }
|
||||||
|
.article-outline { position: sticky; top: 34px; align-self: start; }
|
||||||
|
.article-outline > p { color: var(--rust); font-size: 11px; font-weight: 700; letter-spacing: .12em; }
|
||||||
|
.article-outline ol { margin: 20px 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
|
||||||
|
.article-outline li { border-bottom: 1px solid var(--line); }
|
||||||
|
.article-outline a { display: block; padding: 12px 0; color: var(--muted); font-size: 12px; text-decoration: none; }
|
||||||
|
.article-outline a:hover { color: var(--rust); }
|
||||||
|
.prose { min-width: 0; font-family: var(--serif); font-size: 18px; line-height: 1.9; }
|
||||||
|
.prose h1 { display: none; }
|
||||||
|
.prose h2 { margin: 3.2em 0 .9em; font-size: 40px; font-weight: 400; line-height: 1.2; letter-spacing: -.025em; }
|
||||||
|
.prose h2:first-child { margin-top: 0; }
|
||||||
|
.prose h3 { margin-top: 2.3em; font-size: 25px; font-weight: 500; }
|
||||||
|
.prose p, .prose li { color: #2f3c41; }
|
||||||
|
.prose strong { color: var(--ink); }
|
||||||
|
.prose blockquote { margin: 2.2em 0; padding: 20px 26px; border-left: 3px solid var(--rust); background: rgba(178, 72, 45, .06); }
|
||||||
|
.prose pre { overflow-x: auto; padding: 22px; border-radius: 2px; font-size: 13px; line-height: 1.6; }
|
||||||
|
.prose code:not(pre code) { padding: .12em .35em; background: var(--paper-deep); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em; }
|
||||||
|
.prose table { width: 100%; margin: 2.2em 0; border-collapse: collapse; font-family: var(--sans); font-size: 13px; line-height: 1.55; }
|
||||||
|
.prose th, .prose td { padding: 12px 10px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
|
||||||
|
.prose th { color: var(--rust); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }
|
||||||
|
.prose img, .prose svg { width: min(1100px, calc(100vw - 48px)); max-width: none; margin: 2.6em 0 2.6em 50%; transform: translateX(-50%); }
|
||||||
|
|
||||||
|
@media (max-width: 980px) {
|
||||||
|
.hero { grid-template-columns: 1fr; min-height: auto; }
|
||||||
|
.hero-copy { padding-bottom: 0; }
|
||||||
|
.orbit { min-height: 460px; }
|
||||||
|
.research-shell { grid-template-columns: minmax(0, 1fr); }
|
||||||
|
.research-rail { position: static; }
|
||||||
|
.topic-filters { display: flex; gap: 8px; overflow-x: auto; border: 0; padding-bottom: 8px; }
|
||||||
|
.topic-filter { width: auto; flex: 0 0 auto; gap: 10px; padding: 9px 12px; border: 1px solid var(--line); }
|
||||||
|
.research-entry { grid-template-columns: 36px minmax(0, 1fr); }
|
||||||
|
.entry-preview, .entry-abstract { grid-column: 2; }
|
||||||
|
.method-callout { grid-template-columns: 1fr; gap: 34px; padding: 50px; }
|
||||||
|
.article-grid { grid-template-columns: 1fr; }
|
||||||
|
.article-outline { position: static; }
|
||||||
|
.article-outline ol { display: grid; grid-template-columns: repeat(2, 1fr); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 981px) and (max-width: 1180px) {
|
||||||
|
.hero h1 { font-size: clamp(60px, 6.3vw, 74px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.site-header { width: min(calc(100% - 28px), var(--max)); min-height: 72px; }
|
||||||
|
.site-header nav { gap: 14px; }
|
||||||
|
.site-header nav a:nth-child(2), .site-header nav a:nth-child(4) { display: none; }
|
||||||
|
.brand { letter-spacing: .05em; }
|
||||||
|
.hero, .research-shell, .method-callout, .site-footer, .article-page, .archive-page, .method-page { width: min(calc(100% - 28px), var(--max)); }
|
||||||
|
.hero-copy { padding-top: 62px; }
|
||||||
|
.hero h1 { font-size: clamp(51px, 16vw, 74px); }
|
||||||
|
.hero h1 em { white-space: normal; }
|
||||||
|
.hero-intro { margin-top: 28px; font-size: 16px; }
|
||||||
|
.hero-actions { flex-direction: column; align-items: stretch; }
|
||||||
|
.hero-stats { gap: 20px; margin-top: 44px; }
|
||||||
|
.hero-stats div { min-width: 0; }
|
||||||
|
.orbit { min-height: 370px; margin: 0 -14px; }
|
||||||
|
.thesis-band { justify-content: flex-start; overflow-x: auto; }
|
||||||
|
.research-shell { padding: 82px 0 96px; gap: 42px; }
|
||||||
|
.research-rail h2 { font-size: 40px; }
|
||||||
|
.research-entry { grid-template-columns: 26px minmax(0, 1fr); min-height: 0; padding: 28px 0; gap: 14px; }
|
||||||
|
.entry-copy h3 { font-size: 30px; }
|
||||||
|
.entry-preview, .entry-abstract { min-height: 150px; }
|
||||||
|
.method-callout { margin-bottom: 90px; padding: 36px 28px; }
|
||||||
|
.site-footer { grid-template-columns: 1fr; gap: 2px; padding: 36px 0; }
|
||||||
|
.site-footer p { margin: 2px 0; }
|
||||||
|
.site-footer p:last-child { text-align: left; }
|
||||||
|
.page-intro { padding: 72px 0 50px; }
|
||||||
|
.page-intro h1 { font-size: 54px; }
|
||||||
|
.archive-row { grid-template-columns: 80px 1fr 20px; gap: 12px; padding: 16px 0; }
|
||||||
|
.archive-row span { display: none; }
|
||||||
|
.archive-row strong { font-size: 18px; line-height: 1.25; }
|
||||||
|
.method-steps { grid-template-columns: 1fr; }
|
||||||
|
.method-steps section { min-height: 230px; padding: 28px; }
|
||||||
|
.article-header { padding: 54px 0 64px; }
|
||||||
|
.article-kicker { margin-top: 40px; }
|
||||||
|
.article-header h1 { font-size: 48px; }
|
||||||
|
.article-header > p { font-size: 17px; }
|
||||||
|
.article-grid { padding-top: 48px; gap: 40px; }
|
||||||
|
.article-outline ol { grid-template-columns: 1fr; }
|
||||||
|
.prose { font-size: 17px; line-height: 1.85; }
|
||||||
|
.prose h2 { font-size: 32px; }
|
||||||
|
.prose table { display: block; overflow-x: auto; white-space: nowrap; }
|
||||||
|
.prose img, .prose svg { width: calc(100vw - 28px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
html { scroll-behavior: auto; }
|
||||||
|
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"include": [".astro/types.d.ts", "**/*"],
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||