Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/HNU-himematsu/HNU-TimeLetter/llms.txt

Use this file to discover all available pages before exploring further.

The /api/creation-board endpoint powers the community co-creation board at /creation. It returns two parallel arrays: ideas, which are the raw community-submitted scene proposals from the Feishu 揭示板 table, and headers, which are the card metadata records from the companion 创作板-头表 table. The frontend groups ideas by cardId into visual CreationCard stacks — each card represents one proposed galgame scene, while its stacked entries represent individual community contributions to that scene.

Endpoint

GET /api/creation-board
Authentication: None — this is a fully public endpoint. Query parameters: None.

Response — 200 OK

Returns a JSON object with two top-level keys: ideas and headers.
{
  "ideas": [
    {
      "id": "rec_abc123",
      "cardId": "card_01",
      "author": "晴天",
      "tags": "场景设计",
      "content": "在图书馆门口设计一个傍晚相遇场景,夕阳从西侧廊柱透进来。",
      "images": [
        "https://oss.himematsu.cn/creation/rec_abc123_ref.png"
      ],
      "createdAt": "2026-04-30T10:00:00.000Z",
      "sortOrder": 1
    },
    {
      "id": "rec_def456",
      "cardId": "card_01",
      "author": "匿名",
      "tags": "台词建议",
      "content": "可以加一句「你也喜欢在这个时间来?」",
      "images": [],
      "createdAt": "2026-04-30T11:30:00.000Z",
      "sortOrder": 2
    }
  ],
  "headers": [
    {
      "cardId": "card_01",
      "location": "图书馆",
      "character": "姬松",
      "sortOrder": 1
    }
  ]
}

Response schema

ideas — CreationIdea[]

ideas
CreationIdea[]
required
Raw community-submitted idea records from the Feishu 揭示板 table, view 收集结果. Multiple records may share the same cardId; that is by design and must not be de-duplicated at sync time.

headers — CardHeaderInfo[]

headers
CardHeaderInfo[]
required
Card metadata records from the Feishu 创作板-头表 table. Each record provides the location and character context for a group of ideas sharing the same cardId.

CreationCard aggregation pattern

The /api/creation-board response returns data in its raw, un-aggregated form. The /creation page frontend — specifically src/components/creation/utils.ts — performs a client-side grouping step before rendering:
  1. Group by cardId: All CreationIdea records sharing the same cardId are collected into a single CreationCard.
  2. Match header: The corresponding CardHeaderInfo entry (same cardId) supplies the card’s location and character labels.
  3. Project to CreationEntry: Each CreationIdea is projected to a CreationEntry by dropping the internal submitter field. All other fields are passed through unchanged.
  4. Render: The resulting CreationCard is rendered as a stacked-card widget in the waterfall layout.
The resulting CreationCard shape used at the page layer is:
interface CreationEntry {
  id: string;
  cardId: string;
  author: string;
  tags: string;
  content: string;
  images: string[];
  createdAt: string;
  sortOrder: number;
}

interface CreationCard {
  id: string;          // same as cardId
  cardId: string;
  addIdeaUrl: string;  // link rendered as the "新增创意" button in the card header
  entries: CreationEntry[];
  location?: string;   // campus location name, from the matching CardHeaderInfo
  character?: string;  // galgame character name, from the matching CardHeaderInfo
}
CreationCard and CreationEntry are page-layer constructs only — they are never written to disk by the sync pipeline. The canonical on-disk format is src/data/creation-board.json (raw CreationIdea[]) and src/data/creation-board-headers.json (raw CardHeaderInfo[]).

TypeScript example

interface CreationIdea {
  id: string;
  cardId: string;
  author: string;
  tags: string;
  content: string;
  images: string[];
  createdAt: string;
  sortOrder: number;
}

interface CardHeaderInfo {
  cardId: string;
  location: string;
  character: string;
  sortOrder: number;
}

interface CreationBoardResponse {
  ideas: CreationIdea[];
  headers: CardHeaderInfo[];
}

async function fetchCreationBoard(): Promise<CreationBoardResponse> {
  const res = await fetch("/api/creation-board");
  if (!res.ok) {
    throw new Error(`Failed to fetch creation board: ${res.status}`);
  }
  return res.json() as Promise<CreationBoardResponse>;
}

// Group ideas by cardId
const { ideas, headers } = await fetchCreationBoard();

const cardMap = new Map<string, CreationIdea[]>();
for (const idea of ideas) {
  const group = cardMap.get(idea.cardId) ?? [];
  group.push(idea);
  cardMap.set(idea.cardId, group);
}

// Sort cards according to header sortOrder
const sortedHeaders = [...headers].sort((a, b) => a.sortOrder - b.sortOrder);
for (const header of sortedHeaders) {
  const entries = cardMap.get(header.cardId) ?? [];
  console.log(
    `${header.character} @ ${header.location}: ${entries.length} idea(s)`
  );
}

Data sources

This endpoint reads from two JSON files written by the Feishu sync pipeline:
FileFeishu source tableContent
src/data/creation-board.json揭示板 → 收集结果 viewRaw CreationIdea[] array
src/data/creation-board-headers.json创作板-头表Raw CardHeaderInfo[] array
Trigger a sync of both tables (creation_board and creation_headers) to refresh both files simultaneously.
Connect Feishu Automation to the webhook endpoint so the creation board updates in real time whenever a new community submission arrives. Configure a 发送 HTTP 请求 action in your Feishu automation flow with the following settings:
POST https://himematsu.cn/api/admin/sync/webhook?tables=creation_board,creation_headers
Authorization: Bearer <SYNC_WEBHOOK_SECRET>
The webhook queues a background sync job and returns 202 Accepted immediately. The /api/creation-board endpoint will serve the refreshed data as soon as the job completes — no build or server restart required.

Build docs developers (and LLMs) love