note / learning note

Inside the Prompt Builder source

A closer reading of the saved generation engine, configuration state, source rules, manual-draft recovery, and browser storage.

Part of Prompt Builder →

The build chats alone left an odd gap. Twice I asked for Prompt Builder and twice the assistant reported that its build environment had failed. Yet the archive also held a saved frontend. I read its JavaScript to answer a narrower question than “was the site finished?”: what had been implemented in the version that survived?

The interesting path runs from a short keyword or a selected control to a sentence in the output. The saved code has an engine for choices and wording, and a browser layer for controls, editing, copying, and presets. That separation is where the original idea becomes concrete. It also reveals the practical compromises: a single previous manual draft in memory, browser-local presets, and a character meter that warns but does not shorten the text for me.

Two files with different responsibilities

File Responsibility
engine.mjs Choices, defaults, presets, tags, instruction phrases, generation, and character counting
app.js Rendering controls, handling input, preserving manual edits, copying, and saving presets

The UI imports functions such as defaults, fromPreset, generate, addTag, removeTag, and countState. The engine does not need to know about a particular input element or dialog.

The configuration object contains the task, role, domain, clarification behavior, strictness, explanation detail, output type, source mode, generation mode, six tag arrays, and the selected behavior/output/source options. That gives each control a defined place in the state.

From a tag to a sentence

The tag categories are Goals, Technologies, Features, Constraints, Priorities, and Avoid. Pressing Enter commits a trimmed value. Blank values and case-insensitive duplicates are rejected; removing a chip deletes the corresponding array element.

The engine then uses those categories differently. Technology tags become a sentence beginning “Work with”; constraint tags become “Respect these constraints”; features become capabilities to include. A priority is used to guide decisions instead of being treated as another feature.

Generation collects instructions into named sections. Its section helper filters out empty lines, trims text, and uses a Set to remove exact duplicates. This is a deterministic collection of rules, not a request sent to a language model at runtime.

Source rules change with the information mode

A particularly useful detail is that source toggles are interpreted alongside the selected information base.

  • With Model knowledge, the instructions say not to imply live research or current verification. Citation wording also warns against inventing a reference that cannot be identified reliably.
  • With Provided sources only, missing support becomes a gap to report. Preference for primary sources applies within the supplied materials.
  • With Sources plus outside research, the instructions allow relevant additional research and require a limitation to be stated if research tools are unavailable.

That keeps combinations of controls from casually promising work that the selected mode does not allow.

Live generation and manual edits

The UI holds four pieces of working state: the configuration, an edited flag, the previous manual draft, and the saved-preset list. A control change updates the configuration and calls regenerate().

If the textarea was manually edited, regeneration first copies its current contents into the previous-draft variable. It then replaces the textarea with generate(state), updates the edit indicator, and recounts the text. Restore swaps the current text with that previous draft, so a user can compare the two.

Reset returns the configuration to General defaults and keeps the saved presets. A manually edited draft can also be preserved through that action. None of this is a multi-version document history: the previous draft is held in memory.

What the character counter actually counts

The saved engine uses JavaScript’s string iterator:

export function countState(text, project) {
  const count = Array.from(text).length;
  return {
    count,
    warning: project && count >= 7500 && count <= 8000,
    over: project && count > 8000
  };
}

Those boundaries apply when the selected output is Project Instructions. Exactly 8,000 remains in the warning range; 8,001 is over. Array.from counts Unicode code points, which can differ from the number of visible symbols when a character is composed from several code points. The limit is the one this project was designed around.

Presets, storage, and copying

Saved presets use localStorage under a versioned key. On load, the interface checks that choice values are known, tag arrays contain strings, and selected options belong to their allowed lists. Malformed stored data is ignored.

Saving requires a nonempty, unique name. The saved object is a clone of the controls; the interface explicitly tells the user that manual output edits are not part of the preset. A storage failure leaves the dialog open with an error instead of claiming the preset was saved.

Copy first attempts the Clipboard API, then a browser copy fallback. If neither works, the message tells the user to select the text and copy it manually.

I could trace a control all the way to a sentence, and I could see exactly what happened to a manual edit when the controls changed. The saved code answered those implementation questions. It did not by itself tell me how the old public deployment behaves in a browser today.

Related rabbit holes

PublishedOct 3, 2026
note / experiment

A thin-client Linux experiment

A network-boot question became a comparison of separate remote desktops and shared physical desktops, ending with a usable Linux thin-client session and working audio.

Documented activity: September 20, 2026 – September 26, 2026