JacobLinCool Codex commited on
Commit
67e347f
·
verified ·
1 Parent(s): 3edd42b

feat: select idea board pages

Browse files

Co-authored-by: Codex <noreply@openai.com>

Files changed (3) hide show
  1. README.md +1 -0
  2. static/app.js +61 -11
  3. static/styles.css +22 -0
README.md CHANGED
@@ -81,6 +81,7 @@ the private Field Notes artifact.
81
  The `Rank` command rescans the saved idea board, recalculates each seal against the current targets, selects the
82
  strongest page as the active idea, and drafts the next build step. The app then moves that page to the top of the Idea
83
  Board and refreshes the seal, wood map, plan, and PNG artifact around the chosen direction.
 
84
  If the board is empty, `Plan` and `Rank` do not create placeholder pages; they prompt the user to write an idea or press
85
  `Gap` first.
86
 
 
81
  The `Rank` command rescans the saved idea board, recalculates each seal against the current targets, selects the
82
  strongest page as the active idea, and drafts the next build step. The app then moves that page to the top of the Idea
83
  Board and refreshes the seal, wood map, plan, and PNG artifact around the chosen direction.
84
+ Users can also click any Idea Board page to make it current before pressing `Plan`.
85
  If the board is empty, `Plan` and `Rank` do not create placeholder pages; they prompt the user to write an idea or press
86
  `Gap` first.
87
 
static/app.js CHANGED
@@ -97,6 +97,12 @@ profileEl.addEventListener("input", (event) => {
97
  saveSession();
98
  });
99
 
 
 
 
 
 
 
100
  async function runTurn(message) {
101
  input.value = "";
102
  submit.disabled = true;
@@ -216,18 +222,21 @@ function renderProvenance(data) {
216
  }
217
 
218
  function renderRestoredSession(data) {
219
- currentArtifact = session.last_artifact || null;
220
- if (currentArtifact?.seal) {
221
- renderScore(currentArtifact.seal);
222
- verdictEl.textContent = currentArtifact.verdict || currentArtifact.seal.verdict || "UNWRITTEN";
223
- overallEl.textContent = Number(currentArtifact.overall || currentArtifact.seal.overall || 0).toFixed(1);
224
- renderWoodMap(currentArtifact.wood_map || null);
225
- if (currentArtifact.seal.echoes?.length) {
226
- renderCitations(currentArtifact.seal.echoes);
 
 
 
227
  } else {
228
  renderProjects(data.top_projects || []);
229
  }
230
- exportButton.disabled = false;
231
  } else {
232
  renderScore(null);
233
  renderWoodMap(null);
@@ -410,8 +419,12 @@ function renderIdeas(ideas) {
410
  for (const idea of visibleIdeas(ideas)) {
411
  const score = idea.score?.overall ? Number(idea.score.overall).toFixed(1) : "0.0";
412
  const targets = (idea.targets || []).slice(0, 3).map(targetDisplayName).join(" · ");
413
- const item = document.createElement("div");
414
- item.className = "idea";
 
 
 
 
415
  item.innerHTML = `
416
  <strong>${escapeHtml(idea.title)}</strong>
417
  <p>${escapeHtml((idea.pitch || "").slice(0, 120))}</p>
@@ -429,6 +442,43 @@ function visibleIdeas(ideas) {
429
  return current ? [current, ...remaining] : ideas.slice(-4).reverse();
430
  }
431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  function targetDisplayName(target) {
433
  return targetProfileById.get(target)?.label || target;
434
  }
 
97
  saveSession();
98
  });
99
 
100
+ ideasEl.addEventListener("click", (event) => {
101
+ const card = event.target.closest("[data-idea-id]");
102
+ if (!(card instanceof HTMLElement) || !ideasEl.contains(card)) return;
103
+ selectIdea(card.dataset.ideaId || "");
104
+ });
105
+
106
  async function runTurn(message) {
107
  input.value = "";
108
  submit.disabled = true;
 
222
  }
223
 
224
  function renderRestoredSession(data) {
225
+ const idea = currentIdea();
226
+ const storedArtifact = session.last_artifact || null;
227
+ currentArtifact = !idea || storedArtifact?.title === idea.title ? storedArtifact : null;
228
+ const score = currentArtifact?.seal || idea?.score || null;
229
+ if (score) {
230
+ renderScore(score);
231
+ verdictEl.textContent = currentArtifact?.verdict || score.verdict || "UNWRITTEN";
232
+ overallEl.textContent = Number(currentArtifact?.overall || score.overall || 0).toFixed(1);
233
+ renderWoodMap(currentArtifact?.wood_map || null);
234
+ if (score.echoes?.length) {
235
+ renderCitations(score.echoes);
236
  } else {
237
  renderProjects(data.top_projects || []);
238
  }
239
+ exportButton.disabled = !currentArtifact;
240
  } else {
241
  renderScore(null);
242
  renderWoodMap(null);
 
419
  for (const idea of visibleIdeas(ideas)) {
420
  const score = idea.score?.overall ? Number(idea.score.overall).toFixed(1) : "0.0";
421
  const targets = (idea.targets || []).slice(0, 3).map(targetDisplayName).join(" · ");
422
+ const selected = idea.id === session.current_idea_id;
423
+ const item = document.createElement("button");
424
+ item.type = "button";
425
+ item.className = `idea ${selected ? "current" : ""}`;
426
+ item.dataset.ideaId = idea.id || "";
427
+ item.setAttribute("aria-pressed", selected ? "true" : "false");
428
  item.innerHTML = `
429
  <strong>${escapeHtml(idea.title)}</strong>
430
  <p>${escapeHtml((idea.pitch || "").slice(0, 120))}</p>
 
442
  return current ? [current, ...remaining] : ideas.slice(-4).reverse();
443
  }
444
 
445
+ function currentIdea() {
446
+ const ideas = Array.isArray(session.ideas) ? session.ideas : [];
447
+ return ideas.find((idea) => idea.id === session.current_idea_id) || ideas[ideas.length - 1] || null;
448
+ }
449
+
450
+ function selectIdea(ideaId) {
451
+ if (!ideaId || !Array.isArray(session.ideas)) return;
452
+ const idea = session.ideas.find((item) => item.id === ideaId);
453
+ if (!idea) return;
454
+ session.current_idea_id = idea.id;
455
+ if (Array.isArray(idea.targets) && idea.targets.length) {
456
+ session.targets = targetOptions.filter((option) => idea.targets.includes(option));
457
+ }
458
+ const score = idea.score || null;
459
+ if (score) {
460
+ verdictEl.textContent = score.verdict || "DRAFT";
461
+ overallEl.textContent = Number(score.overall || 0).toFixed(1);
462
+ renderScore(score);
463
+ ink.classList.toggle("bleed", String(score.verdict || "").startsWith("ECHO"));
464
+ ink.classList.toggle("gold", String(score.verdict || "").startsWith("UNWRITTEN"));
465
+ }
466
+ if (session.last_artifact?.title === idea.title) {
467
+ currentArtifact = session.last_artifact;
468
+ renderWoodMap(currentArtifact.wood_map || null);
469
+ exportButton.disabled = false;
470
+ } else {
471
+ currentArtifact = null;
472
+ renderWoodMap(null);
473
+ exportButton.disabled = true;
474
+ }
475
+ renderTargets(session.targets || []);
476
+ renderIdeas(session.ideas);
477
+ renderPlan([]);
478
+ corrections.textContent = `selected: ${idea.title}`;
479
+ saveSession();
480
+ }
481
+
482
  function targetDisplayName(target) {
483
  return targetProfileById.get(target)?.label || target;
484
  }
static/styles.css CHANGED
@@ -317,6 +317,28 @@ button:disabled {
317
  border-radius: 0 8px 8px 0;
318
  }
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  .project strong,
321
  .gap strong,
322
  .idea strong {
 
317
  border-radius: 0 8px 8px 0;
318
  }
319
 
320
+ .idea {
321
+ width: 100%;
322
+ border-top: 0;
323
+ border-right: 0;
324
+ border-bottom: 0;
325
+ appearance: none;
326
+ text-align: left;
327
+ cursor: pointer;
328
+ }
329
+
330
+ .idea:hover,
331
+ .idea:focus-visible,
332
+ .idea.current {
333
+ border-left-color: var(--leaf);
334
+ background: rgba(255, 241, 196, 0.56);
335
+ }
336
+
337
+ .idea:focus-visible {
338
+ outline: 2px solid rgba(47, 122, 73, 0.5);
339
+ outline-offset: 2px;
340
+ }
341
+
342
  .project strong,
343
  .gap strong,
344
  .idea strong {