@jini-ai/vibecoding 0.4.1 → 0.4.3
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/CHANGELOG.md +4 -0
- package/dist/html/node/parse5-region-parser.js +39 -34
- package/dist/react/session.js +9 -14
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -108,14 +108,41 @@ function* walkElements(node) {
|
|
|
108
108
|
for (const child of children)
|
|
109
109
|
yield* walkElements(child);
|
|
110
110
|
}
|
|
111
|
-
/** Fresh `<div>` fragment-context element — see this module's doc for why `<div>`, not the default. */
|
|
112
|
-
function divFragmentContext() {
|
|
113
|
-
return defaultTreeAdapter.createElement("div", html.NS.HTML, []);
|
|
114
|
-
}
|
|
115
111
|
/** A parse-error entry, phrased for the model rather than left as parse5's bare kebab-case code. */
|
|
116
112
|
function describeParseError(error) {
|
|
117
113
|
return `${error.code.replace(/-/g, " ")} near character ${error.startOffset}`;
|
|
118
114
|
}
|
|
115
|
+
function analyzeRegion({ element, handle }, _options = {}) {
|
|
116
|
+
const location = element.sourceCodeLocation;
|
|
117
|
+
if (location === null || location === undefined || location.startTag === undefined) {
|
|
118
|
+
return {
|
|
119
|
+
locationProblem: `a "${AGENT_ELEMENT_ATTRIBUTE}" region ("${handle}") could not be located in the document — ` +
|
|
120
|
+
"the parser's error-recovery rules likely dropped or repositioned it. Rewrite the document " +
|
|
121
|
+
"so this element is reachable in ordinary document structure (for example, not a table-only " +
|
|
122
|
+
"tag used outside a <table>).",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (location.endTag === undefined) {
|
|
126
|
+
return {
|
|
127
|
+
locationProblem: VOID_ELEMENTS.has(element.tagName)
|
|
128
|
+
? `"${handle}" tags a <${element.tagName}> element, which is void and has no inner content — ` +
|
|
129
|
+
`void elements cannot be a ${AGENT_ELEMENT_ATTRIBUTE} region`
|
|
130
|
+
: `a "${AGENT_ELEMENT_ATTRIBUTE}" region ("${handle}") has no closing tag the parser could ` +
|
|
131
|
+
"locate — its content boundaries cannot be determined safely. Close the tag explicitly.",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const role = attributeValue(element, "data-agent-role");
|
|
135
|
+
const label = attributeValue(element, "data-agent-label");
|
|
136
|
+
return {
|
|
137
|
+
region: {
|
|
138
|
+
handle,
|
|
139
|
+
innerStart: location.startTag.endOffset,
|
|
140
|
+
innerEnd: location.endTag.startOffset,
|
|
141
|
+
...(role === undefined ? {} : { role }),
|
|
142
|
+
...(label === undefined ? {} : { label }),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
119
146
|
/**
|
|
120
147
|
* One parse pass, shared by `findRegions` and `checkWellFormed` so both methods judge the same
|
|
121
148
|
* parse5 run rather than parsing the document twice with a chance of disagreeing.
|
|
@@ -124,7 +151,9 @@ function describeParseError(error) {
|
|
|
124
151
|
*/
|
|
125
152
|
function analyzeDocument(html_) {
|
|
126
153
|
const parseErrorReasons = [];
|
|
127
|
-
|
|
154
|
+
/** Fresh `<div>` fragment-context element — see this module's doc for why `<div>`, not the default. */
|
|
155
|
+
const context = defaultTreeAdapter.createElement("div", html.NS.HTML, []);
|
|
156
|
+
const fragment = parseFragment(context, html_, {
|
|
128
157
|
sourceCodeLocationInfo: true,
|
|
129
158
|
onParseError: (error) => parseErrorReasons.push(describeParseError(error)),
|
|
130
159
|
});
|
|
@@ -132,38 +161,14 @@ function analyzeDocument(html_) {
|
|
|
132
161
|
let locationProblem;
|
|
133
162
|
for (const element of walkElements(fragment)) {
|
|
134
163
|
const handle = attributeValue(element, AGENT_ELEMENT_ATTRIBUTE);
|
|
135
|
-
if (handle === undefined)
|
|
136
|
-
continue;
|
|
137
|
-
if (locationProblem !== undefined)
|
|
164
|
+
if (handle === undefined || locationProblem !== undefined)
|
|
138
165
|
continue; // Already failed; stop accumulating regions.
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
141
|
-
locationProblem =
|
|
142
|
-
`a "${AGENT_ELEMENT_ATTRIBUTE}" region ("${handle}") could not be located in the document — ` +
|
|
143
|
-
"the parser's error-recovery rules likely dropped or repositioned it. Rewrite the document " +
|
|
144
|
-
"so this element is reachable in ordinary document structure (for example, not a table-only " +
|
|
145
|
-
"tag used outside a <table>).";
|
|
166
|
+
const analyzed = analyzeRegion({ element, handle });
|
|
167
|
+
if ("locationProblem" in analyzed) {
|
|
168
|
+
locationProblem = analyzed.locationProblem;
|
|
146
169
|
continue;
|
|
147
170
|
}
|
|
148
|
-
|
|
149
|
-
locationProblem = VOID_ELEMENTS.has(element.tagName)
|
|
150
|
-
? `"${handle}" tags a <${element.tagName}> element, which is void and has no inner content — ` +
|
|
151
|
-
`void elements cannot be a ${AGENT_ELEMENT_ATTRIBUTE} region`
|
|
152
|
-
: `a "${AGENT_ELEMENT_ATTRIBUTE}" region ("${handle}") has no closing tag the parser could ` +
|
|
153
|
-
"locate — its content boundaries cannot be determined safely. Close the tag explicitly.";
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
regions.push({
|
|
157
|
-
handle,
|
|
158
|
-
innerStart: location.startTag.endOffset,
|
|
159
|
-
innerEnd: location.endTag.startOffset,
|
|
160
|
-
...(attributeValue(element, "data-agent-role") === undefined
|
|
161
|
-
? {}
|
|
162
|
-
: { role: attributeValue(element, "data-agent-role") }),
|
|
163
|
-
...(attributeValue(element, "data-agent-label") === undefined
|
|
164
|
-
? {}
|
|
165
|
-
: { label: attributeValue(element, "data-agent-label") }),
|
|
166
|
-
});
|
|
171
|
+
regions.push(analyzed.region);
|
|
167
172
|
}
|
|
168
173
|
if (locationProblem === undefined) {
|
|
169
174
|
const rawMentions = html_.match(RAW_HANDLE_ATTRIBUTE_RE)?.length ?? 0;
|
package/dist/react/session.js
CHANGED
|
@@ -61,6 +61,13 @@ export function createVibecodingSession({ target }, options = {}) {
|
|
|
61
61
|
function cacheChange(change, side) {
|
|
62
62
|
partContent.set(change.id, side === 'after' ? change.after : change.before);
|
|
63
63
|
}
|
|
64
|
+
/** Mirrors a recorded snapshot restore in the cache and re-reads the part list it may have grown. */
|
|
65
|
+
async function restoredPatch(entry) {
|
|
66
|
+
for (const change of entry.changes)
|
|
67
|
+
cacheChange(change, 'after');
|
|
68
|
+
const parts = await target.listParts();
|
|
69
|
+
return { parts, partContent: new Map(partContent), canUndo: history.canUndo(), canRedo: history.canRedo() };
|
|
70
|
+
}
|
|
64
71
|
return {
|
|
65
72
|
target,
|
|
66
73
|
history,
|
|
@@ -140,20 +147,8 @@ export function createVibecodingSession({ target }, options = {}) {
|
|
|
140
147
|
takeSnapshot: () => run(async () => ({ result: await target.snapshot(), patch: {} })),
|
|
141
148
|
restoreSnapshot: ({ snapshot }) => run(async () => {
|
|
142
149
|
const entry = await history.restore({ snapshot });
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for (const change of entry.changes)
|
|
146
|
-
cacheChange(change, 'after');
|
|
147
|
-
const parts = await target.listParts();
|
|
148
|
-
return {
|
|
149
|
-
result: entry,
|
|
150
|
-
patch: {
|
|
151
|
-
parts,
|
|
152
|
-
partContent: new Map(partContent),
|
|
153
|
-
canUndo: history.canUndo(),
|
|
154
|
-
canRedo: history.canRedo(),
|
|
155
|
-
},
|
|
156
|
-
};
|
|
150
|
+
// A restore that already matched records no entry and changes nothing to mirror.
|
|
151
|
+
return { result: entry, patch: entry ? await restoredPatch(entry) : {} };
|
|
157
152
|
}),
|
|
158
153
|
};
|
|
159
154
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/vibecoding",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Conversational artifact authoring: a target-agnostic edit loop — addressable parts, validated replace, operation-level undo/redo, and per-turn snapshot/rewind. Framework-free in `/core`; tagged-region HTML documents behind ./html; filesystem targets behind ./node and the chat/preview surface behind ./react.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -75,15 +75,15 @@
|
|
|
75
75
|
"typescript": "^5.9.2",
|
|
76
76
|
"vitest": "^3.2.4",
|
|
77
77
|
"parse5": "^8.0.1",
|
|
78
|
-
"@jini-ai/core": "0.4.
|
|
78
|
+
"@jini-ai/core": "0.4.9"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@jini-ai/core": "^0.4.
|
|
81
|
+
"@jini-ai/core": "^0.4.9"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"react": "^18.3.0 || ^19.0.0",
|
|
85
85
|
"react-dom": "^18.3.0 || ^19.0.0",
|
|
86
|
-
"parse5": "^8.0.1"
|
|
86
|
+
"parse5": "^7.3.0 || ^8.0.1"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
89
89
|
"react": {
|