@open330/kiwimu 0.7.1 โ†’ 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/config.ts CHANGED
@@ -5,6 +5,7 @@ import { join, dirname } from "path";
5
5
  export const CONFIG_FILE = "kiwi.toml";
6
6
  export const DB_FILE = "kiwi.db";
7
7
  export const SITE_DIR = "_site";
8
+ export const SUPPORTED_EXTENSIONS = ['pdf', 'docx', 'pptx', 'doc', 'ppt', 'key', 'rtf', 'md'];
8
9
 
9
10
  export interface LLMConfig {
10
11
  provider: string; // "gemini" | "azure-openai" | "openai" | "anthropic"
@@ -20,13 +21,50 @@ export interface Persona {
20
21
  content_style: string; // injected into content generation prompts
21
22
  }
22
23
 
24
+ export interface EmbeddingConfig {
25
+ provider: string; // "gemini" | "openai" | "azure-openai"
26
+ api_key: string;
27
+ }
28
+
29
+ export interface QAConfig {
30
+ auto_promote: boolean; // If true, automatically save all Q&A answers as wiki pages
31
+ }
32
+
33
+ export interface WikiSchema {
34
+ categories?: string[];
35
+ naming_convention?: 'noun_phrase' | 'question' | 'topic';
36
+ min_page_length?: number;
37
+ max_page_length?: number;
38
+ terms?: Record<string, string>;
39
+ page_template?: { sections?: string[] };
40
+ }
41
+
42
+ /** A user-defined source category for sidebar/index grouping. */
43
+ export interface SourceCategory {
44
+ /** Display label, e.g. "๐Ÿ“‹ PRD". Korean/emoji OK. */
45
+ name: string;
46
+ /** Display order; lower numbers appear first. */
47
+ order: number;
48
+ /**
49
+ * Glob-like patterns matched against the source URI (case-insensitive).
50
+ * Supports `*` as a wildcard. Patterns are tested against the basename
51
+ * (filename) and the full URI; matching either counts.
52
+ * Examples: "F[0-9][0-9]_*", "weekly/*", "STUDY_PLAN*"
53
+ */
54
+ patterns: string[];
55
+ }
56
+
23
57
  export interface KiwiConfig {
24
58
  project: { name: string; created: string };
25
59
  build: { output_dir: string };
26
60
  llm: LLMConfig;
61
+ embedding?: EmbeddingConfig; // separate config for embeddings (optional, falls back to llm)
62
+ qa?: QAConfig; // Q&A feedback loop settings
27
63
  deploy: { target: string };
28
64
  personas?: Persona[];
29
65
  active_persona?: string; // name of the active persona
66
+ schema?: WikiSchema;
67
+ categories?: SourceCategory[]; // optional source categorization for grouped rendering
30
68
  }
31
69
 
32
70
  /** Directory containing built-in persona JSON files (shipped with the package) */
@@ -62,7 +100,7 @@ export function defaultConfig(name: string): KiwiConfig {
62
100
  return {
63
101
  project: { name, created: new Date().toISOString().slice(0, 10) },
64
102
  build: { output_dir: SITE_DIR },
65
- llm: { provider: "gemini", model: "gemini-2.0-flash-lite", api_key: "", endpoint: "" },
103
+ llm: { provider: "gemini", model: "gemini-3.1-flash-lite-preview", api_key: "", endpoint: "" },
66
104
  deploy: { target: "gh-pages" },
67
105
  personas: builtins.length > 0 ? builtins : [getDefaultPersona()],
68
106
  active_persona: builtins[0]?.name || "default",
@@ -79,11 +117,11 @@ export function saveConfig(root: string, config: KiwiConfig): void {
79
117
  }
80
118
 
81
119
  export function loadConfig(root: string): KiwiConfig {
82
- const content = require("fs").readFileSync(join(root, CONFIG_FILE), "utf-8");
120
+ const content = readFileSync(join(root, CONFIG_FILE), "utf-8");
83
121
  const raw = parse(content) as Partial<KiwiConfig> & Record<string, unknown>;
84
122
  // Migrate old config format
85
123
  if (!raw.llm) {
86
- raw.llm = { provider: "gemini", model: "gemini-2.0-flash-lite", api_key: "", endpoint: "" };
124
+ raw.llm = { provider: "gemini", model: "gemini-3.1-flash-lite-preview", api_key: "", endpoint: "" };
87
125
  }
88
126
  // Migrate: add default persona if missing
89
127
  if (!raw.personas || !raw.personas.length) {
@@ -46,14 +46,14 @@ export const DEMO_PAGES = [
46
46
  ];
47
47
 
48
48
  export const DEMO_QUIZZES = [
49
- { page_slug: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ", question: "___์€ ์œ„์น˜์™€ ์šด๋™๋Ÿ‰์„ ๋™์‹œ์— ์ •ํ™•ํžˆ ์ธก์ •ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์–‘์ž์—ญํ•™์˜ ์›๋ฆฌ์ด๋‹ค.", answer: "๋ถˆํ™•์ •์„ฑ ์›๋ฆฌ", quiz_type: "fill_blank" },
50
- { page_slug: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ-๋ฐฉ์ •์‹", question: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹์€ 1926๋…„์— ๋ฐœํ‘œ๋˜์—ˆ๋‹ค.", answer: "O", quiz_type: "ox" },
51
- { page_slug: "์–‘์ž-์ค‘์ฒฉ", question: "์–‘์ž ์ค‘์ฒฉ์—์„œ ๊ด€์ธกํ•˜๋ฉด ์–ด๋–ค ํ˜„์ƒ์ด ์ผ์–ด๋‚˜๋Š”๊ฐ€?", answer: "์ค‘์ฒฉ์ด ๋ถ•๊ดดํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ƒํƒœ๋งŒ ๋‚จ๋Š”๋‹ค", quiz_type: "short_answer" },
52
- { page_slug: "์–‘์ž-์–ฝํž˜", question: "์•„์ธ์Šˆํƒ€์ธ์€ ์–‘์ž ์–ฝํž˜์„ '___ํ•œ ์›๊ฒฉ ์ž‘์šฉ'์ด๋ผ ๋ถˆ๋ €๋‹ค.", answer: "์œผ์Šค์Šค", quiz_type: "fill_blank" },
53
- { page_slug: "์–‘์ž-์ค‘์ฒฉ", question: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ์˜ ๊ณ ์–‘์ด ์‚ฌ๊ณ ์‹คํ—˜์—์„œ ๊ณ ์–‘์ด๋Š” ์‚ด์•„์žˆ๋Š” ์ƒํƒœ์™€ ์ฃฝ์–ด์žˆ๋Š” ์ƒํƒœ์˜ ___์— ๋†“์ธ๋‹ค.", answer: "์ค‘์ฒฉ", quiz_type: "fill_blank" },
54
- { page_slug: "์–‘์ž-์–ฝํž˜", question: "2022๋…„ ๋…ธ๋ฒจ ๋ฌผ๋ฆฌํ•™์ƒ์€ ์–‘์ž ์–ฝํž˜์˜ ๋ฒจ ๋ถ€๋“ฑ์‹ ์‹คํ—˜ ๊ฒ€์ฆ๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค.", answer: "O", quiz_type: "ox" },
55
- { page_slug: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ-๋ฐฉ์ •์‹", question: "์‹œ๊ฐ„ ๋…๋ฆฝ ์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹์—์„œ E๋Š” ๋ฌด์—‡์„ ๋‚˜ํƒ€๋‚ด๋Š”๊ฐ€?", answer: "์—๋„ˆ์ง€ ๊ณ ์œ ๊ฐ’", quiz_type: "short_answer" },
56
- { page_slug: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ", question: "๋ถˆํ™•์ •์„ฑ ์›๋ฆฌ์—์„œ ฮ”xยทฮ”p โ‰ฅ โ„/2 ์ด๋‹ค.", answer: "O", quiz_type: "ox" },
49
+ { page_slug: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ", question: "___์€ ์œ„์น˜์™€ ์šด๋™๋Ÿ‰์„ ๋™์‹œ์— ์ •ํ™•ํžˆ ์ธก์ •ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์–‘์ž์—ญํ•™์˜ ์›๋ฆฌ์ด๋‹ค.", answer: "๋ถˆํ™•์ •์„ฑ ์›๋ฆฌ", explanation: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ๊ฐ€ 1927๋…„์— ์ œ์•ˆํ•œ ์ด ์›๋ฆฌ๋Š” ์–‘์ž์—ญํ•™์˜ ๊ทผ๋ณธ์  ํ•œ๊ณ„๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์œ„์น˜๋ฅผ ์ •ํ™•ํžˆ ์ธก์ •ํ•˜๋ฉด ์šด๋™๋Ÿ‰์˜ ๋ถˆํ™•์ •์„ฑ์ด ์ปค์ง€๊ณ , ๊ทธ ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.", quiz_type: "fill_blank" },
50
+ { page_slug: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ-๋ฐฉ์ •์‹", question: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹์€ 1936๋…„์— ๋ฐœํ‘œ๋˜์—ˆ๋‹ค.", answer: "X", explanation: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹์€ 1936๋…„์ด ์•„๋‹ˆ๋ผ 1926๋…„์— ์—๋ฅด๋นˆ ์Šˆ๋ขฐ๋”ฉ๊ฑฐ๊ฐ€ ๋ฐœํ‘œํ–ˆ์Šต๋‹ˆ๋‹ค.", quiz_type: "ox" },
51
+ { page_slug: "์–‘์ž-์ค‘์ฒฉ", question: "์–‘์ž ์ค‘์ฒฉ์—์„œ ๊ด€์ธกํ•˜๋ฉด ์–ด๋–ค ํ˜„์ƒ์ด ์ผ์–ด๋‚˜๋Š”๊ฐ€?", answer: "์ค‘์ฒฉ์ด ๋ถ•๊ดดํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ƒํƒœ๋งŒ ๋‚จ๋Š”๋‹ค", explanation: "๊ด€์ธก ํ–‰์œ„๊ฐ€ ์–‘์ž ์‹œ์Šคํ…œ์— ์˜ํ–ฅ์„ ์ฃผ์–ด ์—ฌ๋Ÿฌ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ์ค‘ ํ•˜๋‚˜๋กœ '๋ถ•๊ดด'๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํŒŒ๋™ํ•จ์ˆ˜ ๋ถ•๊ดด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.", quiz_type: "short_answer" },
52
+ { page_slug: "์–‘์ž-์–ฝํž˜", question: "์•„์ธ์Šˆํƒ€์ธ์€ ์–‘์ž ์–ฝํž˜์„ '___ํ•œ ์›๊ฒฉ ์ž‘์šฉ'์ด๋ผ ๋ถˆ๋ €๋‹ค.", answer: "์œผ์Šค์Šค", explanation: "์•„์ธ์Šˆํƒ€์ธ์€ ์–‘์ž ์–ฝํž˜์ด ๊ตญ์†Œ์  ์‹ค์žฌ๋ก ์— ์œ„๋ฐฐ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์—ฌ 'spooky action at a distance(์œผ์Šค์Šคํ•œ ์›๊ฒฉ ์ž‘์šฉ)'์ด๋ผ ๋น„ํŒํ–ˆ์Šต๋‹ˆ๋‹ค.", quiz_type: "fill_blank" },
53
+ { page_slug: "์–‘์ž-์ค‘์ฒฉ", question: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ์˜ ๊ณ ์–‘์ด ์‚ฌ๊ณ ์‹คํ—˜์—์„œ ๊ณ ์–‘์ด๋Š” ์‚ด์•„์žˆ๋Š” ์ƒํƒœ์™€ ์ฃฝ์–ด์žˆ๋Š” ์ƒํƒœ์˜ ___์— ๋†“์ธ๋‹ค.", answer: "์ค‘์ฒฉ", explanation: "์ด ์‚ฌ๊ณ ์‹คํ—˜์€ ์–‘์ž ์ค‘์ฒฉ์˜ ๊ฐœ๋…์„ ๊ฑฐ์‹œ์  ์„ธ๊ณ„์— ์ ์šฉํ•˜์—ฌ ์–‘์ž์—ญํ•™์˜ ํ•ด์„ ๋ฌธ์ œ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๊ธฐ ์œ„ํ•ด ๊ณ ์•ˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", quiz_type: "fill_blank" },
54
+ { page_slug: "์–‘์ž-์–ฝํž˜", question: "2022๋…„ ๋…ธ๋ฒจ ๋ฌผ๋ฆฌํ•™์ƒ์€ ์–‘์ž ์–ฝํž˜์˜ ๋ฒจ ๋ถ€๋“ฑ์‹ ์‹คํ—˜ ๊ฒ€์ฆ๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค.", answer: "O", explanation: "์•Œ๋žญ ์•„์ŠคํŽ˜, ์กด ํด๋ผ์šฐ์ €, ์•ˆํ†ค ์ฐจ์ผ๋ง๊ฑฐ๊ฐ€ ๋ฒจ ๋ถ€๋“ฑ์‹ ์œ„๋ฐ˜์„ ์‹คํ—˜์ ์œผ๋กœ ์ฆ๋ช…ํ•˜์—ฌ ์–‘์ž ์–ฝํž˜์˜ ์‹ค์žฌ์„ฑ์„ ํ™•์ธํ•œ ๊ณต๋กœ๋กœ ์ˆ˜์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.", quiz_type: "ox" },
55
+ { page_slug: "์Šˆ๋ขฐ๋”ฉ๊ฑฐ-๋ฐฉ์ •์‹", question: "์‹œ๊ฐ„ ๋…๋ฆฝ ์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹์—์„œ E๋Š” ๋ฌด์—‡์„ ๋‚˜ํƒ€๋‚ด๋Š”๊ฐ€?", answer: "์—๋„ˆ์ง€ ๊ณ ์œ ๊ฐ’", explanation: "์‹œ๊ฐ„ ๋…๋ฆฝ ์Šˆ๋ขฐ๋”ฉ๊ฑฐ ๋ฐฉ์ •์‹ Hฯˆ = Eฯˆ์—์„œ E๋Š” ์‹œ์Šคํ…œ์˜ ์—๋„ˆ์ง€ ๊ณ ์œ ๊ฐ’์œผ๋กœ, ํ—ˆ์šฉ๋œ ์—๋„ˆ์ง€ ์ค€์œ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.", quiz_type: "short_answer" },
56
+ { page_slug: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ", question: "๋ถˆํ™•์ •์„ฑ ์›๋ฆฌ์—์„œ ฮ”xยทฮ”p โ‰ฅ โ„ ์ด๋‹ค.", answer: "X", explanation: "์ •ํ™•ํ•œ ๋ถ€๋“ฑ์‹์€ ฮ”xยทฮ”p โ‰ฅ โ„/2 ์ž…๋‹ˆ๋‹ค. โ„๊ฐ€ ์•„๋‹ˆ๋ผ โ„/2(ํ”Œ๋ž‘ํฌ ์ƒ์ˆ˜์˜ ์ ˆ๋ฐ˜)๊ฐ€ ํ•˜ํ•œ๊ฐ’์ž…๋‹ˆ๋‹ค.", quiz_type: "ox" },
57
57
  ];
58
58
 
59
59
  export const DEMO_LINKS = [
@@ -68,3 +68,70 @@ export const DEMO_LINKS = [
68
68
  { from: "์–‘์ž-์ค‘์ฒฉ", to: "ํ•˜์ด์  ๋ฒ ๋ฅดํฌ" },
69
69
  { from: "์–‘์ž-์–ฝํž˜", to: "์–‘์ž์—ญํ•™-์ž…๋ฌธ" },
70
70
  ];
71
+
72
+ export const CS_DEMO_SOURCES = [
73
+ {
74
+ uri: "demo://data-structures",
75
+ type: "demo",
76
+ title: "์ž๋ฃŒ๊ตฌ์กฐ์™€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ž…๋ฌธ",
77
+ raw_content: "์ž๋ฃŒ๊ตฌ์กฐ์™€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ธฐ์ดˆ๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค..."
78
+ }
79
+ ];
80
+
81
+ export const CS_DEMO_PAGES = [
82
+ {
83
+ slug: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ",
84
+ title: "์ž๋ฃŒ๊ตฌ์กฐ์™€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ž…๋ฌธ",
85
+ content: `# ์ž๋ฃŒ๊ตฌ์กฐ์™€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ž…๋ฌธ\n\n์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.\n\n## ์™œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋ฐฐ์›Œ์•ผ ํ•˜๋Š”๊ฐ€?\n\n๊ฐ™์€ ๋ฌธ์ œ๋ผ๋„ ์–ด๋–ค ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ์‹œ๊ฐ„ ๋ณต์žก๋„๊ฐ€ $O(n)$์—์„œ $O(\\log n)$์œผ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ~~์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ ํ•ฉ๊ฒฉ์˜ ๋น„๋ฐ€์ด ์—ฌ๊ธฐ์—~~\n\n## ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ\n\n- [[๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ]] โ€” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์„ ํ˜• ์ž๋ฃŒ๊ตฌ์กฐ\n- [[์Šคํƒ๊ณผ-ํ]] โ€” LIFO์™€ FIFO\n- [[ํ•ด์‹œํ…Œ์ด๋ธ”]] โ€” $O(1)$ ํƒ์ƒ‰์˜ ๋งˆ๋ฒ•\n- [[์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ]] โ€” ์ •๋ ฌ๋œ ๋ฐ์ดํ„ฐ์˜ ํšจ์œจ์  ๊ด€๋ฆฌ\n\n## ์‹œ๊ฐ„ ๋ณต์žก๋„\n\n| ์ž๋ฃŒ๊ตฌ์กฐ | ํƒ์ƒ‰ | ์‚ฝ์ž… | ์‚ญ์ œ |\n|----------|------|------|------|\n| ๋ฐฐ์—ด | $O(n)$ | $O(n)$ | $O(n)$ |\n| ํ•ด์‹œํ…Œ์ด๋ธ” | $O(1)$ | $O(1)$ | $O(1)$ |\n| BST | $O(\\log n)$ | $O(\\log n)$ | $O(\\log n)$ |`,
86
+ page_type: "source",
87
+ source_id: 2
88
+ },
89
+ {
90
+ slug: "๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ",
91
+ title: "๋ฐฐ์—ด๊ณผ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ",
92
+ content: `# ๋ฐฐ์—ด๊ณผ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ\n\n**๋ฐฐ์—ด**(Array)๊ณผ **์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ**(Linked List)๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์„ ํ˜• [[์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ|์ž๋ฃŒ๊ตฌ์กฐ]]์ž…๋‹ˆ๋‹ค.\n\n## ๋ฐฐ์—ด\n\n์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.\n\n\`\`\`\n์ธ๋ฑ์Šค: [0] [1] [2] [3] [4]\n๊ฐ’: 10 20 30 40 50\n\`\`\`\n\n- ์žฅ์ : $O(1)$ ๋žœ๋ค ์ ‘๊ทผ\n- ๋‹จ์ : ์‚ฝ์ž…/์‚ญ์ œ ์‹œ $O(n)$ ์ด๋™ ํ•„์š”\n\n## ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ\n\n๊ฐ ๋…ธ๋“œ๊ฐ€ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. (ํฌ์ธํ„ฐ์˜ ํž˜!)\n\n- ์žฅ์ : $O(1)$ ์‚ฝ์ž…/์‚ญ์ œ (์œ„์น˜๋ฅผ ์•Œ ๋•Œ)\n- ๋‹จ์ : $O(n)$ ํƒ์ƒ‰ โ€” ์ธ๋ฑ์Šค ์ ‘๊ทผ ๋ถˆ๊ฐ€\n\n## ์–ธ์ œ ๋ญ˜ ์“ฐ๋‚˜?\n\n๋žœ๋ค ์ ‘๊ทผ์ด ๋งŽ์œผ๋ฉด ๋ฐฐ์—ด, ์‚ฝ์ž…/์‚ญ์ œ๊ฐ€ ๋งŽ์œผ๋ฉด ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ. ~~๋ฉด์ ‘์—์„œ ์ด๊ฒƒ๋งŒ ๋Œ€๋‹ตํ•ด๋„ ๋ฐ˜์€ ๋จน๊ณ  ๋“ค์–ด๊ฐ„๋‹ค~~`,
93
+ page_type: "concept",
94
+ source_id: 2
95
+ },
96
+ {
97
+ slug: "์Šคํƒ๊ณผ-ํ",
98
+ title: "์Šคํƒ๊ณผ ํ",
99
+ content: `# ์Šคํƒ๊ณผ ํ\n\n**์Šคํƒ**(Stack)๊ณผ **ํ**(Queue)๋Š” [[์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ|์ž๋ฃŒ๊ตฌ์กฐ]]์˜ ํ•ต์‹ฌ ์ถ”์ƒ ์ž๋ฃŒํ˜•์ž…๋‹ˆ๋‹ค.\n\n## ์Šคํƒ (LIFO)\n\nLast In, First Out. ๋งˆ์ง€๋ง‰์— ๋„ฃ์€ ๊ฒƒ์ด ๋จผ์ € ๋‚˜์˜ต๋‹ˆ๋‹ค.\n\n$$push(x) โ†’ [1, 2, 3, x]$$\n$$pop() โ†’ x$$\n\nํ™œ์šฉ: ํ•จ์ˆ˜ ํ˜ธ์ถœ ์Šคํƒ, ๊ด„ํ˜ธ ๋งค์นญ, DFS, Undo ๊ธฐ๋Šฅ\n\n## ํ (FIFO)\n\nFirst In, First Out. ๋จผ์ € ๋„ฃ์€ ๊ฒƒ์ด ๋จผ์ € ๋‚˜์˜ต๋‹ˆ๋‹ค.\n\nํ™œ์šฉ: BFS, ์ž‘์—… ์Šค์ผ€์ค„๋ง, ํ”„๋ฆฐํ„ฐ ํ\n\n## ๋ฑ (Deque)\n\n์–‘์ชฝ์—์„œ ์‚ฝ์ž…/์‚ญ์ œ ๊ฐ€๋Šฅ. ์Šคํƒ๊ณผ ํ์˜ ์ผ๋ฐ˜ํ™”. (์‚ฌ์‹ค ์ด๊ฒƒ๋งŒ ์•Œ๋ฉด ๋ฉ๋‹ˆ๋‹ค)`,
100
+ page_type: "concept",
101
+ source_id: 2
102
+ },
103
+ {
104
+ slug: "ํ•ด์‹œํ…Œ์ด๋ธ”",
105
+ title: "ํ•ด์‹œํ…Œ์ด๋ธ”",
106
+ content: `# ํ•ด์‹œํ…Œ์ด๋ธ”\n\n**ํ•ด์‹œํ…Œ์ด๋ธ”**(Hash Table)์€ ํ‚ค-๊ฐ’ ์Œ์„ $O(1)$์— ์ €์žฅํ•˜๊ณ  ๊ฒ€์ƒ‰ํ•˜๋Š” [[์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ|์ž๋ฃŒ๊ตฌ์กฐ]]์ž…๋‹ˆ๋‹ค.\n\n## ํ•ด์‹œ ํ•จ์ˆ˜\n\n$$h(key) = key \\mod m$$\n\nํ‚ค๋ฅผ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ข‹์€ ํ•ด์‹œ ํ•จ์ˆ˜๋Š” ์ถฉ๋Œ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.\n\n## ์ถฉ๋Œ ํ•ด๊ฒฐ\n\n### ์ฒด์ด๋‹ (Chaining)\n๊ฐ™์€ ์ธ๋ฑ์Šค์— ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ์ €์žฅ. [[๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ|์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ]] ํ™œ์šฉ.\n\n### ๊ฐœ๋ฐฉ ์ฃผ์†Œ๋ฒ• (Open Addressing)\n์ถฉ๋Œ ์‹œ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์„ ์ฐพ์Œ. ์„ ํ˜• ํƒ์‚ฌ, ์ด์ฐจ ํƒ์‚ฌ ๋“ฑ.\n\n## Python์˜ dict\n\nPython์˜ ๋”•์…”๋„ˆ๋ฆฌ๊ฐ€ ๋ฐ”๋กœ ํ•ด์‹œํ…Œ์ด๋ธ”์ž…๋‹ˆ๋‹ค. ~~์ด๋ฏธ ๋งค์ผ ์“ฐ๊ณ  ์žˆ์—ˆ๋‹ค๋Š” ์‚ฌ์‹ค~~`,
107
+ page_type: "concept",
108
+ source_id: 2
109
+ },
110
+ {
111
+ slug: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ",
112
+ title: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ",
113
+ content: `# ์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ (BST)\n\n**์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ**๋Š” ์™ผ์ชฝ ์ž์‹ < ๋ถ€๋ชจ < ์˜ค๋ฅธ์ชฝ ์ž์‹ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋Š” [[์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ|์ž๋ฃŒ๊ตฌ์กฐ]]์ž…๋‹ˆ๋‹ค.\n\n## ์‹œ๊ฐ„ ๋ณต์žก๋„\n\n| ์—ฐ์‚ฐ | ํ‰๊ท  | ์ตœ์•… |\n|------|------|------|\n| ํƒ์ƒ‰ | $O(\\log n)$ | $O(n)$ |\n| ์‚ฝ์ž… | $O(\\log n)$ | $O(n)$ |\n| ์‚ญ์ œ | $O(\\log n)$ | $O(n)$ |\n\n์ตœ์•…์˜ ๊ฒฝ์šฐ๋Š” ํŠธ๋ฆฌ๊ฐ€ ํ•œ์ชฝ์œผ๋กœ ์น˜์šฐ์น  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. (ํŽธํ–ฅ ์ด์ง„ ํŠธ๋ฆฌ)\n\n## ๊ท ํ˜• ํŠธ๋ฆฌ\n\nAVL ํŠธ๋ฆฌ, Red-Black ํŠธ๋ฆฌ๋Š” **ํšŒ์ „(rotation)** ์—ฐ์‚ฐ์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ ๊ท ํ˜•์„ ์œ ์ง€ํ•˜์—ฌ ํ•ญ์ƒ $O(\\log n)$์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ํšŒ์ „์€ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋ฅผ ์žฌ๋ฐฐ์น˜ํ•ด ํŠธ๋ฆฌ์˜ ๋†’์ด๋ฅผ ๋‚ฎ์ถ”๋Š” ํ•ต์‹ฌ ์—ฐ์‚ฐ์ž…๋‹ˆ๋‹ค.\n\n## ์ˆœํšŒ\n\n- ์ค‘์œ„ ์ˆœํšŒ (Inorder): ์ •๋ ฌ๋œ ์ˆœ์„œ๋กœ ์ถœ๋ ฅ\n- ์ „์œ„ ์ˆœํšŒ (Preorder): ํŠธ๋ฆฌ ๋ณต์‚ฌ\n- ํ›„์œ„ ์ˆœํšŒ (Postorder): ํŠธ๋ฆฌ ์‚ญ์ œ`,
114
+ page_type: "concept",
115
+ source_id: 2
116
+ }
117
+ ];
118
+
119
+ export const CS_DEMO_LINKS = [
120
+ { from: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ", to: "๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ" },
121
+ { from: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ", to: "์Šคํƒ๊ณผ-ํ" },
122
+ { from: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ", to: "ํ•ด์‹œํ…Œ์ด๋ธ”" },
123
+ { from: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ", to: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ" },
124
+ { from: "ํ•ด์‹œํ…Œ์ด๋ธ”", to: "๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ" },
125
+ { from: "์Šคํƒ๊ณผ-ํ", to: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ" },
126
+ { from: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ", to: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ" },
127
+ { from: "๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ", to: "์ž๋ฃŒ๊ตฌ์กฐ-์ž…๋ฌธ" },
128
+ ];
129
+
130
+ export const CS_DEMO_QUIZZES = [
131
+ { page_slug: "๋ฐฐ์—ด๊ณผ-์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ", question: "๋ฐฐ์—ด์—์„œ ์ธ๋ฑ์Šค๋ฅผ ํ†ตํ•œ ๋žœ๋ค ์ ‘๊ทผ์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š”?", answer: "O(1)", quiz_type: "short_answer", explanation: "๋ฐฐ์—ด์€ ์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— ์ €์žฅ๋˜๋ฏ€๋กœ ์ธ๋ฑ์Šค ๊ณ„์‚ฐ๋งŒ์œผ๋กœ ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค." },
132
+ { page_slug: "ํ•ด์‹œํ…Œ์ด๋ธ”", question: "ํ•ด์‹œํ…Œ์ด๋ธ”์˜ ํ‰๊ท  ํƒ์ƒ‰ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” ___์ด๋‹ค.", answer: "O(1)", quiz_type: "fill_blank", explanation: "ํ•ด์‹œ ํ•จ์ˆ˜๊ฐ€ ํ‚ค๋ฅผ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋กœ ์ง์ ‘ ๋ณ€ํ™˜ํ•˜๋ฏ€๋กœ ์ƒ์ˆ˜ ์‹œ๊ฐ„์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค." },
133
+ { page_slug: "์Šคํƒ๊ณผ-ํ", question: "์Šคํƒ์€ FIFO(First In First Out) ๊ตฌ์กฐ์ด๋‹ค.", answer: "X", quiz_type: "ox", explanation: "์Šคํƒ์€ LIFO(Last In First Out)์ž…๋‹ˆ๋‹ค. FIFO๋Š” ํ์˜ ํŠน์„ฑ์ž…๋‹ˆ๋‹ค." },
134
+ { page_slug: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ", question: "BST์—์„œ ์ค‘์œ„ ์ˆœํšŒ๋ฅผ ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์ •๋ ฌ๋œ ์ˆœ์„œ๋กœ ์ถœ๋ ฅ๋œ๋‹ค.", answer: "O", quiz_type: "ox", explanation: "BST์˜ ์™ผ์ชฝ < ๋ถ€๋ชจ < ์˜ค๋ฅธ์ชฝ ๊ทœ์น™์— ์˜ํ•ด ์ค‘์œ„ ์ˆœํšŒ๋Š” ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค." },
135
+ { page_slug: "ํ•ด์‹œํ…Œ์ด๋ธ”", question: "ํ•ด์‹œ ์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ___๊ณผ ๊ฐœ๋ฐฉ ์ฃผ์†Œ๋ฒ•์ด๋‹ค.", answer: "์ฒด์ด๋‹", quiz_type: "fill_blank", explanation: "์ฒด์ด๋‹์€ ๊ฐ™์€ ์ธ๋ฑ์Šค์— ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ์ €์žฅํ•˜๊ณ , ๊ฐœ๋ฐฉ ์ฃผ์†Œ๋ฒ•์€ ๋‹ค์Œ ๋นˆ ์Šฌ๋กฏ์„ ์ฐพ์Šต๋‹ˆ๋‹ค." },
136
+ { page_slug: "์ด์ง„ํƒ์ƒ‰ํŠธ๋ฆฌ", question: "ํŽธํ–ฅ ์ด์ง„ ํŠธ๋ฆฌ์—์„œ ํƒ์ƒ‰์˜ ์ตœ์•… ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š”?", answer: "O(n)", quiz_type: "short_answer", explanation: "ํŠธ๋ฆฌ๊ฐ€ ํ•œ์ชฝ์œผ๋กœ ์น˜์šฐ์น˜๋ฉด ์‚ฌ์‹ค์ƒ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ์™€ ๊ฐ™์•„์ ธ ์„ ํ˜• ํƒ์ƒ‰์ด ๋ฉ๋‹ˆ๋‹ค." },
137
+ ];
package/src/demo/setup.ts CHANGED
@@ -1,18 +1,24 @@
1
- import { DEMO_SOURCES, DEMO_PAGES, DEMO_LINKS, DEMO_QUIZZES } from "./sample-data";
1
+ import { DEMO_SOURCES, DEMO_PAGES, DEMO_LINKS, DEMO_QUIZZES, CS_DEMO_SOURCES, CS_DEMO_PAGES, CS_DEMO_LINKS, CS_DEMO_QUIZZES } from "./sample-data";
2
2
  import type { Store } from "../store";
3
3
 
4
4
  export async function setupDemo(store: Store): Promise<void> {
5
- // 1. Insert demo source
5
+ // 1. Insert demo sources (physics + CS)
6
6
  for (const source of DEMO_SOURCES) {
7
7
  store.addSource(source.uri, source.type, source.title, source.raw_content);
8
8
  }
9
+ for (const source of CS_DEMO_SOURCES) {
10
+ store.addSource(source.uri, source.type, source.title, source.raw_content);
11
+ }
9
12
 
10
- // 2. Insert demo pages
13
+ // 2. Insert demo pages (physics + CS)
11
14
  for (const page of DEMO_PAGES) {
12
- store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type);
15
+ store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type as 'source' | 'concept');
16
+ }
17
+ for (const page of CS_DEMO_PAGES) {
18
+ store.addPage(page.slug, page.title, page.content, page.source_id, undefined, page.page_type as 'source' | 'concept');
13
19
  }
14
20
 
15
- // 3. Insert demo links
21
+ // 3. Insert demo links (physics + CS)
16
22
  for (const link of DEMO_LINKS) {
17
23
  const fromPage = store.getPage(link.from);
18
24
  const toPage = store.getPage(link.to);
@@ -20,12 +26,25 @@ export async function setupDemo(store: Store): Promise<void> {
20
26
  store.addLink(fromPage.id, toPage.id, toPage.title);
21
27
  }
22
28
  }
29
+ for (const link of CS_DEMO_LINKS) {
30
+ const fromPage = store.getPage(link.from);
31
+ const toPage = store.getPage(link.to);
32
+ if (fromPage && toPage) {
33
+ store.addLink(fromPage.id, toPage.id, toPage.title);
34
+ }
35
+ }
23
36
 
24
- // 4. Insert demo quizzes
37
+ // 4. Insert demo quizzes (physics + CS)
25
38
  for (const quiz of DEMO_QUIZZES) {
26
39
  const page = store.getPage(quiz.page_slug);
27
40
  if (page) {
28
- store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type);
41
+ store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type, (quiz as any).explanation || "");
42
+ }
43
+ }
44
+ for (const quiz of CS_DEMO_QUIZZES) {
45
+ const page = store.getPage(quiz.page_slug);
46
+ if (page) {
47
+ store.addQuiz(page.id, quiz.question, quiz.answer, quiz.quiz_type, (quiz as any).explanation || "");
29
48
  }
30
49
  }
31
50
  }
package/src/expand/llm.ts CHANGED
@@ -31,7 +31,7 @@ export async function expandWithApi(page: Page, context: Page[], provider: strin
31
31
  const { default: Anthropic } = await import("@anthropic-ai/sdk");
32
32
  const client = new Anthropic();
33
33
  const resp = await client.messages.create({
34
- model: model || "claude-sonnet-4-20250514",
34
+ model: model || "claude-sonnet-4-6",
35
35
  max_tokens: 4096,
36
36
  messages: [{ role: "user", content: prompt }],
37
37
  });
@@ -42,7 +42,7 @@ export async function expandWithApi(page: Page, context: Page[], provider: strin
42
42
  const { default: OpenAI } = await import("openai");
43
43
  const client = new OpenAI();
44
44
  const resp = await client.chat.completions.create({
45
- model: model || "gpt-4o",
45
+ model: model || "gpt-5.4",
46
46
  messages: [{ role: "user", content: prompt }],
47
47
  });
48
48
  return resp.choices[0].message.content || "";