Quantum Studio: a program from elsewhere is named for where it came from #485

Merged
dcharlot merged 1 commit from wai/quantum-studio-origin-words into main 2026-10-26 08:50:45 -04:00
2 changed files with 10 additions and 5 deletions

View file

@ -18,9 +18,12 @@ export const HANDS = {
person: 'you', assistant: 'the assistant', agent: 'an agent', engine: 'the engine', origin: 'where it came from',
assistant_then_person: 'the assistant, then you', agent_then_person: 'an agent, then you',
};
const KINDS = { person: 'you', assistant: 'the assistant', agent: 'an agent', engine: 'the engine', origin: 'the start' };
const KINDS = { person: 'you', assistant: 'the assistant', agent: 'an agent', engine: 'the engine' };
const HOW = { start: 'started', edit: 'edited', proposal: 'proposed', merge: 'merged' };
export const whoName = (by) => `${KINDS[by?.kind] || by?.kind || '?'}${by?.name ? ` (${by.name})` : ''}`;
// Who, as the reader is told: a program from elsewhere is named for where it came from.
export const whoName = (by) => (by?.kind === 'origin' ? by.name || 'where it came from' : `${KINDS[by?.kind] || by?.kind || '?'}${by?.name ? ` (${by.name})` : ''}`);
// How a version was made, and by whom: “edited by you”, “started from the example Bell pair”.
const made = (v) => (v.by?.kind === 'origin' ? `${v.how === 'start' ? 'started from' : 'opened'} ${whoName(v.by)}` : `${HOW[v.how] || v.how} by ${whoName(v.by)}`);
const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;
const svg = (tag, attrs = {}) => {
@ -229,7 +232,7 @@ export function versionsPane({ engine, text, setText, inputs, lock, reviewHost,
if (heads.includes(v.id) && v.id !== current) actions.push(el('button', { class: 'qs-btn qs-small', id: 'qs-vs-merge', type: 'button', text: `Bring ${v.id} in`, title: 'Review its changes against the program and take the ones you want; recorded as a merge', onclick: () => review(v.source, { kind: 'person' }, null, { merge: v.id }) }));
const r = v.review;
detail.replaceChildren(
el('p', { class: 'qs-vs-what' }, el('b', { text: v.id }), el('span', { text: ` ${HOW[v.how] || v.how} by ${whoName(v.by)}${v.parents?.length ? ` from ${v.parents.join(' and ')}` : ''}, ${new Date(v.at).toLocaleString()}` })),
el('p', { class: 'qs-vs-what' }, el('b', { text: v.id }), el('span', { text: ` ${made(v)}${v.parents?.length ? `, from ${v.parents.join(' and ')}` : ''}, ${new Date(v.at).toLocaleString()}` })),
v.note ? el('p', { class: 'qs-note', text: v.note }) : null,
r ? el('p', { class: 'qs-note', id: 'qs-vs-review', text: `${r.accepted.length} of ${plural(r.offered, 'change')} taken${r.accepted.length < r.offered ? '; the rest were rejected, and the whole proposal is kept below' : ''}.` }) : null,
r && r.accepted.length < r.offered ? el('details', {}, el('summary', { text: 'The whole proposal' }), el('pre', { class: 'qs-ai-code', text: r.proposed })) : null,
@ -256,7 +259,7 @@ export function versionsPane({ engine, text, setText, inputs, lock, reviewHost,
return el('li', { 'data-id': v.id, class: v.id === current ? 'here' : '' },
el('button', { type: 'button', class: 'qs-vs-item', onclick: () => showDetail(v, log) },
el('span', { class: 'qs-vs-id', text: v.id }),
el('span', { class: 'qs-vs-by', text: `${HOW[v.how] || v.how} by ${whoName(v.by)}` }),
el('span', { class: 'qs-vs-by', text: made(v) }),
el('span', { class: 'qs-note', text: `${e.review ? `${e.review.accepted} of ${e.review.offered} taken · ` : ''}+${e.added ?? 0} −${e.removed ?? 0}${tags.length ? ` · ${tags.join(', ')}` : ''}` })));
}));
const b = await blame().catch(() => null);

View file

@ -231,6 +231,8 @@ const chip = (k) => page.$eval(`#qs-measures [data-k=${k}]`, (c) => c.textConten
await setProgram(BASE);
await record();
check(/edited by you/.test(await here()), `what the reader types is recorded as theirs (${await here()})`);
const first = await page.textContent('#qs-vs-list li:last-child');
check(/^v0started from the example /.test(first), `the first version is named for where the program came from (${first})`);
const mine = await page.$eval('#qs-vs-list li.here', (l) => l.dataset.id);
const PROPOSED = 'OPENQASM 3.0;\nqubit[3] q;\nbit[3] c;\nh q[1];\ncx q[0], q[1];\ncx q[1], q[2];\nc = measure q;\n';
await page.evaluate((q) => { window.quantumStudio.propose(q, { kind: 'assistant', name: 'scripted' }, 'entangle the third qubit'); }, PROPOSED);
@ -280,7 +282,7 @@ const chip = (k) => page.$eval(`#qs-measures [data-k=${k}]`, (c) => c.textConten
await page.click('#qs-review-apply');
await page.waitForFunction(() => /merged by you/.test(document.querySelector('#qs-vs-list li.here')?.textContent ?? ''), null, { timeout: LONG });
const merged = await doc();
check(/x q\[2\];/.test(merged) && /cx q\[0\], q\[2\];/.test(merged) && /from v\d+ and v\d+/.test(await page.textContent('#qs-vs-detail')),
check(/x q\[2\];/.test(merged) && /cx q\[0\], q\[2\];/.test(merged) && /, from v\d+ and v\d+/.test(await page.textContent('#qs-vs-detail')),
`bringing a line of work back in is a merge of both (${other}, ${otherHead})`);
check(/the assistant, then you \(scripted\): 1 gate/.test(await page.textContent('#qs-vs-share')), 'the merge keeps each line’s author');
const count = (await page.$$('#qs-vs-list li')).length;