@amcharts/amcharts5-mcp 1.1.9 → 1.3.2
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 +98 -0
- package/content/SKILL.md +62 -1
- package/content/gantt.md +3 -3
- package/content/stock.md +4 -3
- package/content/timeline.md +29 -25
- package/content/ui-elements.md +5 -3
- package/content/wordcloud.md +4 -4
- package/content/xy.md +12 -1
- package/extended/examples/maps/map-sankey-series.md +129 -0
- package/extended/reference/hierarchy.md +1 -0
- package/extended/reference/ihierarchysettings.md +1 -0
- package/extended/reference/imovingaveragedeviationsettings.md +2 -1
- package/extended/reference/imovingaveragesettings.md +2 -1
- package/extended/reference/iserpentinechartsettings.md +1 -0
- package/extended/reference/ispiralchartsettings.md +1 -0
- package/extended/reference/itreesettings.md +3 -0
- package/extended/reference/ivalueaxissettings.md +1 -0
- package/extended/reference/ivoronoitreemapsettings.md +2 -1
- package/extended/reference/movingaverage.md +2 -1
- package/extended/reference/movingaveragedeviation.md +2 -1
- package/extended/reference/tree.md +3 -0
- package/extended/reference/valueaxis.md +1 -0
- package/extended/reference/voronoitreemap.md +2 -1
- package/package.json +9 -1
- package/src/content-fs.js +74 -0
- package/src/content.js +214 -0
- package/src/index.js +10 -516
- package/src/tools.js +391 -0
|
@@ -24,7 +24,8 @@ Extends: ChartIndicator
|
|
|
24
24
|
|
|
25
25
|
- **decreasingColor** (`Color`) — Decreasing color.
|
|
26
26
|
- **increasingColor** (`Color`) — Increasing color.
|
|
27
|
-
- **
|
|
27
|
+
- **maType** (`"simple" | "weighted" | "exponential" | "dema" | "tema"`) — Default "simple" Type of the moving average. (Renamed from `type` in 5.18.0.)
|
|
28
|
+
- **type** (`"simple" | "weighted" | "exponential" | "dema" | "tema"`) — *Deprecated since 5.18.0 — use `maType` (still works).* Default "simple" Type of the moving average.
|
|
28
29
|
- **unit** (`"points" | "percent"`) — Default "points" How units are calculated.
|
|
29
30
|
|
|
30
31
|
## Properties
|
|
@@ -22,5 +22,8 @@ Extends: LinkedHierarchy
|
|
|
22
22
|
|
|
23
23
|
## Settings
|
|
24
24
|
|
|
25
|
+
- **clustered** (`undefined | false | true`) — Default false If set to true, uses a cluster (dendrogram) layout where all leaf nodes are placed at the same depth. @since 5.16.2
|
|
26
|
+
- **fitNodes** (`undefined | false | true`) — Default false If set to true, hidden nodes are excluded from the layout, giving more space to visible ones. @since 5.18.0
|
|
25
27
|
- **inversed** (`undefined | false | true`) — Default false If set to true, will flip the tree direction. @since 5.2.4
|
|
28
|
+
- **nodeSeparation** (`undefined | (a: DataItem, b: DataItem) => number`) — Custom node-separation function; receives two data items and returns a numeric spacing value. @since 5.16.2
|
|
26
29
|
- **orientation** (`"horizontal" | "vertical"`) — Default "vertical" Orientation of the diagram.
|
|
@@ -38,5 +38,6 @@ Extended by: DateAxis, DurationAxis
|
|
|
38
38
|
- **strictMinMax** (`undefined | false | true`) — Force axis scale to be precisely at values as set in min and/or max. In case min and/or max is not set, the axis will fix its scale to precise lowest and highest values available through all of the series attached to it. This effectively locks the axis from auto-zooming itself when chart is zoomed in. If you need to zoom to actual low/high values within currently visible scope, use strictMinMaxSelection instead. Use extraMin and extraMax to add extra "padding". Click here for more info
|
|
39
39
|
- **strictMinMaxSelection** (`undefined | false | true`) — Force axis to auto-zoom to exact lowest and highest values from attached series' data items within currently visible range. This is a good feature when your series is plotted from derivative values, like valueYChangeSelection as it helps to avoid frequent jumping of series to adjusted min and max of the axis. This will not work if strictMinMax is set to true (the axis will not zoom at all in this case). Use extraMin and extraMax to add extra "padding". @since 5.1.11
|
|
40
40
|
- **syncWithAxis** (`ValueAxis`) — This setting can be set to an instance of another ValueAxis. If set the grid of this axis will be synced with grid of the target axis. NOTE: this is not 100% guaranteed to work. In some rare cases perfect sync might not be possible. Click here for more info
|
|
41
|
+
- **syncZeros** (`undefined | false | true`) — Aligns the zero baseline across synced axes. Works only if `syncWithAxis` is set and zero is within the min/max of the axis. @since 5.16.2
|
|
41
42
|
- **tooltipNumberFormat** (`string | NumberFormatOptions`) — A numeric format used for numbers displayed in axis tooltip. Click here for more info
|
|
42
43
|
- **treatZeroAs** (`undefined | number`) — Treat zero values as some other value. Useful in situations where zero would result in error, i.e. logarithmic scale. Click here for more info
|
|
@@ -27,7 +27,8 @@ Extends: Hierarchy
|
|
|
27
27
|
- **cornerCount** (`undefined | number`) — Default 120 Number of corners when type is "polygon". 120 means the polygoon will look like a circle. NOTE: this setting is ignored if type="rectangle".
|
|
28
28
|
- **maxIterationCount** (`undefined | number`) — Default 100 Maximum allowed number of iterations when computing the layout. Computation is stopped when it number of iterations is reached, even if the convergenceRatio is not yet reached. Bigger number means finer results, but slower performance.
|
|
29
29
|
- **minWeightRatio** (`undefined | number`) — Default 0.005 Minimum weight ratio which allows computing the minimum allowed weight (= [maximum weight] * minWeightRatio). Setting very small minWeigtRatio might result flickering. NOTE: the nodes that have smaller weight will be scaled up and will not represent their true value correctly.
|
|
30
|
-
- **
|
|
30
|
+
- **shapeType** (`"rectangle" | "polygon"`) — Default "polygon" Type of the diagram's shape. (Renamed from `type` in 5.18.0.) Click here for more info
|
|
31
|
+
- **type** (`"rectangle" | "polygon"`) — *Deprecated since 5.18.0 — use `shapeType` (still works).* Default "polygon" Type of the diagram's shape.
|
|
31
32
|
|
|
32
33
|
## Properties
|
|
33
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amcharts/amcharts5-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"mcpName": "io.github.amcharts/amcharts5",
|
|
5
5
|
"description": "On-demand access to amCharts 5 docs, code examples, and API reference",
|
|
6
6
|
"type": "module",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"start": "node src/index.js",
|
|
20
20
|
"dev": "node --watch src/index.js",
|
|
21
|
+
"test": "node test.js",
|
|
22
|
+
"build:worker": "node scripts/build-worker-content.js",
|
|
23
|
+
"dev:worker": "npm run build:worker && wrangler dev",
|
|
24
|
+
"deploy": "npm run build:worker && wrangler deploy",
|
|
21
25
|
"prepublishOnly": "node -e \"const fs=require('fs');if(!fs.existsSync('content'))fs.mkdirSync('content');fs.cpSync('amcharts5-skill/amcharts5-skill/SKILL.md','content/SKILL.md');for(const f of fs.readdirSync('amcharts5-skill/amcharts5-skill/references').filter(x=>x.endsWith('.md')))fs.cpSync('amcharts5-skill/amcharts5-skill/references/'+f,'content/'+f)\"",
|
|
22
26
|
"postpublish": "node -e \"require('fs').rmSync('content',{recursive:true,force:true})\""
|
|
23
27
|
},
|
|
@@ -48,5 +52,9 @@
|
|
|
48
52
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
49
53
|
"jsdom": "^29.0.0",
|
|
50
54
|
"turndown": "^7.2.2"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"agents": "^0.16.2",
|
|
58
|
+
"wrangler": "^4.102.0"
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Node-only disk loader.
|
|
3
|
+
//
|
|
4
|
+
// Reads raw markdown off the filesystem into the { skillFiles, extendedFiles }
|
|
5
|
+
// shape consumed by buildContent(). Used by the stdio server (src/index.js) and
|
|
6
|
+
// by the Worker content build script (scripts/build-worker-content.js).
|
|
7
|
+
//
|
|
8
|
+
// NOTE: imports `fs` — must NOT be imported by the Worker bundle.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
12
|
+
import { join, dirname } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { buildContent } from "./content.js";
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const ROOT = join(__dirname, "..");
|
|
18
|
+
|
|
19
|
+
// Submodule path (local dev) or content/ fallback (npm install)
|
|
20
|
+
const SUBMODULE_DIR = join(ROOT, "amcharts5-skill", "amcharts5-skill");
|
|
21
|
+
const CONTENT_DIR = join(ROOT, "content");
|
|
22
|
+
const SKILL_DIR = existsSync(SUBMODULE_DIR) ? SUBMODULE_DIR : CONTENT_DIR;
|
|
23
|
+
const REFERENCES_DIR = existsSync(join(SKILL_DIR, "references"))
|
|
24
|
+
? join(SKILL_DIR, "references")
|
|
25
|
+
: SKILL_DIR;
|
|
26
|
+
const EXTENDED_DIR = join(ROOT, "extended");
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Read all skill + extended markdown into the raw { skillFiles, extendedFiles }
|
|
30
|
+
* shape that buildContent() expects.
|
|
31
|
+
*/
|
|
32
|
+
export function readRawFiles() {
|
|
33
|
+
const skillFiles = {};
|
|
34
|
+
const extendedFiles = {};
|
|
35
|
+
|
|
36
|
+
// Core SKILL.md
|
|
37
|
+
const skillPath = join(SKILL_DIR, "SKILL.md");
|
|
38
|
+
if (existsSync(skillPath)) {
|
|
39
|
+
skillFiles["SKILL"] = readFileSync(skillPath, "utf-8");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Reference files (skip SKILL.md if it lives in the same dir as references)
|
|
43
|
+
if (existsSync(REFERENCES_DIR)) {
|
|
44
|
+
for (const file of readdirSync(REFERENCES_DIR)) {
|
|
45
|
+
if (!file.endsWith(".md") || file === "SKILL.md") continue;
|
|
46
|
+
skillFiles[file.replace(".md", "")] = readFileSync(join(REFERENCES_DIR, file), "utf-8");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extended docs + examples (recursive)
|
|
51
|
+
walkExtended(EXTENDED_DIR, "", extendedFiles);
|
|
52
|
+
|
|
53
|
+
return { skillFiles, extendedFiles };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function walkExtended(dir, prefix, out) {
|
|
57
|
+
if (!existsSync(dir)) return;
|
|
58
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
59
|
+
const fullPath = join(dir, entry.name);
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
walkExtended(fullPath, prefix ? `${prefix}/${entry.name}` : entry.name, out);
|
|
62
|
+
} else if (entry.name.endsWith(".md")) {
|
|
63
|
+
const key = prefix
|
|
64
|
+
? `${prefix}/${entry.name.replace(".md", "")}`
|
|
65
|
+
: entry.name.replace(".md", "");
|
|
66
|
+
out[key] = readFileSync(fullPath, "utf-8");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Read from disk and build the in-memory content structures. */
|
|
72
|
+
export function loadContentFromDisk() {
|
|
73
|
+
return buildContent(readRawFiles());
|
|
74
|
+
}
|
package/src/content.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared, transport-agnostic content layer.
|
|
3
|
+
//
|
|
4
|
+
// Pure logic only — NO filesystem, NO Node built-ins — so this module can be
|
|
5
|
+
// bundled into the Cloudflare Worker as-is. The stdio server reads files from
|
|
6
|
+
// disk (see content-fs.js) and the Worker imports a pre-generated bundle (see
|
|
7
|
+
// scripts/build-worker-content.js); both feed the same buildContent() here so
|
|
8
|
+
// the two deployments behave identically.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** Chart type aliases → reference file mapping */
|
|
12
|
+
export const CHART_TYPE_MAP = {
|
|
13
|
+
// XY family
|
|
14
|
+
"xy": "xy", "line": "xy", "area": "xy", "bar": "xy", "column": "xy",
|
|
15
|
+
"candlestick": "xy", "ohlc": "xy", "scatter": "xy", "bubble": "xy",
|
|
16
|
+
"stacked": "xy", "stacked-bar": "xy", "stacked-column": "xy",
|
|
17
|
+
"stacked-area": "xy", "range": "xy", "waterfall": "xy",
|
|
18
|
+
// Pie family
|
|
19
|
+
"pie": "pie", "donut": "pie", "funnel": "pie", "pyramid": "pie",
|
|
20
|
+
"semi-circle": "pie", "pictorial": "pie", "sliced": "pie",
|
|
21
|
+
// Map
|
|
22
|
+
"map": "map", "choropleth": "map", "geo": "map", "geographic": "map",
|
|
23
|
+
"world": "map", "country": "map", "bubble-map": "map", "point-map": "map",
|
|
24
|
+
// Hierarchy
|
|
25
|
+
"hierarchy": "hierarchy", "treemap": "hierarchy", "sunburst": "hierarchy",
|
|
26
|
+
"force-directed": "hierarchy", "pack": "hierarchy", "partition": "hierarchy",
|
|
27
|
+
"tree": "hierarchy", "org-chart": "hierarchy",
|
|
28
|
+
// Flow
|
|
29
|
+
"flow": "flow", "sankey": "flow", "chord": "flow", "arc": "flow",
|
|
30
|
+
"alluvial": "flow",
|
|
31
|
+
// Radar
|
|
32
|
+
"radar": "radar", "spider": "radar", "polar": "radar", "gauge": "radar",
|
|
33
|
+
"speedometer": "radar", "meter": "radar",
|
|
34
|
+
// Stock
|
|
35
|
+
"stock": "stock", "financial": "stock", "trading": "stock",
|
|
36
|
+
// Timeline
|
|
37
|
+
"timeline": "timeline", "serpentine": "timeline", "spiral": "timeline",
|
|
38
|
+
"curve": "timeline",
|
|
39
|
+
// Gantt
|
|
40
|
+
"gantt": "gantt", "project-timeline": "gantt",
|
|
41
|
+
// Word cloud
|
|
42
|
+
"wordcloud": "wordcloud", "word-cloud": "wordcloud", "tag-cloud": "wordcloud",
|
|
43
|
+
// Venn
|
|
44
|
+
"venn": "venn",
|
|
45
|
+
// UI
|
|
46
|
+
"ui": "ui-elements", "ui-elements": "ui-elements", "button": "ui-elements",
|
|
47
|
+
"slider": "ui-elements", "controls": "ui-elements",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function parseSections(content) {
|
|
51
|
+
const sections = [];
|
|
52
|
+
const lines = content.split("\n");
|
|
53
|
+
let currentHeading = "";
|
|
54
|
+
let currentBody = [];
|
|
55
|
+
|
|
56
|
+
for (const line of lines) {
|
|
57
|
+
const headingMatch = line.match(/^(#{1,3})\s+(.+)/);
|
|
58
|
+
if (headingMatch) {
|
|
59
|
+
const level = headingMatch[1].length;
|
|
60
|
+
const title = headingMatch[2].trim();
|
|
61
|
+
if (level <= 2) {
|
|
62
|
+
// Major section boundary (# or ##)
|
|
63
|
+
if (currentHeading || currentBody.length > 0) {
|
|
64
|
+
sections.push({ heading: currentHeading, body: currentBody.join("\n") });
|
|
65
|
+
}
|
|
66
|
+
currentHeading = title;
|
|
67
|
+
currentBody = [];
|
|
68
|
+
} else {
|
|
69
|
+
// Sub-heading (###) — include in current section body
|
|
70
|
+
currentBody.push(line);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
currentBody.push(line);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (currentHeading || currentBody.length > 0) {
|
|
77
|
+
sections.push({ heading: currentHeading, body: currentBody.join("\n") });
|
|
78
|
+
}
|
|
79
|
+
return sections;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Import sections carry two ### sub-blocks: one for ES modules, one for CDN /
|
|
84
|
+
* script tags. Return only the requested one (esm | html), or "" if not found.
|
|
85
|
+
*/
|
|
86
|
+
export function extractImportBlock(body, format) {
|
|
87
|
+
const lines = body.split("\n");
|
|
88
|
+
const wantEsm = format === "esm";
|
|
89
|
+
let capturing = false;
|
|
90
|
+
let captured = [];
|
|
91
|
+
let sawSub = false;
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
const sub = line.match(/^###\s+(.+)/);
|
|
94
|
+
if (sub) {
|
|
95
|
+
sawSub = true;
|
|
96
|
+
const h = sub[1].toLowerCase();
|
|
97
|
+
const isEsm = /es module|esm|typescript|import/.test(h);
|
|
98
|
+
// Note: "typescript" contains "script", so match "script tag"/"cdn", not bare "script".
|
|
99
|
+
const isCdn = /cdn|script tag|<script/.test(h);
|
|
100
|
+
capturing = wantEsm ? isEsm : isCdn;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (capturing) captured.push(line);
|
|
104
|
+
}
|
|
105
|
+
if (!sawSub) return ""; // no sub-blocks → caller falls back to full body
|
|
106
|
+
return captured.join("\n").trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Build the in-memory content structures from raw markdown files.
|
|
111
|
+
*
|
|
112
|
+
* @param {{ skillFiles: Record<string,string>, extendedFiles: Record<string,string> }} raw
|
|
113
|
+
* skillFiles — name (no .md) → raw markdown for SKILL.md + references/*.md
|
|
114
|
+
* extendedFiles — path key (no .md) → raw markdown for everything under extended/
|
|
115
|
+
* @returns {{ docs: Map, extendedDocs: Map, examples: Map }}
|
|
116
|
+
*/
|
|
117
|
+
export function buildContent({ skillFiles = {}, extendedFiles = {} } = {}) {
|
|
118
|
+
/** @type {Map<string, {title: string, content: string, sections: {heading: string, body: string}[]}>} */
|
|
119
|
+
const docs = new Map();
|
|
120
|
+
/** @type {Map<string, {title: string, content: string, sections: {heading: string, body: string}[], path: string}>} */
|
|
121
|
+
const extendedDocs = new Map();
|
|
122
|
+
/** @type {Map<string, {title: string, category: string, content: string, source: string}>} */
|
|
123
|
+
const examples = new Map();
|
|
124
|
+
|
|
125
|
+
// Skill docs (SKILL.md + references/*.md)
|
|
126
|
+
for (const [name, content] of Object.entries(skillFiles)) {
|
|
127
|
+
const titleMatch = content.match(/^#\s+(.+)/m);
|
|
128
|
+
const title = titleMatch ? titleMatch[1] : name;
|
|
129
|
+
docs.set(name, { title, content, sections: parseSections(content) });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Extended docs + examples (frontmatter-tagged markdown)
|
|
133
|
+
for (const [key, content] of Object.entries(extendedFiles)) {
|
|
134
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
135
|
+
const body = fmMatch ? fmMatch[2].trim() : content;
|
|
136
|
+
const fm = fmMatch ? fmMatch[1] : "";
|
|
137
|
+
const titleMatch = fm.match(/title:\s*"(.+?)"/);
|
|
138
|
+
const categoryMatch = fm.match(/category:\s*"(.+?)"/);
|
|
139
|
+
const sourceMatch = fm.match(/source:\s*"(.+?)"/);
|
|
140
|
+
const fallbackTitle = key.split("/").pop();
|
|
141
|
+
const title = titleMatch ? titleMatch[1] : fallbackTitle;
|
|
142
|
+
|
|
143
|
+
if (key.startsWith("examples/")) {
|
|
144
|
+
examples.set(key, {
|
|
145
|
+
title,
|
|
146
|
+
category: categoryMatch ? categoryMatch[1] : "",
|
|
147
|
+
content: body,
|
|
148
|
+
source: sourceMatch ? sourceMatch[1] : "",
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
extendedDocs.set(key, {
|
|
152
|
+
title,
|
|
153
|
+
content: body,
|
|
154
|
+
sections: parseSections(body),
|
|
155
|
+
path: key,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { docs, extendedDocs, examples };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Keyword search over the skill docs and (optionally) the extended docs.
|
|
165
|
+
* @param {{docs: Map, extendedDocs: Map}} content
|
|
166
|
+
*/
|
|
167
|
+
export function searchDocs(content, query, maxResults = 10, scope = "skill") {
|
|
168
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
169
|
+
const results = [];
|
|
170
|
+
|
|
171
|
+
// Skill docs (SKILL.md + references/*.md)
|
|
172
|
+
for (const [name, doc] of content.docs) {
|
|
173
|
+
for (const section of doc.sections) {
|
|
174
|
+
const text = (section.heading + " " + section.body).toLowerCase();
|
|
175
|
+
const score = terms.reduce((s, t) => s + (text.includes(t) ? 1 : 0), 0);
|
|
176
|
+
if (score > 0) {
|
|
177
|
+
results.push({
|
|
178
|
+
file: name,
|
|
179
|
+
source: "skill",
|
|
180
|
+
docTitle: doc.title,
|
|
181
|
+
heading: section.heading,
|
|
182
|
+
body: section.body.trim(),
|
|
183
|
+
// Nudge skill results slightly ahead of extended on ties — the skill
|
|
184
|
+
// is the curated layer; extended is the exhaustive API reference.
|
|
185
|
+
score: score + 0.1,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Extended docs (charts/, concepts/, getting-started/, reference/<class>) —
|
|
192
|
+
// this is where the per-class API + defaults live. Only scanned when scope=all.
|
|
193
|
+
if (scope === "all") {
|
|
194
|
+
for (const [key, doc] of content.extendedDocs) {
|
|
195
|
+
for (const section of doc.sections) {
|
|
196
|
+
const text = (section.heading + " " + section.body).toLowerCase();
|
|
197
|
+
const score = terms.reduce((s, t) => s + (text.includes(t) ? 1 : 0), 0);
|
|
198
|
+
if (score > 0) {
|
|
199
|
+
results.push({
|
|
200
|
+
file: key,
|
|
201
|
+
source: "extended",
|
|
202
|
+
docTitle: doc.title,
|
|
203
|
+
heading: section.heading,
|
|
204
|
+
body: section.body.trim(),
|
|
205
|
+
score,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
results.sort((a, b) => b.score - a.score);
|
|
213
|
+
return results.slice(0, maxResults);
|
|
214
|
+
}
|