fix(plan-canvas): stop dropping list items when a block's first item is over-indented (#2501)

* fix(plan-canvas): stop dropping list items when a block's first item is over-indented

* fix: address greptile findings for PR #2501 - list-type detection and outdent nesting

- plan-canvas markdown: fix nested list rendering where outdented runs (indent
  6→4) create duplicate sibling UL blocks instead of sharing parent (#2501)
- transcript-context: add LARGE_WINDOW_NATIVE_MODEL_IDS array for models whose
  default context window is 1M but do NOT carry the [1m] marker (fixes #2497)
- Add test coverage for transcript-context and shell-substitution modules
- Add test coverage for project-detect module (#2498)

* fix(plan-canvas): handle outdented list runs

* fix(plan-canvas): start a new list when marker type changes at the same indent

CommonMark treats a marker-type change (bullet to ordered or back) at
the same indentation as the start of a new list. buildList previously
absorbed the run into the current list, so mixed runs rendered under a
single wrong tag. Stop the run on a tag change and let buildListBlock
render the next run as a sibling list with its own tag.
This commit is contained in:
黄云龙 2026-07-18 05:10:58 +08:00 committed by GitHub
parent fb98726d0a
commit 5d68ef3617
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 2 deletions

View file

@ -132,8 +132,12 @@ function renderListItem(text) {
return '<li>' + renderInline(text) + '</li>';
}
function listTag(marker) {
return /^\d/.test(marker) ? 'ol' : 'ul';
}
function buildList(items, start, indent) {
const tag = /^\d/.test(items[start].marker) ? 'ol' : 'ul';
const tag = listTag(items[start].marker);
const parts = [];
let i = start;
while (i < items.length && items[i].indent >= indent) {
@ -148,6 +152,10 @@ function buildList(items, start, indent) {
}
i = nested.end;
} else {
// A marker-type change at the same indent starts a new list
// (CommonMark); stop here so the caller renders the next run with
// its own tag instead of absorbing it into this one.
if (listTag(items[i].marker) !== tag) break;
parts.push(renderListItem(items[i].text));
i += 1;
}
@ -155,6 +163,17 @@ function buildList(items, start, indent) {
return { html: '<' + tag + '>\n' + parts.join('\n') + '\n</' + tag + '>', end: i };
}
function buildListBlock(items) {
let lists = [];
let i = 0;
while (i < items.length) {
const list = buildList(items, i, items[i].indent);
lists = [...lists, list.html];
i = list.end;
}
return lists.join('\n');
}
function startsBlock(line, nextLine) {
return /^```/.test(line) ||
/^#{1,6}\s/.test(line) ||
@ -257,7 +276,10 @@ function renderMarkdown(text) {
items.push({ indent: m[1].length, marker: m[2], text: m[3] });
i += 1;
}
out.push(buildList(items, 0, items[0].indent).html);
// An outdent below the first item's indentation ends that list. Render
// the remaining run as a sibling list so malformed indentation cannot
// silently drop content or create an empty parent item.
out.push(buildListBlock(items));
continue;
}