JacobLinCool Codex commited on
Commit
1c7ce9d
·
verified ·
1 Parent(s): 98631d7

fix: clarify builder-facing interface copy

Browse files

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

Files changed (3) hide show
  1. static/app.js +19 -19
  2. static/index.html +5 -5
  3. tests/test_frontend_copy.py +42 -0
static/app.js CHANGED
@@ -99,7 +99,7 @@ goalsEl.addEventListener("change", (event) => {
99
  );
100
  session.goals = goalOptions.filter((option) => checked.has(option));
101
  syncCurrentIdeaGoals();
102
- invalidateCurrentSeal("Goals updated. Press Ink or Plan to refresh the seal.");
103
  saveSession();
104
  renderGoals(session.goals);
105
  renderIdeas(session.ideas || []);
@@ -166,7 +166,7 @@ async function runTurn(message) {
166
  completed = true;
167
  } catch (error) {
168
  clearTurnWatchdog();
169
- ink.textContent = `The page tore before it could answer: ${error.message}`;
170
  ink.classList.remove("thinking");
171
  ink.classList.add("bleed");
172
  } finally {
@@ -336,7 +336,7 @@ function resetSession() {
336
  input.disabled = false;
337
  setSessionControlsDisabled(false);
338
  input.value = "";
339
- ink.textContent = "The book is open. The next page waits for its first line.";
340
  ink.classList.remove("thinking", "bleed", "gold");
341
  corrections.textContent = "Session reset.";
342
  renderGoals(session.goals);
@@ -362,14 +362,14 @@ async function loadDemoSession() {
362
  setSessionControlsDisabled(true);
363
  ink.classList.remove("bleed", "gold");
364
  ink.classList.add("thinking");
365
- ink.textContent = "A sample page is being inked.";
366
  corrections.textContent = "";
367
  try {
368
  const response = await fetch("/api/demo-session");
369
- if (!response.ok) throw new Error(`demo rehearsal failed with ${response.status}`);
370
  applyDemoSession(await response.json());
371
  } catch (error) {
372
- ink.textContent = `The demo rehearsal could not be loaded: ${error.message}`;
373
  ink.classList.remove("thinking");
374
  ink.classList.add("bleed");
375
  } finally {
@@ -385,9 +385,9 @@ function applyDemoSession(data) {
385
  session.profile = session.profile || {};
386
  session.goals = Array.isArray(session.goals) ? session.goals : [];
387
  session.last_response = data.response || session.last_response || "";
388
- session.ui_status = `example loaded: ${data.turn_count || 0} advisor turns`;
389
  currentArtifact = data.artifact || session.last_artifact || null;
390
- ink.textContent = data.response || "Demo rehearsal loaded.";
391
  ink.classList.remove("thinking");
392
  if (data.score) {
393
  setVerdictDisplay(data.score.verdict, data.score.overall, data.score);
@@ -657,7 +657,7 @@ function renderProjectReferenceState() {
657
  currentArtifact = null;
658
  renderScore(null);
659
  setVerdictDisplay("READY", 0, null);
660
- sealCopyEl.textContent = "Project pages are shown below. Score an idea to press a new seal.";
661
  ink.classList.remove("bleed", "gold");
662
  renderWoodMap(null);
663
  }
@@ -670,7 +670,7 @@ function renderIdeas(ideas) {
670
  if (ideaCountEl) ideaCountEl.textContent = ideas.length;
671
  ideasEl.innerHTML = "";
672
  if (!ideas.length) {
673
- ideasEl.innerHTML = `<div class="empty">Your idea board is empty. Write an idea or open a gap.</div>`;
674
  return;
675
  }
676
  for (const idea of visibleIdeas(ideas)) {
@@ -793,7 +793,7 @@ function renderScore(score) {
793
  function renderWoodMap(map) {
794
  woodMapEl.innerHTML = "";
795
  if (!map?.dots?.length) {
796
- woodMapEl.innerHTML = `<div class="wood"><div class="empty wood-empty">No idea has been placed yet.</div></div>`;
797
  return;
798
  }
799
  const field = document.createElement("div");
@@ -824,11 +824,11 @@ function renderWoodMap(map) {
824
  `;
825
  const caption = document.createElement("p");
826
  caption.className = "wood-cap";
827
- caption.textContent = map.caption || "Your page is plotted against the current Wood.";
828
  woodMapEl.append(field, legend, caption);
829
  }
830
 
831
- function renderProjects(projects, emptyMessage = "No red ink yet.") {
832
  projectsEl.innerHTML = "";
833
  if (!projects.length) {
834
  projectsEl.innerHTML = `<div class="empty">${escapeHtml(emptyMessage)}</div>`;
@@ -849,7 +849,7 @@ function renderProjects(projects, emptyMessage = "No red ink yet.") {
849
  function renderCitations(echoes) {
850
  projectsEl.innerHTML = "";
851
  if (!echoes.length) {
852
- projectsEl.innerHTML = `<div class="empty">No red ink yet.</div>`;
853
  return;
854
  }
855
  for (const echo of echoes.slice(0, 5)) {
@@ -874,7 +874,7 @@ function renderCitations(echoes) {
874
  function renderWhitespace(items) {
875
  whitespaceEl.innerHTML = "";
876
  if (!items.length) {
877
- whitespaceEl.innerHTML = `<div class="empty">Gold has not gathered.</div>`;
878
  return;
879
  }
880
  for (const item of items.slice(0, 4)) {
@@ -895,7 +895,7 @@ function renderWhitespace(items) {
895
  function renderPlan(steps) {
896
  planEl.innerHTML = "";
897
  if (!steps.length) {
898
- planEl.innerHTML = `<li class="empty">No wax path pressed.</li>`;
899
  return;
900
  }
901
  for (const step of steps) {
@@ -922,11 +922,11 @@ function setCommandDisabled(disabled) {
922
  function startTurnWatchdog() {
923
  clearTurnWatchdog();
924
  sawTurnToken = false;
925
- ink.textContent = "The page is choosing its words.";
926
  ink.classList.add("thinking");
927
  turnWatchdog = window.setTimeout(() => {
928
  if (sawTurnToken) return;
929
- ink.textContent = "Still riffling the inked pages.";
930
  }, 2200);
931
  }
932
 
@@ -1158,7 +1158,7 @@ function drawWoodMap(ctx, map, x, y, width, height, verdict) {
1158
 
1159
  ctx.fillStyle = "#6b4e35";
1160
  ctx.font = "800 18px Inter, sans-serif";
1161
- ctx.fillText("YOU VS THE WOOD", x, y - 14);
1162
 
1163
  for (const dot of map.dots) {
1164
  const px = x + (width * boundedPercent(dot.x)) / 100;
 
99
  );
100
  session.goals = goalOptions.filter((option) => checked.has(option));
101
  syncCurrentIdeaGoals();
102
+ invalidateCurrentSeal("Goals updated. Press Ink or Plan to refresh the score.");
103
  saveSession();
104
  renderGoals(session.goals);
105
  renderIdeas(session.ideas || []);
 
166
  completed = true;
167
  } catch (error) {
168
  clearTurnWatchdog();
169
+ ink.textContent = `The advisor could not answer: ${error.message}`;
170
  ink.classList.remove("thinking");
171
  ink.classList.add("bleed");
172
  } finally {
 
336
  input.disabled = false;
337
  setSessionControlsDisabled(false);
338
  input.value = "";
339
+ ink.textContent = "The book is open. Describe an idea to start a new page.";
340
  ink.classList.remove("thinking", "bleed", "gold");
341
  corrections.textContent = "Session reset.";
342
  renderGoals(session.goals);
 
362
  setSessionControlsDisabled(true);
363
  ink.classList.remove("bleed", "gold");
364
  ink.classList.add("thinking");
365
+ ink.textContent = "Loading an example idea board.";
366
  corrections.textContent = "";
367
  try {
368
  const response = await fetch("/api/demo-session");
369
+ if (!response.ok) throw new Error(`example session failed with ${response.status}`);
370
  applyDemoSession(await response.json());
371
  } catch (error) {
372
+ ink.textContent = `The example session could not be loaded: ${error.message}`;
373
  ink.classList.remove("thinking");
374
  ink.classList.add("bleed");
375
  } finally {
 
385
  session.profile = session.profile || {};
386
  session.goals = Array.isArray(session.goals) ? session.goals : [];
387
  session.last_response = data.response || session.last_response || "";
388
+ session.ui_status = `Example loaded: ${data.turn_count || 0} advisor turns`;
389
  currentArtifact = data.artifact || session.last_artifact || null;
390
+ ink.textContent = data.response || "Example session loaded.";
391
  ink.classList.remove("thinking");
392
  if (data.score) {
393
  setVerdictDisplay(data.score.verdict, data.score.overall, data.score);
 
657
  currentArtifact = null;
658
  renderScore(null);
659
  setVerdictDisplay("READY", 0, null);
660
+ sealCopyEl.textContent = "Project pages are shown below. Write or select an idea to score it.";
661
  ink.classList.remove("bleed", "gold");
662
  renderWoodMap(null);
663
  }
 
670
  if (ideaCountEl) ideaCountEl.textContent = ideas.length;
671
  ideasEl.innerHTML = "";
672
  if (!ideas.length) {
673
+ ideasEl.innerHTML = `<div class="empty">Your idea board is empty. Write an idea or choose an under-explored direction.</div>`;
674
  return;
675
  }
676
  for (const idea of visibleIdeas(ideas)) {
 
793
  function renderWoodMap(map) {
794
  woodMapEl.innerHTML = "";
795
  if (!map?.dots?.length) {
796
+ woodMapEl.innerHTML = `<div class="wood"><div class="empty wood-empty">Score an idea to plot it on the map.</div></div>`;
797
  return;
798
  }
799
  const field = document.createElement("div");
 
824
  `;
825
  const caption = document.createElement("p");
826
  caption.className = "wood-cap";
827
+ caption.textContent = map.caption || "Your idea is plotted against the current project map.";
828
  woodMapEl.append(field, legend, caption);
829
  }
830
 
831
+ function renderProjects(projects, emptyMessage = "No nearby projects yet.") {
832
  projectsEl.innerHTML = "";
833
  if (!projects.length) {
834
  projectsEl.innerHTML = `<div class="empty">${escapeHtml(emptyMessage)}</div>`;
 
849
  function renderCitations(echoes) {
850
  projectsEl.innerHTML = "";
851
  if (!echoes.length) {
852
+ projectsEl.innerHTML = `<div class="empty">No nearby project echoes yet.</div>`;
853
  return;
854
  }
855
  for (const echo of echoes.slice(0, 5)) {
 
874
  function renderWhitespace(items) {
875
  whitespaceEl.innerHTML = "";
876
  if (!items.length) {
877
+ whitespaceEl.innerHTML = `<div class="empty">No under-explored directions are loaded yet.</div>`;
878
  return;
879
  }
880
  for (const item of items.slice(0, 4)) {
 
895
  function renderPlan(steps) {
896
  planEl.innerHTML = "";
897
  if (!steps.length) {
898
+ planEl.innerHTML = `<li class="empty">Press Plan to draft build steps for the selected idea.</li>`;
899
  return;
900
  }
901
  for (const step of steps) {
 
922
  function startTurnWatchdog() {
923
  clearTurnWatchdog();
924
  sawTurnToken = false;
925
+ ink.textContent = "Checking the current project map.";
926
  ink.classList.add("thinking");
927
  turnWatchdog = window.setTimeout(() => {
928
  if (sawTurnToken) return;
929
+ ink.textContent = "Still comparing against nearby projects.";
930
  }, 2200);
931
  }
932
 
 
1158
 
1159
  ctx.fillStyle = "#6b4e35";
1160
  ctx.font = "800 18px Inter, sans-serif";
1161
+ ctx.fillText("IDEA MAP", x, y - 14);
1162
 
1163
  for (const dot of map.dots) {
1164
  const px = x + (width * boundedPercent(dot.x)) / 100;
static/index.html CHANGED
@@ -77,7 +77,7 @@
77
 
78
  <nav class="mobile-nav" aria-label="Sections">
79
  <button type="button" class="active" data-tab="page">Page</button>
80
- <button type="button" data-tab="proof">Proof</button>
81
  <button type="button" data-tab="almanac">Board</button>
82
  </nav>
83
 
@@ -183,7 +183,7 @@
183
  </span>
184
  <p id="ink" class="prophecy">
185
  The book is open. Describe a project idea, compare it against the current map, then turn the result into
186
- a build plan.
187
  </p>
188
  </article>
189
 
@@ -210,17 +210,17 @@
210
  </section>
211
 
212
  <section class="section">
213
- <div class="eyebrow">You vs the Wood</div>
214
  <div id="wood-map" class="wood-map"></div>
215
  </section>
216
 
217
  <section class="section">
218
- <div class="eyebrow">Closest echoes</div>
219
  <div id="projects" class="project-list"></div>
220
  </section>
221
 
222
  <section class="section">
223
- <div class="eyebrow">Gold margins</div>
224
  <div id="whitespace" class="whitespace-list"></div>
225
  </section>
226
  </aside>
 
77
 
78
  <nav class="mobile-nav" aria-label="Sections">
79
  <button type="button" class="active" data-tab="page">Page</button>
80
+ <button type="button" data-tab="proof">Evidence</button>
81
  <button type="button" data-tab="almanac">Board</button>
82
  </nav>
83
 
 
183
  </span>
184
  <p id="ink" class="prophecy">
185
  The book is open. Describe a project idea, compare it against the current map, then turn the result into
186
+ concrete build steps.
187
  </p>
188
  </article>
189
 
 
210
  </section>
211
 
212
  <section class="section">
213
+ <div class="eyebrow">Idea map</div>
214
  <div id="wood-map" class="wood-map"></div>
215
  </section>
216
 
217
  <section class="section">
218
+ <div class="eyebrow">Closest project echoes</div>
219
  <div id="projects" class="project-list"></div>
220
  </section>
221
 
222
  <section class="section">
223
+ <div class="eyebrow">Under-explored directions</div>
224
  <div id="whitespace" class="whitespace-list"></div>
225
  </section>
226
  </aside>
tests/test_frontend_copy.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+
3
+
4
+ def test_main_interface_copy_is_builder_facing() -> None:
5
+ html = Path("static/index.html").read_text(encoding="utf-8")
6
+ app_js = Path("static/app.js").read_text(encoding="utf-8")
7
+ combined = f"{html}\n{app_js}"
8
+
9
+ assert "Under-explored directions" in html
10
+ assert "Closest project echoes" in html
11
+ assert "Press Plan to draft build steps for the selected idea." in app_js
12
+ assert "Loading an example idea board." in app_js
13
+
14
+ stale_jargon = [
15
+ "No wax path pressed.",
16
+ "Gold has not gathered.",
17
+ "No red ink yet.",
18
+ "Demo rehearsal",
19
+ "demo rehearsal",
20
+ "press a new seal",
21
+ "The page is choosing its words.",
22
+ "Still riffling the inked pages.",
23
+ "YOU VS THE WOOD",
24
+ "current Wood",
25
+ ]
26
+ for phrase in stale_jargon:
27
+ assert phrase not in combined
28
+
29
+
30
+ def test_visible_static_shell_does_not_promote_submission_evidence() -> None:
31
+ html = Path("static/index.html").read_text(encoding="utf-8").lower()
32
+
33
+ promotional_terms = [
34
+ "judge",
35
+ "prize",
36
+ "submission",
37
+ "badge",
38
+ "build-small",
39
+ "hackathon criteria",
40
+ ]
41
+ for term in promotional_terms:
42
+ assert term not in html