OpenLab-NLP commited on
Commit
aff5f11
ยท
verified ยท
1 Parent(s): 6536001

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +200 -151
index.html CHANGED
@@ -2,185 +2,234 @@
2
  <html lang="ko">
3
  <head>
4
  <meta charset="UTF-8">
5
- <title>Huiucl Auto-Load Dictionary</title>
6
  <style>
7
- :root { --primary: #2c3e50; --accent: #e67e22; --bg: #f8f9fa; --card: #ffffff; }
8
- body { font-family: 'Malgun Gothic', 'Apple SD Gothic Neo', sans-serif; background: var(--bg); padding: 20px; color: var(--primary); }
9
- .container { max-width: 900px; margin: 0 auto; }
10
-
11
- /* ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ */
12
- #loader { text-align: center; padding: 20px; font-weight: bold; color: #2980b9; }
13
-
14
- /* ํ—ค๋” ์„น์…˜ */
15
- .header-panel { background: var(--primary); color: white; padding: 30px; border-radius: 15px; margin-bottom: 25px; box-shadow: 0 4px 15px rgba(0,0,0,0.2); }
16
- .header-panel h1 { margin: 0; font-size: 32px; letter-spacing: 1px; }
17
 
18
- /* ๊ฒ€์ƒ‰์ฐฝ */
19
- .search-container { position: sticky; top: 15px; z-index: 1000; margin-bottom: 30px; }
20
- .search-input { width: 100%; padding: 20px 25px; font-size: 22px; border: 4px solid #ddd; border-radius: 50px; box-sizing: border-box; outline: none; box-shadow: 0 10px 25px rgba(0,0,0,0.1); transition: all 0.3s ease; }
21
- .search-input:focus { border-color: var(--accent); transform: translateY(-2px); }
22
-
23
- /* ๊ฒฐ๊ณผ ์นด๋“œ */
24
- .card { background: var(--card); border-radius: 15px; padding: 30px; margin-bottom: 25px; box-shadow: 0 5px 20px rgba(0,0,0,0.05); border-left: 8px solid #dcdde1; transition: transform 0.2s; }
25
- .card:hover { transform: scale(1.01); }
26
- .card.exact { border-left-color: var(--accent); }
 
 
 
27
 
28
- .badge { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-bottom: 10px; }
29
- .badge-cat { background: #f1f2f6; color: #7f8c8d; }
30
- .badge-exact { background: var(--accent); color: white; margin-left: 10px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- .word-row { display: flex; align-items: baseline; gap: 15px; flex-wrap: wrap; }
33
- .word-text { font-size: 32px; font-weight: 900; color: #2980b9; text-transform: uppercase; }
34
- .meaning-text { font-size: 20px; color: #2c3e50; font-weight: 600; }
35
- .meaning-en { color: #95a5a6; font-size: 16px; font-style: italic; }
36
-
37
- /* ๋ฐ์ดํ„ฐ ๊ทธ๋ฆฌ๋“œ (ํŒŒ์ƒ์–ด, ๋ณ€์ดํ˜•) */
38
- .grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; margin-top: 20px; }
39
- .data-list { background: #fdfdfd; border: 1px solid #f1f1f1; border-radius: 10px; padding: 15px; }
40
- .list-title { font-size: 14px; font-weight: bold; color: #34495e; border-bottom: 2px solid #3498db; padding-bottom: 5px; margin-bottom: 10px; }
41
- .item-row { display: flex; font-size: 14px; margin-bottom: 4px; }
42
- .item-key { font-weight: bold; width: 110px; color: #e67e22; }
43
-
44
- /* ์ƒ์„ธ ์ •๋ณด (๊ฒ€์ƒ‰ ์ œ์™ธ ๊ตฌ์—ญ) */
45
- .details { margin-top: 20px; padding-top: 15px; border-top: 1px dashed #eee; font-size: 14px; color: #555; }
46
- .detail-item { margin-bottom: 5px; }
47
- .label { font-weight: bold; color: #7f8c8d; margin-right: 5px; }
48
- .example-box { color: #2980b9; background: #ebf5fb; padding: 10px; border-radius: 5px; margin-top: 5px; }
49
  </style>
50
  </head>
51
  <body>
52
 
53
- <div class="container">
54
- <div class="header-panel">
55
- <h1>Huiucl Engine <small style="font-size: 14px; opacity: 0.8;">v3.0 Auto-Load</small></h1>
56
- <div id="loader">โš™๏ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...</div>
57
  </div>
 
58
 
59
- <div class="search-container">
60
- <input type="text" id="query" class="search-input" placeholder="๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š” (su, a, mi, ์ œ์ž‘ํ•˜๋‹ค...)" onkeyup="search()">
61
- </div>
 
62
 
63
- <div id="results"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  </div>
65
 
66
  <script>
67
- let lexicon = {};
68
-
69
- // [์ž๋™ ๋กœ๋“œ] ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ Huiucl.json ํŒŒ์ผ์„ ์ฆ‰์‹œ ์ฝ์–ด์˜ด
70
- window.onload = async function() {
71
- try {
72
- const response = await fetch('Huiucl.json');
73
- if (!response.ok) throw new Error('ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
74
- const data = await response.json();
75
-
76
- process(data);
77
-
78
- document.getElementById('loader').innerText = `โœ… ${Object.keys(lexicon).length}๊ฐœ ํ•ญ๋ชฉ ๋กœ๋“œ ์™„๋ฃŒ. ๋ฐ”๋กœ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.`;
79
- setTimeout(() => document.getElementById('loader').style.display = 'none', 3000);
80
- } catch (err) {
81
- document.getElementById('loader').innerHTML = `โŒ ์˜ค๋ฅ˜: ${err.message}<br><small>Huiucl.json ํŒŒ์ผ์ด ๊ฐ™์€ ํด๋”์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.</small>`;
82
  }
83
- };
84
-
85
- function process(obj, parentKey = null, currentCat = null) {
86
- if (typeof obj !== 'object' || obj === null) return;
87
-
88
- // 1. ๋Œ€๋ช…์‚ฌ ์„น์…˜ (a, i, u ๋“ฑ ํ•œ ๊ธ€์ž ๋Œ€๋ช…์‚ฌ ์™„๋ฒฝ ๋กœ๋“œ)
89
- const pSects = ["1st_Person", "2nd_Person", "3rd_Person", "Demonstratives"];
90
- if (pSects.includes(parentKey)) {
91
- for (const [pk, pv] of Object.entries(obj)) {
92
- lexicon[pk] = {
93
- meaning_ko: pv,
94
- _category: `Pronoun (${parentKey})`,
95
- _search_target: `${pk} ${pv}`.toLowerCase()
96
- };
 
 
 
 
 
 
97
  }
98
- return;
99
  }
100
 
101
- // 2. ์ผ๋ฐ˜ ๋‹จ์–ด ์ฒ˜๋ฆฌ
102
- const keys = ['meaning_ko', 'ko', 'definition', 'morpheme', 'meaning_en', 'en'];
103
- const hasInfo = keys.some(k => k in obj);
104
-
105
- if (hasInfo && parentKey) {
106
- let entry = { ...obj };
107
- if (entry.definition && typeof entry.definition === 'object') Object.assign(entry, entry.definition);
108
-
109
- // [์ค‘์š”] ๊ฒ€์ƒ‰ ํƒ€๊ฒŸ์—์„œ usage, example์€ ์ œ๊ฑฐ (์˜ค๋กœ์ง€ ๋‹จ์–ด๋ช…๊ณผ ๋œป๋งŒ)
110
- let targets = [parentKey];
111
- keys.forEach(k => { if (entry[k]) targets.push(entry[k]); });
112
-
113
- entry._category = currentCat;
114
- entry._search_target = targets.join(" ").toLowerCase();
115
- lexicon[parentKey] = entry;
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
117
 
118
- for (const [k, v] of Object.entries(obj)) {
119
- if (typeof v === 'object') {
120
- const nextCat = (parentKey === null) ? k : currentCat;
121
- if (k !== 'Settings') process(v, k, nextCat);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
 
123
  }
124
- }
125
-
126
- function search() {
127
- const q = document.getElementById('query').value.trim().toLowerCase();
128
- const resDiv = document.getElementById('results');
129
- resDiv.innerHTML = '';
130
- if (!q) return;
131
-
132
- let found = [];
133
- // ์ •ํ™•ํ•œ ๋…๋ฆฝ ํ˜•ํƒœ์†Œ ์ธ์ง€๋ฅผ ์œ„ํ•œ ์ •๊ทœ์‹
134
- const re = new RegExp(`(^|[^a-zA-Z๊ฐ€-ํžฃ])${q}($|[^a-zA-Z๊ฐ€-ํžฃ])`, 'i');
135
-
136
- for (const [word, info] of Object.entries(lexicon)) {
137
- const wordL = word.toLowerCase();
138
- const target = info._search_target || "";
139
 
140
- if (q === wordL) found.push({ word, info, score: 2 });
141
- else if (re.test(target) || (q.length > 1 && target.includes(q))) found.push({ word, info, score: 1 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
 
144
- found.sort((a, b) => b.score - a.score).forEach(item => {
145
- const info = item.info;
146
- const card = document.createElement('div');
147
- card.className = `card ${item.score === 2 ? 'exact' : ''}`;
148
-
149
- // ํŒŒ์ƒ์–ด/๋ณ€์ดํ˜• ๊ทธ๋ฆฌ๋“œ ์ƒ์„ฑ
150
- let gridHtml = '';
151
- const derivs = info.derivations || info.derivation;
152
- if (derivs) {
153
- gridHtml += `<div class="data-list"><div class="list-title">๐Ÿ“‚ ๊ธฐ๋ก๋œ ํŒŒ์ƒ์–ด</div>`;
154
- for(const [dw, dm] of Object.entries(derivs)) gridHtml += `<div class="item-row"><span class="item-key">${dw}</span> <span>${dm}</span></div>`;
155
- gridHtml += `</div>`;
156
  }
157
 
158
- let vars = {};
159
- ["tense_variants", "aspect_voice", "variants"].forEach(k => { if(info[k]) Object.assign(vars, info[k]); });
160
- if (Object.keys(vars).length > 0) {
161
- gridHtml += `<div class="data-list"><div class="list-title">๐Ÿ“‚ ์‹œ์ œ ๋ฐ ๋ณ€์ด</div>`;
162
- for(const [vw, vi] of Object.entries(vars)) gridHtml += `<div class="item-row"><span class="item-key">${vw}</span> <span>${vi.meaning_ko || vi.ko || ''}</span></div>`;
163
- gridHtml += `</div>`;
 
 
 
 
 
 
 
164
  }
165
-
166
- card.innerHTML = `
167
- <span class="badge badge-cat">${info._category}</span>
168
- ${item.score === 2 ? '<span class="badge badge-exact">์ •ํ™•ํ•œ ์ผ์น˜</span>' : ''}
169
- <div class="word-row">
170
- <span class="word-text">${item.word}</span>
171
- <span class="meaning-text">${info.meaning_ko || info.ko || info.morpheme || ''}</span>
172
- <span class="meaning-en">${info.meaning_en || info.en || ''}</span>
173
- </div>
174
- <div class="grid-container">${gridHtml}</div>
175
- <div class="details">
176
- ${info.usage ? `<div class="detail-item"><span class="label">๐Ÿ“ ์šฉ๋ฒ•:</span> ${typeof info.usage === 'object' ? info.usage.ko : info.usage}</div>` : ''}
177
- ${info.note ? `<div class="detail-item"><span class="label">๐Ÿ’ก ์ฐธ๊ณ :</span> ${info.note}</div>` : ''}
178
- ${info.example ? `<div class="example-box"><span class="label">๐Ÿ“– ์˜ˆ๋ฌธ:</span> ${info.example}</div>` : ''}
179
- </div>
180
- `;
181
- resDiv.appendChild(card);
182
- });
183
- }
184
  </script>
185
  </body>
186
  </html>
 
2
  <html lang="ko">
3
  <head>
4
  <meta charset="UTF-8">
5
+ <title>HUIUCL CORE ARCHIVE v5.0</title>
6
  <style>
7
+ :root {
8
+ --bg: #f5f3f0;
9
+ --card: #ffffff;
10
+ --accent: #a68b6a;
11
+ --primary: #2c2c2c;
12
+ --secondary: #767676;
13
+ --border: #e0ddd5;
14
+ }
15
+
16
+ body { font-family: 'Pretendard', -apple-system, sans-serif; background: var(--bg); color: var(--primary); margin: 0; line-height: 1.6; }
17
 
18
+ /* Navigation */
19
+ nav { background: var(--primary); padding: 1rem 0; position: sticky; top: 0; z-index: 1000; box-shadow: 0 2px 15px rgba(0,0,0,0.1); }
20
+ .nav-container { max-width: 1100px; margin: 0 auto; display: flex; justify-content: center; gap: 40px; }
21
+ nav a { color: #fff; text-decoration: none; font-weight: 600; font-size: 0.9rem; letter-spacing: 2px; cursor: pointer; opacity: 0.7; transition: 0.3s; }
22
+ nav a:hover, nav a.active { opacity: 1; color: var(--accent); }
23
+
24
+ /* Header */
25
+ header { background: #fff; padding: 60px 20px; text-align: center; border-bottom: 1px solid var(--border); }
26
+ h1 { margin: 0; letter-spacing: 12px; font-weight: 200; color: var(--primary); font-size: 2.5rem; }
27
+ .sub-header { color: var(--secondary); font-size: 0.8rem; margin-top: 15px; letter-spacing: 3px; text-transform: uppercase; }
28
+
29
+ .container { max-width: 1100px; margin: 40px auto; padding: 0 20px; }
30
 
31
+ /* Dictionary Styling */
32
+ .search-box { width: 100%; padding: 20px 30px; border: 1px solid var(--border); border-radius: 40px; font-size: 1.1rem; box-sizing: border-box; outline: none; background: #fff; box-shadow: 0 5px 20px rgba(0,0,0,0.03); transition: 0.3s; margin-bottom: 30px; }
33
+ .search-box:focus { border-color: var(--accent); box-shadow: 0 5px 25px rgba(166, 139, 106, 0.15); }
34
+
35
+ .table-card { background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 40px rgba(0,0,0,0.04); border: 1px solid var(--border); }
36
+ table { width: 100%; border-collapse: collapse; table-layout: fixed; }
37
+ th { background: #faf9f7; padding: 20px; text-align: left; font-size: 0.75rem; color: #999; letter-spacing: 1px; border-bottom: 2px solid var(--border); }
38
+ td { padding: 20px; border-bottom: 1px solid var(--border); vertical-align: top; word-break: break-all; }
39
+
40
+ .word-cell { width: 25%; font-size: 1.2rem; font-weight: 700; color: var(--primary); }
41
+ .meaning-cell { width: 55%; }
42
+ .path-cell { width: 20%; text-align: right; font-size: 0.7rem; color: #bbb; text-transform: uppercase; }
43
+
44
+ .ko-text { font-size: 1.1rem; font-weight: 600; color: var(--accent); margin-bottom: 4px; }
45
+ .en-text { font-size: 0.95rem; color: var(--secondary); }
46
+ .usage-box { margin-top: 12px; padding: 12px; background: #fdfcfb; border: 1px solid #f0eee9; border-left: 4px solid var(--accent); font-size: 0.85rem; color: #555; }
47
 
48
+ /* Grammar Section Styling */
49
+ .grammar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(450px, 1fr)); gap: 20px; }
50
+ .grammar-card { background: #fff; padding: 30px; border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 5px 15px rgba(0,0,0,0.02); }
51
+ .grammar-card h2 { margin-top: 0; font-size: 1.3rem; color: var(--accent); border-bottom: 1px solid var(--border); padding-bottom: 15px; margin-bottom: 20px; font-weight: 400; }
52
+ .rule-item { margin-bottom: 15px; }
53
+ .rule-name { font-weight: 700; color: var(--primary); display: block; margin-bottom: 3px; font-size: 0.95rem; }
54
+ .rule-desc { font-size: 0.9rem; color: var(--secondary); display: block; }
55
+
56
+ section { display: none; }
57
+ section.active { display: block; animation: fadeIn 0.4s ease-out; }
58
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
 
 
 
 
 
 
59
  </style>
60
  </head>
61
  <body>
62
 
63
+ <nav>
64
+ <div class="nav-container">
65
+ <a id="btn-dict" class="active" onclick="showTab('dictionary')">DICTIONARY</a>
66
+ <a id="btn-gram" onclick="showTab('grammar')">GRAMMAR</a>
67
  </div>
68
+ </nav>
69
 
70
+ <header>
71
+ <h1>HUIUCL</h1>
72
+ <div class="sub-header">Systemized Linguistic Archive v5.0</div>
73
+ </header>
74
 
75
+ <div class="container">
76
+ <section id="dictionary" class="active">
77
+ <input type="text" id="searchInput" class="search-box" placeholder="Search words, meanings, or categories..." oninput="render()">
78
+ <div class="table-card">
79
+ <table>
80
+ <thead>
81
+ <tr>
82
+ <th>WORD</th>
83
+ <th>MEANING & USAGE</th>
84
+ <th style="text-align:right">CATEGORY</th>
85
+ </tr>
86
+ </thead>
87
+ <tbody id="dictBody"></tbody>
88
+ </table>
89
+ </div>
90
+ </section>
91
+
92
+ <section id="grammar">
93
+ <div id="grammarContent" class="grammar-grid">
94
+ </div>
95
+ </section>
96
  </div>
97
 
98
  <script>
99
+ let db = {};
100
+
101
+ function showTab(id) {
102
+ document.querySelectorAll('section').forEach(s => s.classList.remove('active'));
103
+ document.querySelectorAll('nav a').forEach(a => a.classList.remove('active'));
104
+ document.getElementById(id).classList.add('active');
105
+ document.getElementById('btn-' + (id === 'dictionary' ? 'dict' : 'gram')).classList.add('active');
 
 
 
 
 
 
 
 
106
  }
107
+
108
+ window.onload = () => {
109
+ fetch('Huiucl.json')
110
+ .then(res => res.json())
111
+ .then(data => {
112
+ db = data;
113
+ render();
114
+ renderGrammar();
115
+ })
116
+ .catch(err => console.error("JSON ๋กœ๋“œ ์‹คํŒจ:", err));
117
+ };
118
+
119
+ function render() {
120
+ const query = document.getElementById('searchInput').value.toLowerCase();
121
+ const body = document.getElementById('dictBody');
122
+ body.innerHTML = '';
123
+
124
+ for (const cat in db) {
125
+ if (cat === "Settings") continue;
126
+ processCategory(db[cat], cat, query);
127
  }
 
128
  }
129
 
130
+ function processCategory(obj, path, query) {
131
+ if (path.includes("Grammar_Rules")) return;
132
+
133
+ for (const key in obj) {
134
+ const val = obj[key];
135
+ if (!val) continue;
136
+
137
+ const info = getInfo(key, val);
138
+
139
+ if (info.isWord) {
140
+ if ((info.word + info.ko + info.en + path).toLowerCase().includes(query)) {
141
+ appendRow(info, path);
142
+ }
143
+ // ํŒŒ์ƒ์–ด ์ฒ˜๋ฆฌ
144
+ if (val.derivations) {
145
+ for (const dk in val.derivations) {
146
+ appendRow({ word: dk, ko: val.derivations[dk], en: "", usage: "" }, path, true);
147
+ }
148
+ }
149
+ // ์‹œ์ œ ๋ณ€ํ˜• ๋“ฑ ๋‚ด๋ถ€ ์žฌ๊ท€
150
+ if (typeof val === 'object' && val.tense_variants) {
151
+ processCategory(val.tense_variants, path + " > " + key, query);
152
+ }
153
+ } else if (typeof val === 'object') {
154
+ processCategory(val, path + " > " + key, query);
155
+ }
156
+ }
157
  }
158
 
159
+ function getInfo(key, val) {
160
+ let res = { isWord: false, word: key, ko: "", en: "", usage: "" };
161
+
162
+ if (typeof val === 'string') {
163
+ res.isWord = true;
164
+ res.ko = val;
165
+ } else if (typeof val === 'object') {
166
+ // morpheme ํ‚ค๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ๋‹จ์–ด๋กœ ์ทจ๊ธ‰
167
+ if (val.morpheme) res.word = val.morpheme;
168
+
169
+ res.ko = val.meaning_ko || val.ko || val.๋œป || val.definition || "";
170
+ res.en = val.meaning_en || val.en || "";
171
+ res.usage = val.usage || val.note || "";
172
+
173
+ // ๊ฐ์ฒด ์•ˆ์˜ ๊ฐ์ฒด ์—๋Ÿฌ ๋ฐฉ์ง€ (๊ฐ•์ œ ๋ฌธ์ž์—ดํ™”)
174
+ const clean = (v) => (typeof v === 'object' ? (v.ko || v.meaning_ko || v.en || JSON.stringify(v)) : v);
175
+ res.ko = clean(res.ko);
176
+ res.en = clean(res.en);
177
+ res.usage = clean(res.usage);
178
+
179
+ if (res.ko || res.en) res.isWord = true;
180
  }
181
+ return res;
182
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
+ function appendRow(info, path, isSub) {
185
+ const body = document.getElementById('dictBody');
186
+ const row = body.insertRow();
187
+ if (isSub) row.style.background = "#fcfcfc";
188
+
189
+ let meaningHtml = `<div class="ko-text">${info.ko}</div>`;
190
+ if (info.en) meaningHtml += `<div class="en-text">${info.en}</div>`;
191
+ if (info.usage) meaningHtml += `<div class="usage-box"><strong>NOTE:</strong> ${info.usage}</div>`;
192
+
193
+ row.innerHTML = `
194
+ <td class="word-cell" style="${isSub ? 'padding-left:40px; font-size:1rem; opacity:0.7;' : ''}">
195
+ ${isSub ? 'โ†ณ ' : ''}${info.word}
196
+ </td>
197
+ <td class="meaning-cell">${meaningHtml}</td>
198
+ <td class="path-cell">${path}</td>
199
+ `;
200
  }
201
 
202
+ function renderGrammar() {
203
+ const container = document.getElementById('grammarContent');
204
+ let html = "";
205
+
206
+ // 1. Settings ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
207
+ if (db.Settings) {
208
+ html += `<div class="grammar-card">
209
+ <h2>General System</h2>
210
+ <div class="rule-item"><span class="rule-name">Word Order</span><span class="rule-desc">${db.Settings.Syntax.Word_Order.join(', ')}</span></div>
211
+ <div class="rule-item"><span class="rule-name">Imperative</span><span class="rule-desc">${db.Settings.Syntax.Imperative["2"]}</span></div>
212
+ <div class="rule-item"><span class="rule-name">Phonology</span><span class="rule-desc">Vowels: ${Object.keys(db.Settings.Phonology.Vowels).join(', ')} / Consonants: ${Object.keys(db.Settings.Phonology.Consonants).join(', ')}</span></div>
213
+ </div>`;
214
  }
215
 
216
+ // 2. ํŠธ๋ฆฌ ์ „์ฒด๋ฅผ ๋’ค์ ธ์„œ Grammar_Rules ์ˆ˜์ง‘
217
+ function collect(obj, path) {
218
+ for (const key in obj) {
219
+ if (key === "Grammar_Rules") {
220
+ html += `<div class="grammar-card"><h2>${path} Rules</h2>`;
221
+ for (const r in obj[key]) {
222
+ html += `<div class="rule-item"><span class="rule-name">${r}</span><span class="rule-desc">${obj[key][r]}</span></div>`;
223
+ }
224
+ html += `</div>`;
225
+ } else if (typeof obj[key] === 'object' && key !== "Settings") {
226
+ collect(obj[key], path ? path + " > " + key : key);
227
+ }
228
+ }
229
  }
230
+ collect(db, "");
231
+ container.innerHTML = html || "<p style='grid-column: 1/-1; text-align:center; padding:50px;'>No grammar rules found.</p>";
232
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  </script>
234
  </body>
235
  </html>