reasonix 0.16.0 → 0.17.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 +55 -16
- package/README.zh-CN.md +50 -11
- package/dashboard/app.css +148 -1
- package/dashboard/app.js +226 -0
- package/dist/cli/index.js +538 -281
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.js +90 -64
- package/dist/index.js.map +1 -1
- package/package.json +113 -110
package/dashboard/app.js
CHANGED
|
@@ -3265,10 +3265,212 @@ function SemanticPanel() {
|
|
|
3265
3265
|
<button disabled=${busy || running || !ready} onClick=${() => start(true)}>Rebuild (wipe + full)</button>
|
|
3266
3266
|
<button disabled=${busy || !running} onClick=${stop}>Stop</button>
|
|
3267
3267
|
</div>
|
|
3268
|
+
|
|
3269
|
+
<${SemanticExcludesCard} />
|
|
3270
|
+
</div>
|
|
3271
|
+
`;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
function SemanticExcludesCard() {
|
|
3275
|
+
const [data, setData] = useState(null);
|
|
3276
|
+
const [draft, setDraft] = useState(null);
|
|
3277
|
+
const [preview, setPreview] = useState(null);
|
|
3278
|
+
const [busy, setBusy] = useState(false);
|
|
3279
|
+
const [error, setError] = useState(null);
|
|
3280
|
+
const [info, setInfo] = useState(null);
|
|
3281
|
+
const [open, setOpen] = useState(false);
|
|
3282
|
+
|
|
3283
|
+
const load = useCallback(async () => {
|
|
3284
|
+
try {
|
|
3285
|
+
const r = await api("/index-config");
|
|
3286
|
+
setData(r);
|
|
3287
|
+
setDraft(toDraft(r.resolved));
|
|
3288
|
+
} catch (err) {
|
|
3289
|
+
setError(err.message);
|
|
3290
|
+
}
|
|
3291
|
+
}, []);
|
|
3292
|
+
|
|
3293
|
+
useEffect(() => {
|
|
3294
|
+
if (open && !data) load();
|
|
3295
|
+
}, [open, data, load]);
|
|
3296
|
+
|
|
3297
|
+
const reset = useCallback(() => {
|
|
3298
|
+
if (data) setDraft(toDraft(data.defaults));
|
|
3299
|
+
setPreview(null);
|
|
3300
|
+
}, [data]);
|
|
3301
|
+
|
|
3302
|
+
const save = useCallback(async () => {
|
|
3303
|
+
if (!draft) return;
|
|
3304
|
+
setBusy(true);
|
|
3305
|
+
setError(null);
|
|
3306
|
+
setInfo(null);
|
|
3307
|
+
try {
|
|
3308
|
+
const payload = fromDraft(draft);
|
|
3309
|
+
const r = await api("/index-config", { method: "POST", body: payload });
|
|
3310
|
+
setInfo(`saved · ${r.changed.length || 0} fields updated · re-run index to apply`);
|
|
3311
|
+
await load();
|
|
3312
|
+
} catch (err) {
|
|
3313
|
+
setError(err.message);
|
|
3314
|
+
} finally {
|
|
3315
|
+
setBusy(false);
|
|
3316
|
+
}
|
|
3317
|
+
}, [draft, load]);
|
|
3318
|
+
|
|
3319
|
+
const runPreview = useCallback(async () => {
|
|
3320
|
+
if (!draft) return;
|
|
3321
|
+
setBusy(true);
|
|
3322
|
+
setError(null);
|
|
3323
|
+
setInfo("running dry walk against project root…");
|
|
3324
|
+
try {
|
|
3325
|
+
const payload = fromDraft(draft);
|
|
3326
|
+
const r = await api("/index-config/preview", { method: "POST", body: payload });
|
|
3327
|
+
setPreview(r);
|
|
3328
|
+
setInfo(null);
|
|
3329
|
+
} catch (err) {
|
|
3330
|
+
setError(err.message);
|
|
3331
|
+
setInfo(null);
|
|
3332
|
+
} finally {
|
|
3333
|
+
setBusy(false);
|
|
3334
|
+
}
|
|
3335
|
+
}, [draft]);
|
|
3336
|
+
|
|
3337
|
+
return html`
|
|
3338
|
+
<div class="excludes-toggle" onClick=${() => setOpen(!open)}>
|
|
3339
|
+
<span class="caret">${open ? "▼" : "▶"}</span>
|
|
3340
|
+
<span class="label">Excludes</span>
|
|
3341
|
+
<span class="hint">config-driven skip rules applied during indexing</span>
|
|
3268
3342
|
</div>
|
|
3343
|
+
${
|
|
3344
|
+
!open
|
|
3345
|
+
? null
|
|
3346
|
+
: !draft
|
|
3347
|
+
? html`<div class="empty">loading…</div>`
|
|
3348
|
+
: html`
|
|
3349
|
+
<div class="card excludes-card">
|
|
3350
|
+
${info ? html`<div class="notice">${info}</div>` : null}
|
|
3351
|
+
${error ? html`<div class="notice err">${error}</div>` : null}
|
|
3352
|
+
<div class="lead">
|
|
3353
|
+
One value per line. Dirs / files match by basename. Patterns use picomatch syntax (e.g. <code>**/*.generated.ts</code>, <code>vendor/**</code>, <code>!keep-me</code>).
|
|
3354
|
+
</div>
|
|
3355
|
+
<div class="excludes-grid">
|
|
3356
|
+
<${ExcludesField} label="Exclude dirs" value=${draft.excludeDirs} onChange=${(v) => setDraft({ ...draft, excludeDirs: v })} />
|
|
3357
|
+
<${ExcludesField} label="Exclude files" value=${draft.excludeFiles} onChange=${(v) => setDraft({ ...draft, excludeFiles: v })} />
|
|
3358
|
+
<${ExcludesField} label="Exclude extensions" value=${draft.excludeExts} onChange=${(v) => setDraft({ ...draft, excludeExts: v })} />
|
|
3359
|
+
<${ExcludesField} label="Exclude patterns (glob)" value=${draft.excludePatterns} onChange=${(v) => setDraft({ ...draft, excludePatterns: v })} />
|
|
3360
|
+
</div>
|
|
3361
|
+
<div class="excludes-options">
|
|
3362
|
+
<label>
|
|
3363
|
+
<input type="checkbox" checked=${draft.respectGitignore} onChange=${(e) => setDraft({ ...draft, respectGitignore: e.target.checked })} />
|
|
3364
|
+
Respect <code>.gitignore</code>
|
|
3365
|
+
</label>
|
|
3366
|
+
<label>
|
|
3367
|
+
Max file size
|
|
3368
|
+
<input type="number" min="1024" step="1024" value=${draft.maxFileBytes} onChange=${(e) => setDraft({ ...draft, maxFileBytes: Number(e.target.value) || 0 })} />
|
|
3369
|
+
<span class="muted">bytes</span>
|
|
3370
|
+
</label>
|
|
3371
|
+
</div>
|
|
3372
|
+
<div class="excludes-actions">
|
|
3373
|
+
<button class="primary" disabled=${busy} onClick=${save}>Save</button>
|
|
3374
|
+
<button disabled=${busy} onClick=${runPreview}>Preview (dry-walk)</button>
|
|
3375
|
+
<button disabled=${busy} onClick=${reset}>Reset to defaults</button>
|
|
3376
|
+
</div>
|
|
3377
|
+
${preview ? html`<${ExcludesPreview} preview=${preview} />` : null}
|
|
3378
|
+
</div>
|
|
3379
|
+
`
|
|
3380
|
+
}
|
|
3269
3381
|
`;
|
|
3270
3382
|
}
|
|
3271
3383
|
|
|
3384
|
+
function ExcludesPreview({ preview }) {
|
|
3385
|
+
const buckets = preview.skipBuckets || {};
|
|
3386
|
+
const samples = preview.skipSamples || {};
|
|
3387
|
+
const totalSkipped = Object.values(buckets).reduce((a, b) => a + (b || 0), 0);
|
|
3388
|
+
const reasons = [
|
|
3389
|
+
"gitignore",
|
|
3390
|
+
"pattern",
|
|
3391
|
+
"defaultDir",
|
|
3392
|
+
"defaultFile",
|
|
3393
|
+
"binaryExt",
|
|
3394
|
+
"binaryContent",
|
|
3395
|
+
"tooLarge",
|
|
3396
|
+
"readError",
|
|
3397
|
+
].filter((k) => (buckets[k] || 0) > 0);
|
|
3398
|
+
return html`
|
|
3399
|
+
<div class="excludes-preview">
|
|
3400
|
+
<div class="summary">
|
|
3401
|
+
Preview — would index <strong>${preview.filesIncluded}</strong> file(s), skip <strong>${totalSkipped}</strong>
|
|
3402
|
+
</div>
|
|
3403
|
+
${
|
|
3404
|
+
reasons.length === 0
|
|
3405
|
+
? html`<div class="muted">nothing skipped — all walked files would be indexed.</div>`
|
|
3406
|
+
: reasons.map(
|
|
3407
|
+
(r) => html`
|
|
3408
|
+
<details>
|
|
3409
|
+
<summary><strong>${r}: ${buckets[r]}</strong></summary>
|
|
3410
|
+
<ul>
|
|
3411
|
+
${(samples[r] || []).map((p) => html`<li><code>${p}</code></li>`)}
|
|
3412
|
+
${
|
|
3413
|
+
(buckets[r] || 0) > (samples[r] || []).length
|
|
3414
|
+
? html`<li class="muted">…${buckets[r] - samples[r].length} more</li>`
|
|
3415
|
+
: null
|
|
3416
|
+
}
|
|
3417
|
+
</ul>
|
|
3418
|
+
</details>
|
|
3419
|
+
`,
|
|
3420
|
+
)
|
|
3421
|
+
}
|
|
3422
|
+
${
|
|
3423
|
+
preview.sampleIncluded?.length
|
|
3424
|
+
? html`
|
|
3425
|
+
<details>
|
|
3426
|
+
<summary>first ${preview.sampleIncluded.length} included file(s)</summary>
|
|
3427
|
+
<ul>
|
|
3428
|
+
${preview.sampleIncluded.map((p) => html`<li><code>${p}</code></li>`)}
|
|
3429
|
+
</ul>
|
|
3430
|
+
</details>
|
|
3431
|
+
`
|
|
3432
|
+
: null
|
|
3433
|
+
}
|
|
3434
|
+
</div>
|
|
3435
|
+
`;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
function ExcludesField({ label, value, onChange }) {
|
|
3439
|
+
return html`
|
|
3440
|
+
<div class="excludes-field">
|
|
3441
|
+
<label>${label}</label>
|
|
3442
|
+
<textarea rows="5" value=${value} onChange=${(e) => onChange(e.target.value)}></textarea>
|
|
3443
|
+
</div>
|
|
3444
|
+
`;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
function toDraft(c) {
|
|
3448
|
+
return {
|
|
3449
|
+
excludeDirs: (c.excludeDirs ?? []).join("\n"),
|
|
3450
|
+
excludeFiles: (c.excludeFiles ?? []).join("\n"),
|
|
3451
|
+
excludeExts: (c.excludeExts ?? []).join("\n"),
|
|
3452
|
+
excludePatterns: (c.excludePatterns ?? []).join("\n"),
|
|
3453
|
+
respectGitignore: c.respectGitignore !== false,
|
|
3454
|
+
maxFileBytes: c.maxFileBytes ?? 262144,
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
function fromDraft(d) {
|
|
3459
|
+
const lines = (s) =>
|
|
3460
|
+
s
|
|
3461
|
+
.split(/\r?\n/)
|
|
3462
|
+
.map((x) => x.trim())
|
|
3463
|
+
.filter((x) => x.length > 0);
|
|
3464
|
+
return {
|
|
3465
|
+
excludeDirs: lines(d.excludeDirs),
|
|
3466
|
+
excludeFiles: lines(d.excludeFiles),
|
|
3467
|
+
excludeExts: lines(d.excludeExts),
|
|
3468
|
+
excludePatterns: lines(d.excludePatterns),
|
|
3469
|
+
respectGitignore: !!d.respectGitignore,
|
|
3470
|
+
maxFileBytes: d.maxFileBytes,
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3272
3474
|
function SemanticJobView({ job, running }) {
|
|
3273
3475
|
const phaseLabel =
|
|
3274
3476
|
{
|
|
@@ -3321,10 +3523,34 @@ function SemanticJobView({ job, running }) {
|
|
|
3321
3523
|
} · ${(job.result.durationMs / 1000).toFixed(1)}s</div>`
|
|
3322
3524
|
: null
|
|
3323
3525
|
}
|
|
3526
|
+
${
|
|
3527
|
+
job.result?.skipBuckets
|
|
3528
|
+
? html`<${SkipBucketsView} buckets=${job.result.skipBuckets} />`
|
|
3529
|
+
: null
|
|
3530
|
+
}
|
|
3324
3531
|
</div>
|
|
3325
3532
|
`;
|
|
3326
3533
|
}
|
|
3327
3534
|
|
|
3535
|
+
function SkipBucketsView({ buckets }) {
|
|
3536
|
+
const order = [
|
|
3537
|
+
["gitignore", "gitignore"],
|
|
3538
|
+
["pattern", "pattern"],
|
|
3539
|
+
["defaultDir", "defaultDir"],
|
|
3540
|
+
["defaultFile", "defaultFile"],
|
|
3541
|
+
["binaryExt", "binaryExt"],
|
|
3542
|
+
["binaryContent", "binaryContent"],
|
|
3543
|
+
["tooLarge", "tooLarge"],
|
|
3544
|
+
["readError", "readError"],
|
|
3545
|
+
];
|
|
3546
|
+
const total = order.reduce((a, [k]) => a + (buckets[k] || 0), 0);
|
|
3547
|
+
if (total === 0) return null;
|
|
3548
|
+
const parts = order
|
|
3549
|
+
.filter(([k]) => (buckets[k] || 0) > 0)
|
|
3550
|
+
.map(([k, label]) => `${label}: ${buckets[k]}`);
|
|
3551
|
+
return html`<div><span class="kv-key">skipped</span>${total} files <span class="muted">(${parts.join(", ")})</span></div>`;
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3328
3554
|
function McpPanel() {
|
|
3329
3555
|
const [data, setData] = useState(null);
|
|
3330
3556
|
const [specs, setSpecs] = useState(null);
|