ondc-code-generator 0.8.8 → 0.9.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/README.md +1 -1
- package/alpha/table/page/index.html +11487 -0
- package/alpha/table/page/style.css +449 -0
- package/alpha/table/rag-table-docs/confirm.md +60 -0
- package/alpha/table/rag-table-docs/init.md +78 -0
- package/alpha/table/rag-table-docs/on_confirm.md +161 -0
- package/alpha/table/rag-table-docs/on_init.md +143 -0
- package/alpha/table/rag-table-docs/on_search.md +160 -0
- package/alpha/table/rag-table-docs/on_select.md +96 -0
- package/alpha/table/rag-table-docs/on_status.md +150 -0
- package/alpha/table/rag-table-docs/on_update.md +161 -0
- package/alpha/table/rag-table-docs/raw_table.json +11198 -0
- package/alpha/table/rag-table-docs/search.md +125 -0
- package/alpha/table/rag-table-docs/select.md +67 -0
- package/alpha/table/rag-table-docs/status.md +41 -0
- package/alpha/table/rag-table-docs/update.md +48 -0
- package/alpha/table/readme.md +1312 -0
- package/alpha/table/validPaths.json +34134 -0
- package/alpha/table.zip +0 -0
- package/dist/bin/cli.js +6 -0
- package/dist/generator/config-compiler.js +16 -0
- package/dist/generator/generators/documentation/md-generator.d.ts +11 -5
- package/dist/generator/generators/documentation/md-generator.js +22 -28
- package/dist/generator/generators/documentation/templates/index.mustache +162 -26
- package/dist/generator/generators/documentation/templates/style.css +387 -142
- package/dist/generator/generators/rag/rag-generator.d.ts +48 -0
- package/dist/generator/generators/rag/rag-generator.js +185 -0
- package/dist/generator/generators/rag/rag-table-generator.d.ts +55 -0
- package/dist/generator/generators/rag/rag-table-generator.js +263 -0
- package/dist/types/compiler-types.d.ts +4 -1
- package/dist/types/compiler-types.js +3 -0
- package/docs/jval-dsl.md +913 -0
- package/package.json +1 -1
- package/alpha/golang/newPkg/go.mod +0 -3
- package/alpha/golang/newPkg/jsonvalidations/cancel.go +0 -1289
- package/alpha/golang/newPkg/jsonvalidations/confirm.go +0 -9121
- package/alpha/golang/newPkg/jsonvalidations/init.go +0 -4864
- package/alpha/golang/newPkg/jsonvalidations/issue.go +0 -4868
- package/alpha/golang/newPkg/jsonvalidations/on_cancel.go +0 -7111
- package/alpha/golang/newPkg/jsonvalidations/on_confirm.go +0 -8903
- package/alpha/golang/newPkg/jsonvalidations/on_init.go +0 -4445
- package/alpha/golang/newPkg/jsonvalidations/on_issue.go +0 -2828
- package/alpha/golang/newPkg/jsonvalidations/on_issue_status.go +0 -1938
- package/alpha/golang/newPkg/jsonvalidations/on_search.go +0 -3356
- package/alpha/golang/newPkg/jsonvalidations/on_status.go +0 -8129
- package/alpha/golang/newPkg/jsonvalidations/on_track.go +0 -1415
- package/alpha/golang/newPkg/jsonvalidations/on_update.go +0 -8700
- package/alpha/golang/newPkg/jsonvalidations/search.go +0 -3585
- package/alpha/golang/newPkg/jsonvalidations/status.go +0 -1073
- package/alpha/golang/newPkg/jsonvalidations/track.go +0 -1073
- package/alpha/golang/newPkg/jsonvalidations/update.go +0 -3012
- package/alpha/golang/newPkg/main-validator.go +0 -196
- package/alpha/golang/newPkg/main-validator_test.go +0 -165
- package/alpha/golang/newPkg/storageutils/api_save_utils.go +0 -83
- package/alpha/golang/newPkg/storageutils/cancel.go +0 -30
- package/alpha/golang/newPkg/storageutils/confirm.go +0 -30
- package/alpha/golang/newPkg/storageutils/index.go +0 -132
- package/alpha/golang/newPkg/storageutils/init.go +0 -30
- package/alpha/golang/newPkg/storageutils/issue.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_cancel.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_confirm.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_init.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_issue.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_issue_status.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_search.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_status.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_track.go +0 -30
- package/alpha/golang/newPkg/storageutils/on_update.go +0 -30
- package/alpha/golang/newPkg/storageutils/save_utils.go +0 -75
- package/alpha/golang/newPkg/storageutils/search.go +0 -30
- package/alpha/golang/newPkg/storageutils/status.go +0 -30
- package/alpha/golang/newPkg/storageutils/track.go +0 -30
- package/alpha/golang/newPkg/storageutils/update.go +0 -30
- package/alpha/golang/newPkg/validationutils/json_normalizer.go +0 -152
- package/alpha/golang/newPkg/validationutils/json_path_utils.go +0 -173
- package/alpha/golang/newPkg/validationutils/storage-interface.go +0 -107
- package/alpha/golang/newPkg/validationutils/test-config.go +0 -69
- package/alpha/golang/newPkg/validationutils/validation_utils.go +0 -429
- package/alpha/golang/page/index.html +0 -6137
- package/alpha/golang/page/style.css +0 -204
- package/alpha/golang/readme.md +0 -5939
- package/alpha/golang/validationpkg/go.mod +0 -3
- package/alpha/golang/validationpkg/jsonvalidations/cancel.go +0 -1289
- package/alpha/golang/validationpkg/jsonvalidations/confirm.go +0 -9121
- package/alpha/golang/validationpkg/jsonvalidations/init.go +0 -4864
- package/alpha/golang/validationpkg/jsonvalidations/issue.go +0 -4868
- package/alpha/golang/validationpkg/jsonvalidations/on_cancel.go +0 -7111
- package/alpha/golang/validationpkg/jsonvalidations/on_confirm.go +0 -8903
- package/alpha/golang/validationpkg/jsonvalidations/on_init.go +0 -4445
- package/alpha/golang/validationpkg/jsonvalidations/on_issue.go +0 -2828
- package/alpha/golang/validationpkg/jsonvalidations/on_issue_status.go +0 -1938
- package/alpha/golang/validationpkg/jsonvalidations/on_search.go +0 -3356
- package/alpha/golang/validationpkg/jsonvalidations/on_status.go +0 -8129
- package/alpha/golang/validationpkg/jsonvalidations/on_track.go +0 -1415
- package/alpha/golang/validationpkg/jsonvalidations/on_update.go +0 -8700
- package/alpha/golang/validationpkg/jsonvalidations/search.go +0 -3585
- package/alpha/golang/validationpkg/jsonvalidations/status.go +0 -1073
- package/alpha/golang/validationpkg/jsonvalidations/track.go +0 -1073
- package/alpha/golang/validationpkg/jsonvalidations/update.go +0 -3012
- package/alpha/golang/validationpkg/main-validator.go +0 -196
- package/alpha/golang/validationpkg/main-validator_test.go +0 -165
- package/alpha/golang/validationpkg/storageutils/api_save_utils.go +0 -83
- package/alpha/golang/validationpkg/storageutils/cancel.go +0 -30
- package/alpha/golang/validationpkg/storageutils/confirm.go +0 -30
- package/alpha/golang/validationpkg/storageutils/index.go +0 -132
- package/alpha/golang/validationpkg/storageutils/init.go +0 -30
- package/alpha/golang/validationpkg/storageutils/issue.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_cancel.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_confirm.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_init.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_issue.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_issue_status.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_search.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_status.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_track.go +0 -30
- package/alpha/golang/validationpkg/storageutils/on_update.go +0 -30
- package/alpha/golang/validationpkg/storageutils/save_utils.go +0 -75
- package/alpha/golang/validationpkg/storageutils/search.go +0 -30
- package/alpha/golang/validationpkg/storageutils/status.go +0 -30
- package/alpha/golang/validationpkg/storageutils/track.go +0 -30
- package/alpha/golang/validationpkg/storageutils/update.go +0 -30
- package/alpha/golang/validationpkg/validationutils/json_normalizer.go +0 -152
- package/alpha/golang/validationpkg/validationutils/json_path_utils.go +0 -173
- package/alpha/golang/validationpkg/validationutils/storage-interface.go +0 -107
- package/alpha/golang/validationpkg/validationutils/test-config.go +0 -69
- package/alpha/golang/validationpkg/validationutils/validation_utils.go +0 -429
- /package/alpha/{golang/validPaths.json → validPaths.json} +0 -0
package/alpha/table.zip
ADDED
|
Binary file
|
package/dist/bin/cli.js
CHANGED
|
@@ -124,6 +124,12 @@ function getSupportedLanguage(lang) {
|
|
|
124
124
|
return SupportedLanguages.Javascript;
|
|
125
125
|
case "go":
|
|
126
126
|
return SupportedLanguages.Golang;
|
|
127
|
+
case "md":
|
|
128
|
+
return SupportedLanguages.Markdown;
|
|
129
|
+
case "rag":
|
|
130
|
+
return SupportedLanguages.RAG;
|
|
131
|
+
case "rag_table":
|
|
132
|
+
return SupportedLanguages.RAG_TABLE;
|
|
127
133
|
default:
|
|
128
134
|
throw new Error(`Unsupported language: ${lang}. Supported languages are: ${getValidLanguageOptions()}`);
|
|
129
135
|
}
|
|
@@ -12,6 +12,9 @@ import { duplicateVariablesInChildren } from "../utils/config-utils/duplicateVar
|
|
|
12
12
|
import { PythonGenerator } from "./generators/python/py-generator.js";
|
|
13
13
|
import { JavascriptGenerator } from "./generators/javascript/js-generator.js";
|
|
14
14
|
import { GoGenerator } from "./generators/go/go-generator.js";
|
|
15
|
+
import { MarkdownDocGenerator } from "./generators/documentation/md-generator.js";
|
|
16
|
+
import { RagGenerator } from "./generators/rag/rag-generator.js";
|
|
17
|
+
import { RagTableGenerator } from "./generators/rag/rag-table-generator.js";
|
|
15
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
19
|
const __dirname = path.dirname(__filename);
|
|
17
20
|
const defaultConfig = {
|
|
@@ -97,6 +100,19 @@ export class ConfigCompiler {
|
|
|
97
100
|
goPkgName: goPackageName,
|
|
98
101
|
});
|
|
99
102
|
break;
|
|
103
|
+
case SupportedLanguages.Markdown:
|
|
104
|
+
await new MarkdownDocGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode();
|
|
105
|
+
break;
|
|
106
|
+
case SupportedLanguages.RAG:
|
|
107
|
+
await new RagGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
|
|
108
|
+
codeName: codeName,
|
|
109
|
+
});
|
|
110
|
+
break;
|
|
111
|
+
case SupportedLanguages.RAG_TABLE:
|
|
112
|
+
await new RagTableGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
|
|
113
|
+
codeName: codeName,
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
100
116
|
default:
|
|
101
117
|
throw new Error("Language not supported");
|
|
102
118
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
|
|
2
|
+
/**
|
|
3
|
+
* MarkdownDocGenerator — generates a readme.md and a styled index.html
|
|
4
|
+
* for a validation config, reusing RagTableGenerator for the table layout.
|
|
5
|
+
*
|
|
6
|
+
* Output:
|
|
7
|
+
* ./readme.md — concatenated GFM table markdown for all actions
|
|
8
|
+
* ./page/index.html — the same content rendered to HTML with a stylesheet
|
|
9
|
+
* ./page/style.css — stylesheet
|
|
10
|
+
*/
|
|
3
11
|
export declare class MarkdownDocGenerator extends CodeGenerator {
|
|
4
12
|
generateSessionDataCode(): Promise<void>;
|
|
5
13
|
generateUnitTestingCode(): Promise<void>;
|
|
6
14
|
generateValidationCode: () => Promise<void>;
|
|
7
|
-
generateCode: () => Promise<void>;
|
|
8
|
-
generateMarkdownForTest: (testObject: TestObject) => string;
|
|
9
|
-
generateHtmlCode: (markdownData: string) => Promise<string>;
|
|
15
|
+
generateCode: (_codeConfig?: CodeGeneratorProps) => Promise<void>;
|
|
10
16
|
}
|
|
@@ -1,55 +1,49 @@
|
|
|
1
|
-
import { ConfigSyntax
|
|
2
|
-
import { CodeGenerator } from "../classes/abstract-generator.js";
|
|
3
|
-
import { markdownMessageGenerator } from "./markdown-message-generator.js";
|
|
1
|
+
import { ConfigSyntax } from "../../../constants/syntax.js";
|
|
2
|
+
import { CodeGenerator, } from "../classes/abstract-generator.js";
|
|
4
3
|
import { writeFileWithFsExtra } from "../../../utils/fs-utils.js";
|
|
5
4
|
import { marked } from "marked";
|
|
6
5
|
import Mustache from "mustache";
|
|
7
6
|
import { readFileSync } from "fs";
|
|
8
7
|
import path from "path";
|
|
9
8
|
import { fileURLToPath } from "url";
|
|
10
|
-
import {
|
|
9
|
+
import { RagTableGenerator } from "../rag/rag-table-generator.js";
|
|
11
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
11
|
const __dirname = path.dirname(__filename);
|
|
12
|
+
/**
|
|
13
|
+
* MarkdownDocGenerator — generates a readme.md and a styled index.html
|
|
14
|
+
* for a validation config, reusing RagTableGenerator for the table layout.
|
|
15
|
+
*
|
|
16
|
+
* Output:
|
|
17
|
+
* ./readme.md — concatenated GFM table markdown for all actions
|
|
18
|
+
* ./page/index.html — the same content rendered to HTML with a stylesheet
|
|
19
|
+
* ./page/style.css — stylesheet
|
|
20
|
+
*/
|
|
13
21
|
export class MarkdownDocGenerator extends CodeGenerator {
|
|
14
22
|
constructor() {
|
|
15
23
|
super(...arguments);
|
|
16
24
|
this.generateValidationCode = async () => {
|
|
17
25
|
const testConfig = this.validationConfig[ConfigSyntax.Tests];
|
|
26
|
+
// Reuse RagTableGenerator's table builder — same config, same error codes
|
|
27
|
+
const tableGen = new RagTableGenerator(this.validationConfig, this.errorCodes, this.rootPath);
|
|
28
|
+
// Build one GFM table section per action and concatenate
|
|
18
29
|
let finalMarkdown = "";
|
|
19
|
-
for (const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
[TestObjectSyntax.Return]: testObjects,
|
|
24
|
-
};
|
|
25
|
-
const md = this.generateMarkdownForTest(betaConfig);
|
|
26
|
-
finalMarkdown += `\n\n${md}`;
|
|
30
|
+
for (const action of Object.keys(testConfig)) {
|
|
31
|
+
const section = tableGen.buildActionMarkdown(action, action, // use action name as codeName heading inside the doc
|
|
32
|
+
testConfig[action]);
|
|
33
|
+
finalMarkdown += `\n\n${section}`;
|
|
27
34
|
}
|
|
35
|
+
finalMarkdown = finalMarkdown.trimStart();
|
|
28
36
|
const cssData = readFileSync(path.resolve(__dirname, "./templates/style.css"), "utf-8");
|
|
29
37
|
const htmlTemplate = readFileSync(path.resolve(__dirname, "./templates/index.mustache"), "utf-8");
|
|
30
38
|
writeFileWithFsExtra(this.rootPath, "./readme.md", finalMarkdown);
|
|
31
39
|
writeFileWithFsExtra(this.rootPath, "./page/index.html", Mustache.render(htmlTemplate, {
|
|
32
|
-
content: await
|
|
40
|
+
content: await marked(finalMarkdown),
|
|
33
41
|
}));
|
|
34
42
|
writeFileWithFsExtra(this.rootPath, "./page/style.css", cssData);
|
|
35
43
|
};
|
|
36
|
-
this.generateCode = async () => {
|
|
44
|
+
this.generateCode = async (_codeConfig) => {
|
|
37
45
|
await this.generateValidationCode();
|
|
38
46
|
};
|
|
39
|
-
this.generateMarkdownForTest = (testObject) => {
|
|
40
|
-
const ret = testObject[TestObjectSyntax.Return];
|
|
41
|
-
if (typeof ret === "string") {
|
|
42
|
-
const skip = testObject[TestObjectSyntax.Continue];
|
|
43
|
-
return markdownMessageGenerator(ret, testObject, testObject[TestObjectSyntax.Name], skip ? [skip] : undefined);
|
|
44
|
-
}
|
|
45
|
-
const subMardowns = ret.map((r) => {
|
|
46
|
-
return this.generateMarkdownForTest(r);
|
|
47
|
-
});
|
|
48
|
-
return `- **${testObject[TestObjectSyntax.Name]}** : All the following sub conditions must pass as per the api requirement\n\n${addTabToMarkdown(subMardowns.join("\n\n"))}`;
|
|
49
|
-
};
|
|
50
|
-
this.generateHtmlCode = async (markdownData) => {
|
|
51
|
-
return await marked(markdownData);
|
|
52
|
-
};
|
|
53
47
|
}
|
|
54
48
|
generateSessionDataCode() {
|
|
55
49
|
throw new Error("Method not implemented.");
|
|
@@ -1,36 +1,172 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<link rel="stylesheet" href="./style.css">
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>API Validations</title>
|
|
7
|
+
<link rel="stylesheet" href="./style.css">
|
|
9
8
|
</head>
|
|
10
9
|
<body>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
|
|
11
|
+
<header class="top-bar">
|
|
12
|
+
<span class="logo">◼ ONDC Validations</span>
|
|
13
|
+
<div class="toolbar">
|
|
14
|
+
<div class="search-wrap">
|
|
15
|
+
<svg class="search-icon" viewBox="0 0 20 20" fill="none">
|
|
16
|
+
<circle cx="8.5" cy="8.5" r="5.5" stroke="currentColor" stroke-width="1.8"/>
|
|
17
|
+
<path d="M13 13l3.5 3.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
|
18
|
+
</svg>
|
|
19
|
+
<input id="search" type="text" placeholder="Search tests, descriptions, error codes…" autocomplete="off">
|
|
20
|
+
<button id="clear-search" class="clear-btn hidden">×</button>
|
|
14
21
|
</div>
|
|
15
|
-
|
|
22
|
+
<div class="filter-group" id="type-filter">
|
|
23
|
+
<button class="pill active" data-type="all">All</button>
|
|
24
|
+
<button class="pill" data-type="group">GRP - Groups</button>
|
|
25
|
+
<button class="pill" data-type="leaf">LF - Tests</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</header>
|
|
29
|
+
|
|
30
|
+
<div class="layout">
|
|
31
|
+
<nav id="sidebar">
|
|
32
|
+
<p class="nav-heading">Actions</p>
|
|
33
|
+
<!-- populated by JS -->
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<main id="content">
|
|
37
|
+
{{{content}}}
|
|
38
|
+
</main>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div id="match-count" class="hidden"></div>
|
|
42
|
+
|
|
16
43
|
<script>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
(function () {
|
|
45
|
+
|
|
46
|
+
/* ── 1. Strip YAML front-matter bleed-through ─────────────────────────
|
|
47
|
+
marked renders --- as <hr> and the key:value block as <h2> */
|
|
48
|
+
document.querySelectorAll('#content hr').forEach(hr => {
|
|
49
|
+
const sib = hr.nextElementSibling;
|
|
50
|
+
if (sib && sib.tagName === 'H2' &&
|
|
51
|
+
/action:|codeName:|numTests:|generated:/.test(sib.textContent)) {
|
|
52
|
+
const trailingHr = sib.nextElementSibling;
|
|
53
|
+
if (trailingHr && trailingHr.tagName === 'HR') trailingHr.remove();
|
|
54
|
+
sib.remove();
|
|
55
|
+
hr.remove();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/* ── 2. Build sidebar nav from h1 headings ────────────────────────── */
|
|
60
|
+
const nav = document.getElementById('sidebar');
|
|
61
|
+
document.querySelectorAll('#content h1').forEach((h1, i) => {
|
|
62
|
+
const id = 'section-' + i;
|
|
63
|
+
h1.id = id;
|
|
64
|
+
const link = document.createElement('a');
|
|
65
|
+
link.href = '#' + id;
|
|
66
|
+
const match = h1.textContent.match(/`([^`]+)`/);
|
|
67
|
+
link.textContent = match ? match[1] : h1.textContent.slice(0, 28);
|
|
68
|
+
link.addEventListener('click', () => {
|
|
69
|
+
nav.querySelectorAll('a').forEach(a => a.classList.remove('active'));
|
|
70
|
+
link.classList.add('active');
|
|
34
71
|
});
|
|
72
|
+
nav.appendChild(link);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/* ── 3. Tag every table body row with data attributes ─────────────── */
|
|
76
|
+
document.querySelectorAll('table tbody tr').forEach(row => {
|
|
77
|
+
const typeCell = row.cells[1];
|
|
78
|
+
if (!typeCell) return;
|
|
79
|
+
row.dataset.rowtype = typeCell.textContent.includes('GRP') ? 'group' : 'leaf';
|
|
80
|
+
row.dataset.text = row.textContent.toLowerCase();
|
|
81
|
+
/* cache original inner HTML of each cell for highlight restore */
|
|
82
|
+
Array.from(row.cells).forEach(td => { td.dataset.raw = td.innerHTML; });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/* ── 4. Shared filter function ────────────────────────────────────── */
|
|
86
|
+
const searchInput = document.getElementById('search');
|
|
87
|
+
const clearBtn = document.getElementById('clear-search');
|
|
88
|
+
const countEl = document.getElementById('match-count');
|
|
89
|
+
let activeType = 'all';
|
|
90
|
+
|
|
91
|
+
function escapeRe(s) {
|
|
92
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function applyFilters() {
|
|
96
|
+
const q = searchInput.value.trim().toLowerCase();
|
|
97
|
+
clearBtn.classList.toggle('hidden', q === '');
|
|
98
|
+
let visible = 0, total = 0;
|
|
99
|
+
|
|
100
|
+
document.querySelectorAll('table tbody tr').forEach(row => {
|
|
101
|
+
const typeOk = activeType === 'all' || row.dataset.rowtype === activeType;
|
|
102
|
+
const textOk = q === '' || (row.dataset.text || '').includes(q);
|
|
103
|
+
const show = typeOk && textOk;
|
|
104
|
+
row.style.display = show ? '' : 'none';
|
|
105
|
+
if (show) visible++;
|
|
106
|
+
total++;
|
|
107
|
+
|
|
108
|
+
/* inline search highlight */
|
|
109
|
+
Array.from(row.cells).forEach(td => {
|
|
110
|
+
if (!td.dataset.raw) return;
|
|
111
|
+
if (q === '') {
|
|
112
|
+
td.innerHTML = td.dataset.raw;
|
|
113
|
+
} else {
|
|
114
|
+
td.innerHTML = td.dataset.raw.replace(
|
|
115
|
+
new RegExp('(' + escapeRe(q) + ')', 'gi'),
|
|
116
|
+
'<mark>$1</mark>'
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const filtered = q !== '' || activeType !== 'all';
|
|
123
|
+
countEl.textContent = filtered ? visible + ' / ' + total + ' rows shown' : '';
|
|
124
|
+
countEl.classList.toggle('hidden', !filtered);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
searchInput.addEventListener('input', applyFilters);
|
|
128
|
+
clearBtn.addEventListener('click', () => {
|
|
129
|
+
searchInput.value = '';
|
|
130
|
+
applyFilters();
|
|
131
|
+
searchInput.focus();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/* ── 5. Type filter pills ─────────────────────────────────────────── */
|
|
135
|
+
document.querySelectorAll('#type-filter .pill').forEach(btn => {
|
|
136
|
+
btn.addEventListener('click', () => {
|
|
137
|
+
document.querySelectorAll('#type-filter .pill')
|
|
138
|
+
.forEach(b => b.classList.remove('active'));
|
|
139
|
+
btn.classList.add('active');
|
|
140
|
+
activeType = btn.dataset.type;
|
|
141
|
+
applyFilters();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/* ── 6. Keyboard shortcut: / to focus search ─────────────────────── */
|
|
146
|
+
document.addEventListener('keydown', e => {
|
|
147
|
+
if (e.key === '/' && document.activeElement !== searchInput) {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
searchInput.focus();
|
|
150
|
+
}
|
|
151
|
+
if (e.key === 'Escape') {
|
|
152
|
+
searchInput.value = '';
|
|
153
|
+
applyFilters();
|
|
154
|
+
searchInput.blur();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/* ── 7. Highlight active section on scroll ────────────────────────── */
|
|
159
|
+
const observer = new IntersectionObserver(entries => {
|
|
160
|
+
entries.forEach(e => {
|
|
161
|
+
if (e.isIntersecting) {
|
|
162
|
+
nav.querySelectorAll('a').forEach(a =>
|
|
163
|
+
a.classList.toggle('active', a.getAttribute('href') === '#' + e.target.id));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}, { threshold: 0.5 });
|
|
167
|
+
document.querySelectorAll('#content h1').forEach(h => observer.observe(h));
|
|
168
|
+
|
|
169
|
+
})();
|
|
35
170
|
</script>
|
|
171
|
+
</body>
|
|
36
172
|
</html>
|