M2: planning-view Sharpen control with non-clobbering coach updates
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@
|
|||||||
<script>
|
<script>
|
||||||
const view = document.getElementById('view');
|
const view = document.getElementById('view');
|
||||||
let countdownTimer = null;
|
let countdownTimer = null;
|
||||||
|
let renderedState = null; // which runtime_state the DOM currently shows
|
||||||
|
|
||||||
function post(path, body) {
|
function post(path, body) {
|
||||||
return fetch(path, {
|
return fetch(path, {
|
||||||
@@ -70,9 +71,36 @@ function evidenceBlock(ev) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updatePlanningCoach(coach) {
|
||||||
|
const statusEl = document.getElementById('coachStatus');
|
||||||
|
if (!statusEl) return;
|
||||||
|
if (!coach || coach.status === 'idle') { statusEl.textContent = ''; return; }
|
||||||
|
if (coach.status === 'pending') { statusEl.textContent = 'Sharpening…'; return; }
|
||||||
|
if (coach.status === 'error') { statusEl.textContent = coach.error || 'coach unavailable'; return; }
|
||||||
|
if (coach.status === 'ready' && coach.proposal) {
|
||||||
|
statusEl.textContent = 'AI suggestion applied — edit freely.';
|
||||||
|
// Pre-fill once per ready proposal so later manual edits are not clobbered
|
||||||
|
// by subsequent SSE ticks (idempotent via the dataset stamp below).
|
||||||
|
const stamp = JSON.stringify(coach.proposal);
|
||||||
|
if (statusEl.dataset.applied === stamp) return;
|
||||||
|
statusEl.dataset.applied = stamp;
|
||||||
|
const na = document.getElementById('na'), sc = document.getElementById('sc'),
|
||||||
|
mins = document.getElementById('mins'), start = document.getElementById('start');
|
||||||
|
if (na && !na.value.trim()) na.value = coach.proposal.next_action || '';
|
||||||
|
if (sc && !sc.value.trim()) sc.value = coach.proposal.success_condition || '';
|
||||||
|
if (mins && coach.proposal.timebox_secs) mins.value = Math.round(coach.proposal.timebox_secs / 60);
|
||||||
|
if (start) start.disabled = !(na.value.trim() && sc.value.trim() && +mins.value > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function render(state) {
|
function render(state) {
|
||||||
if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }
|
|
||||||
const rs = state.runtime_state;
|
const rs = state.runtime_state;
|
||||||
|
if (rs === 'planning' && renderedState === 'planning') {
|
||||||
|
updatePlanningCoach(state.coach); // partial update only
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (countdownTimer) { clearInterval(countdownTimer); countdownTimer = null; }
|
||||||
|
renderedState = rs;
|
||||||
if (rs === 'locked') {
|
if (rs === 'locked') {
|
||||||
view.innerHTML = `<span class="pill">Locked</span>
|
view.innerHTML = `<span class="pill">Locked</span>
|
||||||
<p class="meta">No active commitment.</p>
|
<p class="meta">No active commitment.</p>
|
||||||
@@ -80,6 +108,10 @@ function render(state) {
|
|||||||
document.getElementById('plan').onclick = () => post('/planning');
|
document.getElementById('plan').onclick = () => post('/planning');
|
||||||
} else if (rs === 'planning') {
|
} else if (rs === 'planning') {
|
||||||
view.innerHTML = `<span class="pill">Planning</span>
|
view.innerHTML = `<span class="pill">Planning</span>
|
||||||
|
<label>Rough intent</label>
|
||||||
|
<input id="intent" placeholder="e.g. work on the quarterly report">
|
||||||
|
<button id="sharpen" type="button">Sharpen</button>
|
||||||
|
<div id="coachStatus" class="meta"></div>
|
||||||
<label>Next action</label><input id="na" placeholder="What exactly will you do?">
|
<label>Next action</label><input id="na" placeholder="What exactly will you do?">
|
||||||
<label>Success condition</label><input id="sc" placeholder="How do you know it's done?">
|
<label>Success condition</label><input id="sc" placeholder="How do you know it's done?">
|
||||||
<label>Minutes</label><input id="mins" type="number" min="1" value="25">
|
<label>Minutes</label><input id="mins" type="number" min="1" value="25">
|
||||||
@@ -93,6 +125,11 @@ function render(state) {
|
|||||||
success_condition: sc.value.trim(),
|
success_condition: sc.value.trim(),
|
||||||
timebox_secs: Math.round(+mins.value * 60),
|
timebox_secs: Math.round(+mins.value * 60),
|
||||||
});
|
});
|
||||||
|
document.getElementById('sharpen').onclick = () => {
|
||||||
|
const intent = document.getElementById('intent').value.trim();
|
||||||
|
if (intent) post('/coach', { intent });
|
||||||
|
};
|
||||||
|
updatePlanningCoach(state.coach);
|
||||||
} else if (rs === 'active') {
|
} else if (rs === 'active') {
|
||||||
const c = state.commitment || {};
|
const c = state.commitment || {};
|
||||||
view.innerHTML = `<span class="pill">Active</span>
|
view.innerHTML = `<span class="pill">Active</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user