2026-06-21
Newest entry on top.
DPL Phase 4 polish 3 — "Blocks", global any/any-ver, default wrapper (CODE)
Owner: rename the group "Blocks" (not Prompts); make any version-locked + add a global any-ver shown under
"any" on every version (no version prefix — it's global); and build a sensible default wrapper from common
v2 start/end framing, applied when nothing else is loaded.
- Engine (
dynamicPrompt.js): added{#any-ver}(+-sfw/-nsfw) — picks one generator across ALL generations;{#any}stays the current-version (v3) wildcard. Both unprefixed/global. - SPA (
promptEngine.js): the "any" wildcard is now one GLOBAL set (any+any-ver, no{#v2/any}), shown under the "any" category on every version tab. Renamed the v3 block/group to Blocks (Home.jsxhead + block title).renderWrapperPart()added so wrapper boxes are real DPL (bullets/probability) rendered to tokens before generation. - Default wrapper (
wrapperStore.jsDEFAULT_WRAPPER): STARTmasterpiece, best quality, highly detailed; END (DPL){#fx}, {#artists}+ 50%intricate detail/sharp focus/wide shot/<rays>+ 35%<dap>— distilled from the most common v2 endings (intricate ×16, highly detailed ×6, dap ×5, sharp focus ×5, wide shot ×5, + auto fx/artists).Home.buildPromptsappliessettings.wrapper ?? DEFAULT_WRAPPER, rendering the wrapper per prompt. WrapperFab popover gained a built-in Default entry (active when nothing chosen) alongside None. - Verified: lint clean, smoke OK,
dpl-engine-check({#any}/{#any-ver}) OK, web build 597 modules.
DPL Phase 4 polish 2 — v3 drops full/partial; prefix in button labels (CODE)
Owner: v3 doesn't use full/partial anymore — remove type: from the v3 .dpl files; v3's submenu under
"Prompts" is just Prompts (one list); and the v1/v2 chip labels must show the root prefix (the syntax
you'd type).
- Stripped
type: full/type: partialfrom all 80 v3.dplfiles (PowerShell, LF/no-BOM). v3 generators now carry onlydescription/suggestions/script. - Classifier (
promptFilesAndSuggestions.js): v3 is one pool — every active generator is a "prompt" (whole set is the suggestion pool minussuggestions: off);partialRegularstays empty. v1/v2 keep theirfullflag (frozen, still split). - SPA (
promptEngine.js): three dynVersioned blocks — Prompts (v3 only, all generators, one tab), Full prompts / Partial prompts (v2/v1 only). The navbar shows v3 → one "Prompts" sub-tab; v2 → Full- Partial; v1 → Full. Chip + group labels now show the token's inner text — bare for v3 (
cave,scene), prefixed for v1/v2 (v2/cave,v2/scene) — so the button reflects exactly what gets typed.
- Partial; v1 → Full. Chip + group labels now show the token's inner text — bare for v3 (
- Verified: lint clean, smoke OK,
dpl-engine-checkOK, web build 597 modules.
DPL Phase 4 polish — first-class v1/v2, prefix tokens, wrapper-in-field-bar (CODE)
Owner feedback: "to access a non-default generation, root-prefix with v#/ then the normal token; the
buttons must reflect this. v1/v2 are frozen but still first class (categories, partials, the same
system). Put the wrapper as an icon-only button in the prompt-box field bar, not a floating FAB."
- Engine made generation-generic (
core/stages/dynamicPrompt.js): oneexpandGen(name, tag)handles v3 (tag "") and the frozen v1/v2 identically — suffix resolution, implied folder groups, and the{#any}wildcard all work per generation ({#scene},{#v2/scene},{#any},{#v2/any},{#v1/castle},{#v2/cave}verified). Frozen generations only differ in being prefix-addressed + forcing fx/artists off. - Loaders gained
dynPromptGroupDirsAll()+dynPromptForcedPrefixDirsAll()(all generations; the engine/UI filter by prefix) in bothnodeLoaderandbrowserLoader. - SPA building blocks (
promptEngine.js) rebuilt generation-aware: v1/v2/v3 each browse the same way — categories, full/partial,{#any}— with tokens{#<short>}for v3 and{#v<n>/<short>}for v1/v2 (short =computeButtonNames, not full paths). Fixes the missing v2 partials/categories and the broken-v1tokens. - Wrapper moved into the prompt-box field bar as an icon-only
.field-actbutton (popover anchored above the bar); removed the floating FAB (Home.jsx,App.jsx,WrapperFab.jsx,styles.css). - Verified: 25 DPL tests, smoke,
dpl-engine-check(incl. group + wildcard per generation), web build (597 modules) — all green.
DPL Phase 4 — wrapper UI + SPA v3 building blocks (CODE)
Built the "wrapper" UI and reconciled the SPA building-block panel with the v3-default engine.
- Wrapper (owner spec): a START + END pair that frames every prompt (v3 root layer = open + middle +
close). New
web-app/src/lib/wrapperStore.js(localStorage presets{name:{start,end}}) andcomponents/WrapperFab.jsx— a bottom-right floating button whose popover lists saved wrapper presets (apply one / None) with a Manage presets button that opens a modal: preset naming/management above, the two side-by-side Start/End editors below. Selected wrapper lives insettings.wrapper(shared via the share-link);Home.buildPromptsframes generation asstart, prompt, end. Styled to match the app (tokens, pill button, popover, modal) instyles.css. - SPA building blocks updated for Phase 3: v3 is the default set (bare
{#name}); the navbar superset toggle is now v3 / v2 / v1 (v3 default); frozen v1/v2 chips emit path-prefix tokens ({#v1/castle},{#v2/scene/cave}) instead of the removed-v1suffix (promptEngine.js,Home.jsx). - Verified:
npm test(lint 0 errors, smoke green, v3 generators active in the suggestion);npm --prefix web-app run buildgreen (597 modules). Start/end UX open question inv3-layers.mdresolved.
DPL Phase 3 — loader/engine integration: v3 is the default catalog (CODE)
Wired v3 .dpl (+ JS sidecars) into the live engine, both loaders, the dynamic-prompt stage, and the
classifier. Owner calls during this phase: v3 is the default (bare {#name}); v1 and v2 are frozen
and addressed only by their path prefix {#v1/…} / {#v2/…} (the new folder/path system — no
-v1/-v2 suffix); and v1/v2 must stay fully functional without code changes (kept on disk, loadable).
core/dpl/dpl.js— the parser/renderer (Phase 1) compiles.dpl→ the{default, full, suggestion_exclude}module shape.core/nodeLoader.js—loadDynamicPromptcompiles.dpl(cached) with a JS-sidecar bridge (script:/{js:}/insert js:, relative or root-absolute paths) and stillrequires.jsfor v1/v2;dynamicPromptNameslists v1+v2+v3 (a.jsis a generator only without a same-name.dpl); group/force-prefix dirs scoped to v3.core/browserLoader.js— mirror via Vite:?rawglob for.dpl, sidecar bridge resolving modules from the bundle by joined key; v2 included; groups/force-prefix scoped to v3.core/stages/dynamicPrompt.js— three-way split: bare → v3 (expandActive),{#v1/…}/{#v2/…}→expandFrozen(prefix strip + suffix resolve within that generation, autoAdd off).{#any}/groups over v3.promptFilesAndSuggestions.js— buckets v1/v2 as frozen (v1Files/v2Files, full-path tokens, out of the suggestion pool), v3/user →userFiles, the rest → active v3 pools.- Verified:
npm run smokegreen; newscripts/dpl-engine-check.mjsexpands{#cave},{#v3/scene/cave},{#v1/castle},{#v2/scene/cave},{#v2/cave}, sidecar-backed{#vibrant-art}/{#space}/{#entity}/{#random-words}end-to-end with lists resolving and no leftover tokens;npm --prefix web-app run buildgreen (595 modules,.dplbundled); lint clean. Docs:reference/prompt-dsl.mdupdated to v3-default + path-prefix addressing; next-steps 7a added.
DPL Phase 2 — COMPLETE: style + prompt + user converted (CODE)
- style/ (19): pure
.dplfor the publicprompts templates (3d-*, comic, fluffy-animal, funko-3d-print, gold-pendant, lowpoly, needle-felt, plushie, psychedelic, space-hologram, sports-logo, sticker, anime-irl, retro-poster —autoAdd=falsedropped);.jssidecars for silhouette (sentence interpolation) and vibrant-art (space-joinedcolorful()). - prompt/ (builtins, all
.jssidecars): random-words, random, simple-random, extra-random, artists, d (+_force-prefixmarker for{#prompt/…}parity); fx is the one pure-.dplpartial. - user/ beach-merk → pure
.dpl(its direct-import composition became{#city}/{#nature}tokens; the partial-case switch becameone of (60% nothing)). - Phase 2 done: the entire v2 catalog is mirrored in
data/dynamic-prompts/v3/; v1 + v2 kept frozen. Lint clean, 25 engine tests green, all v3.dplrender viadpl-validate.mjs. Next: Phase 3 (wire the node + browser loaders so.dpl/sidecars actually load in the live engine).
DPL Phase 2 — subject category converted (CODE)
- Converted
subject/(12 generators, 17 files)..dpl+.jssidecars for the entity type-system:entity.js(core polymorphic picker, ported from v2) + thin wrappersanimal/person/living-entity/entity-namethat callentity(…, kind/nameOnly). Pure.dpl: wildlife, portrait, portrait-animal, portrait-person, portrait-princess, knight, and furry (its per-item glow/colour clothing became aone ofnested inside arepeat 0 to 5 timesblock — a nice proof the language covers the count+choice combo). Lint clean, all render.
DPL Phase 2 — scene category converted (CODE)
- Converted the whole
scene/category to v3 (29 files). Pure.dpl: beach, castle, city, cave, house, landscape, log-cabin, mountains, micro-city, micro-landscape, park, room, ruins, school-room, ship, store-interior, storefront, underwaterscape, vehicle, zoo, settlement..dpl+.jssidecars for the logic/space-join cases: futuristic (dedup flags + else-if), great-bridge + great-tree (size() helper + inline interpolation), space (maybeAddSize + switches), spaceship (space-joined adjectives). Faithful to v2 (one-of-with-miss for weather-fx, maybe/otherwise condition blocks, hand-emphasis((x))/[x]passthrough). - All render via
dpl-validate.mjs; sidecar.jspassnode --check; lint clean (one portedno-dupe-else-iffalse-positive marked intentional —_.random()re-rolls).
DPL Phase 2 — bullet-default fix + fragment category converted (CODE)
- Fidelity fix in
dpl.js: a bare simple-clause bullet now defaults to 50% (was rendering as always-on). Structural bullets (one of/repeat/maybe/otherwise/block) stay unconditional; plain (non-bullet) lines stay always-on;otherwisepairs only with an authored gate, not a defaulted 50%. Caught via the cave/vehicle validate output emitting every clause. Added tests (default-50%, case-sensitive ref) — 25 checks green. - Converted the whole
fragment/category to.dpl(color, eerie, mystical, water, lava, crystal, glow, neon, nature, underwater, expressive, ice, general-state, room-state; weather already done). Spot-checked outputs match v2 (color's 50%-nothing split, nature's gated repeat loops, water's gated one-of-with-miss, glow/neon pick-one, maybe/otherwise condition blocks).scripts/dpl-validate.mjsrenders all green.
DPL Phase 1 — engine built + verified, v3 samples converted (CODE)
First real code. Owner: "implement it; make a v3 folder; convert v2; later the prompt box uses DPL by default and 3 wrapper-preset boxes." Phased to protect the working app — Phase 1 = engine + samples (non-breaking).
src/core/dpl/dpl.js— DPL parser + weighted-layer renderer.compileDpl(source, bridge)→{ default, full, suggestion_exclude }(the existing generator-module shape, so engine/loader untouched). Supports: front-matter (type/description/suggestions/script),=×≥3 headings, first-indent unit, plain (always) vs- bullet(50% default),[n]weights (auto from 1000, local recursive sort), gates (NN%/maybe/NN% chance/otherwisechain),one of+N of/A to B of(digits) with(NN% nothing)+ weighted options,repeat N times/A to B times(gate-first vs per-iteration),+call/insert/go to/go back, and the JS bridge (script:/{js:path}/insert js:+ ctx section/prompt/list/expand). Section lookup is case-sensitive; counts are digits only (owner calls).scripts/dpl-test.mjs— 22 checks (weights, gates, choice/multi, miss, repeat modes, maybe/otherwise, local call, case-sensitivity, front-matter, JS bridge). Green.scripts/dpl-validate.mjsrenders the real v3 files.data/dynamic-prompts/v3/— convertedfragment/weather,prompt/fx,scene/cave,scene/vehicleto.dpl; all render correctly (+cave-type/+weather-fxresolve, picks/gates/repeats work).- Lint clean, prettier-formatted. Not yet wired into the loaders/engine (Phase 3) — purely additive so far.
v3/DPL — weight syntax + build order settled (no code)
Owner decisions: auto weights start at 1000 (+1 per line); explicit weight = [n] at the start of a
line/block (e.g. [900]) — recommended over @900, safe because A1111 [..]/salt [..] only appear inside
payload text; a section/ref carries a weight (include-site > section-declared > auto); and DPL is built
first because it's wired into the v3 engine (start/end UX waits on it).
reference/dpl-design.md: added a "Weights (v3 layers)" subsection (auto-1000,[n]syntax, section/ref weights + precedence, marker rationale) and updated the grammar sketch.plans/v3-layers.md: moved auto-start/[n]syntax into Resolved mechanics, added a Build-order section (DPL first), trimmed the open questions to start/end UX + read-only vars + auto-weight collisions.plans/next-steps.md: item 7 (DPL) reframed as the active next build wired into v3; item 8 updated.
v3 — weighted-layer engine concept captured (exploratory, no code)
Owner explained the v3 direction (the model the DPL authors for): v1/v2 build an ordered string (position = meaning); v3 orders by weight over a layer tree. Every file/section/line is a layer; the user's prompt box is the root and each building block a child. Weights are plain sort numbers (not A1111 attention) — lower = rendered closer to the front; a line auto-gets the next number unless an explicit weight (syntax TBD) leads the line/block. Files have no weight (modularity); their inner sections/lines do. Render = recursive depth-first, each layer sorts its own children (words never leave their layer; no global scale; ties = document order). The engine does no de-duplication — that's a non-goal. Big goal: retire "full prompts" so regular blocks stop auto-emitting start/end framing (the real v2 duplication source) — the engine supplies start/end blocks (leaning presets over a settings page; maybe 3 boxes), so building blocks are mostly "middle". Building blocks read read-only variables, never define them; wants deep engine↔blocks↔user integration (mostly future).
- Wrote exploratory
../../plans/v3-layers.md(model, retiring full prompts, read-only vars, DPL's role, and the open mechanics: weight scope, sort key, tie-break, start/end UX, weight syntax). Linked fromdpl-design.mdscope note and next-steps item 8. No source changed.
DPL — design revision 3: two-way JS↔DPL bridge (no code)
Owner: JS must also be able to grab/call DPL sections from code (a section executes and returns its string), and JS can hand control to the DPL side which returns results — symmetric, recursable.
- Updated
../../reference/dpl-design.md§6 (renamed "The JavaScript bridge (two-way)"): split DPL→JS (script:/{js:path}/insert js: path) from JS→DPL via actxobject —ctx.section(name)(run a local section),ctx.prompt("#name")(run another generator),ctx.list(name)(pull a list value),ctx.expand(snippet)(run an inline DPL/token string) — all returning the rendered string, plus read-onlyctx.settings/ctx.random. Added a workeddetail-stack.jsexample, a coverage-table row, and refined open questions (bridge surface + recursion limits, local-section vs global-prompt resolution). No source changed.
DPL — design revision 2: data-not-code, JS holds all logic (no code)
Owner sharpened the boundary: no in-language programming — remove counters, flags, variables, arguments,
and counter-driven loops; all of that goes to referenced JavaScript (relative to the .dpl, or
root-absolute from project root with a leading /). JS reference is not only inline calls but also block
insertions. Flow is linear top-to-bottom: go to jumps and naturally falls downward, with go back as
the return. one of may pick more than one (two of:, 1 to 3 of:). Indentation unit = the first
indent in the file (tab or N spaces, then consistent).
- Rewrote
../../reference/dpl-design.md: reframed the language as "data, not code"; deleted the counters/flags section and all variable/argument/repeat whileconstructs; added the three JS forms —script:(whole file),{js:path}(inline value),insert js: path(block insertion) — with relative/root-absolute path resolution; rewrote flow aroundgo to/go back/+name(inline call) /insert name(block), with the heading-boundary fall-through rule; addedN of/A to B ofmulti-select; added the first-indent indentation rule; moved the wholeentityfamily back to JS (script: entity.js). Updated the requirements table, grammar sketch, and open questions. No source changed.
DPL — design revision: control flow, gating, repetition (no code)
Owner feedback on the proposal: drop auto-fx/auto-artists (v3 will differ) and the adult front-matter
key (NSFW inferred by name only, to avoid mixups); section underline is ≥3 = (not exactly 4); no
start/middle/end framing (that's v2 — v3 differs) but keep Start as the entry section; expand
branching (different branches, jumps, loops, counters, probabilities); handle gating with simple syntax
options; and fix repetition so it isn't always "gate-then-repeat" — allow ungated loops and per-iteration
probabilities.
- Rewrote
../../reference/dpl-design.md: removed the three front-matter keys; set the heading underline to=×≥3; reframed lines as plain (always) vs bullet (50% default) in document order with no enforced anchor/tail. Added a gating cheat-sheet (NN%,maybe:/otherwise:/NN% chance:blocks, chainedotherwise NN% chance:), explicit repetition modes (repeat N timesungated;NN% repeat A to B timesgate-first;repeat N times: NN% …per-iteration;repeat while COUNTER > 0with a safety cap), counters & flags (set/clear/add/subtract,→ set flagon a chosen option,when/unlessguards), and branching/jumps (+namecall vsgo to namejump,branch:weighted jump, conditional jumps). Showedentityrewritten in pure Tier-1 using the state model, updated the requirements-coverage table and grammar sketch. Added a v3 clarifier todpl-language.md. No source changed.
DPL — expanded language proposal (no code)
Follow-up to the mockup analysis. Owner: "understand the comprehensive needs of the v2 generators and propose a much more complete language; keep it Markdown-readable and usable by non-programmers; referencing JavaScript inside it (separate file/functions) is fine."
- Read a broad structural sample of v2 generators —
subject/{entity,animal,portrait,portrait-person,knight},fragment/{color,general-state,weather,expressive,fx},prompt/{random-words,random,d},style/vibrant-art,scene/{city,vehicle},user/beach-merk— plushelpers/keywordRepeater.js. Extracted the full pattern set: weighted/empty choices, grouped if/else blocks, count-loops, flags set-on-pick read later (entity), parameters + filtered pools (entity), settings read/write directives (auto-fx off), space-joined sub-builders (colorful), direct-import composition (beach-merk), and helper builtins. - Designed a two-tier language: Tier 1 is pure Markdown (YAML front-matter for metadata/directives, setext
headings for sections,
- bulletclauses withNN%,maybe:/otherwise:indented blocks,one of (NN% nothing):,N to M of:repetition, unchanged{list}/{#prompt}/<exp>tokens) — proven to cover the large majority of the catalog with worked rewrites. Tier 2 is a JS escape hatch (script:whole-file delegation,{js:fn}inline value) for the logic-heavy ~10% (entity,random-words,random,d,vibrant-art'scolorful). Compiles to the existing(settings,…) => stringcontract so the engine is untouched. - Wrote
../../reference/dpl-design.md: goals, the Tier-1 spec, real-generator rewrites, the Tier-2 hatch, a requirements-coverage table (every v2 pattern → exemplar file → coverage), a grammar sketch, and open questions. Cross-linked fromreference/dpl-language.md; added next-steps item 7. No source changed.
DPL (Dynamic Prompt Language) — mockup analysis (no code)
Owner: "begin making the DPL; two revisions are proposed in assets/mockup but largely incomplete. Analyze
and fully understand both mockups, write up the understanding in notes, and analyze what makes a full vs
partial prompt in the v2 JS (start/middle/end layering + balancing)."
- Read both mockups against the generators they encode:
mockup-of-dpl-language.txt=v2/scene/beach.js(winter/tropical via*failchains + named sections),mockup-of-dpl-language2.txt=v2/scene/cave.js(rev 2 adds theselect N name:pick-one group). Confirmed the grammar:==== ====banners,=- key: value+=- full promptmetadata,name:/start:sections, plain base line (START),*/*NN%/*failoptional clauses (MIDDLE),+namesubroutine call,#nameembed,{name}list,(…)weight group, and the trailing bare line (END). These map 1:1 to the JS_.random()<paccretion idiom. - Documented the full vs partial model: full = START anchor + balanced MIDDLE body (independent ~50%
coin-flips, some weighted,
*fail/selectfor mutual exclusion) + unconditional END context line, then the engine auto-appends{#fx}/{#artists}. Partial = empty base, all-MIDDLE", frag"clauses, no END,fullflag absent.=- full prompt⇒export const full = true, read bypromptFilesAndSuggestions.js. - Wrote it all up in new
../../reference/dpl-language.md(incl. a grammar table, the full/partial structural breakdown, and an open-questions list for the parser/compiler design); cross-linked fromreference/dynamic-prompts.mdandreference/prompt-dsl.md. No source changed.
prompt/ category rename + "Prompts" navbar grouping (2.5.0)
Owner: "remove force group entry on user; engine should be called prompt and force-prefix; within it
danbooru→d, drop -prompt, and bare random→random-words; for the category, have Prompts with a single
v1/v2, then full/partial below; reverse v1/v2 order; better category descriptions (no group mechanics)."
Also confirmed v1 has no partials (all v1 generators are full).
- Renames.
git mv v2/engine v2/prompt; within:random→random-words, thenrandom-prompt→random(order matters to avoid collision),simple-random-prompt→simple-random,extra-random-prompt→extra-random,danbooru→d; added av2/prompt/_force-prefixmarker. No sibling/relative breakage exceptextra-random.js's./random-prompt.jsimport (→./random.js), caught by the web build. Regenerated the 121 sidecars (updated metaDmap + better, group-free category descriptions).dynPromptTagskey updated tov2/prompt/d. - Default.
settings.prompt/ engine fallback / SPA defaults repointed{#random}→{#random-words}so the default still produces the keyword pile (now that bare{#random}resolves to the composite). - Reverted user force-group. Removed
v2/user/_enable-group-list;{#user}is not a group (auto-rule: the 1-file folder doesn't qualify). - Navbar.
getBlockstags the two dynamic blocks withsubLabelfull/partial;Home.jsxrenders a single "Prompts" group head with one v1/v2 switch (orderv1 v2, v2 default) and full/partial sub-tabs, with Expansions/Lists/Special as normal tabs. - Verified
{#prompt/random},{#prompt/d},{#d},{#random-words},{#prompt}group, default gen; lint 0 errors, smoke green, web build 495 modules.
Pick-one groups + {#any} wildcard + Full/Partial navbar (2.5.0)
Owner reversed the earlier "no groups / no wildcard" calls once reframed: "implement the list auto group entry that selects one but instead of one word it would be one generator… #any and its sfw and nsfw variants… do this for expansions too." Plus a navbar spec: "full and partial separation in category; v1/v2 as superset links on the navbar next to Dynamic/Partial, v2 default; remove the v1/v2 toggle from category text."
- Pick-one groups (engine). Re-added
dynPromptGroupDirs/readDynPromptGroup+ newexpansionGroupDirs/readExpansionGroupin both loaders (autoGroupListDirsover the names,.groupfiles,_enable/_disable-group-listmarkers). Dyn stage:{#folder}→pickFrom(dynGroupMembers,…)runs one random member (resolvePool includes group dirs so{#scene}suffix-matchesv2/scene). Expansion stage:<folder>(or.group) splices one random member. All gate-aware viahasNsfwToken. {#any}family.{#any}/{#any-sfw}/{#any-nsfw}pick one generator from the whole v2 catalog with{keyword}-style variant semantics; the variant suffix is parsed ONLY for{#any}so a real nsfw-token generator name still resolves directly.dynPromptManifest.jsrestored RESERVED_ANY/ isReservedAny/dynGroupMembers.- SPA navbar.
getBlocksnow emits twodynVersionedblocks — "Dynamic prompts" (full) and "Partial prompts" (partial) — each folder-grouped with clickable{#folder}pills (+ a clickable{#any}"any" pill in the full tab), carryingvariants.v2/variants.v1. Expansion folder pills are clickable<folder>too.Home.jsx:dynVerstate drives the blocks viaeffItems; v1/v2 rendered as superset links on the cat-tabs navbar (v2 default); the inline chip-area toggle removed. - Verified: temp checks ({#scene}, {#fragment}, {#any}/-sfw/-nsfw, <lighting>, <detail>, direct
{#beach}) all resolve;
npm run lint0 errors,npm run smokegreen,npm --prefix web-app run build493 modules. Bumped to 2.5.0. Un-rejected the group/wildcard entry indecisions/rejected.md(kept the lasting rule: a group/wildcard resolves to one concrete generator, never a line union).
Dynamic prompts: {#name} sigil + gating/wildcard + uniform SPA (2.4.0)
Owner follow-ups after the 2.3.0 parity pass: "there needs to be uniformity… # doesn't work for dynamic
prompts because we need the / and the UX is bad — use {#…} like {#random}… bring over a lot of the
standards we skipped." Then, mid-pass: "no clickable group folders for dynamic prompts — they're not lists
of words, they're scripts with a specific input and output… no group entry lists."
- Sigil →
{#name}. Stage regex/\{#([\w/-]+)\}/g; loop guardprompt.includes("{#"); list stage hardened with ap1.startsWith("#")skip so{#x}is never mis-pulled as a list. Migrated 204 internal refs in 54 v2 generators viascripts/migrate-dynprompt-sigil.mjs(line-based, skips comments, idempotent(?<!\{)guard). v1 has zero internal#refs (verified) so it stayed frozen. Updated non-generator refs:core/engine.js+settings.js+ websettings.jsdefaults ({#random}),promptFilesAndSuggestions.jssuggestion builder,genImg.js,Home.jsx. Left the classicsrc/web/*jQuery#idselectors + Pug frontend alone (legacy). - Standards. NSFW gating by name token (
isGatedDynPrompt = hasNsfwToken || gatedDynPrompts), applied in the stage (gated → "") and the suggestion pools;src/dynPromptManifest.js(dynPromptTags). - Group + wildcard reversal. First implemented implied
{#folder}groups (random member) +.groupfiles + enable/disable markers, plus a{#any}random-pick wildcard; the owner rejected the whole "pick one of many" model for dynamic prompts ("they're scripts with a specific input and output", "#any — this isn't a list where we need to pick out an entry"), so all of it was removed — folders are pure organization and every{#name}names one concrete generator. Recorded indecisions/rejected.md. - Uniform SPA.
getBlockscollapses Full/Partial/User/V1 into one Dynamic prompts block with category-folder pills (plain labels, not clickable groups), and carries anitemsV1variant;Home.jsxrenders a v1/v2 toggle (dynMode) on the chip-area header. - Verified: gates resolve end-to-end (temp expansion checks for
{#scene/beach},{#castle-v1},{#random}),npm run lint0 errors,npm run smokegreen,npm --prefix web-app run build493 modules. BumpedVERSION+package.json→ 2.4.0.
Dynamic prompts brought to parity with lists/expansions + v2/ reorg (2.3.0)
Owner: "upgrade the dynamic prompts to the same new standards we set for lists and expansions including on the ui side and the file side" → "reorganize completely, add to a v2 folder at root, move user submitted ones to v2 as well" → "don't mess with classic server… in notes only use the new one, the classic is being replaced, read-only reference only."
First committed the 18 leftover ../../src helper-import fixups as their own commit (completing 2.2.2)
so the reorg landed clean. Then:
- Reorg (file side). Wrote
scripts/reorg-dynprompts-v2.mjswith an explicitname→categorymap; it moved the 79 top-level generators +user-submitted/beach-merkintodata/dynamic-prompts/v2/{scene,subject,fragment,style,engine,user}/(v1/ frozen) and rewrote every relative import by resolving the old→new absolute path (so../../src/helpers/…→../../../../src/…from a v2 file, and cross-category siblings like../fragment/nature.js/../scene/city.jsare correct). 80 files moved. - Suffix resolution.
core/stages/dynamicPrompt.jsnow importsresolveName, splits the catalog intov1/(reached via#name-v1) vs v2 (reached bare), and resolves#nameby path suffix against the right subset;#user-namestrips the alias and resolves intov2/user/. Every old#beach/#fx/#artistsreference still works. - Loaders.
nodeLoader+browserLoadergotreadDynPromptMeta,dynPromptForcedPrefixDirs, a_-prefix skip, andcompareNamessorting indynamicPromptNames(nodeLoader reusesnamesUnder). - Sidecars.
scripts/dynprompt-meta/write-dynprompt-meta.mjs(mirror of the expansion meta script) wrote 121<name>.jsonfiles (113 generators + 8 folders); v1 are auto-described as frozen mirrors. - Classifier + SPA.
promptFilesAndSuggestions.jsclassifies by path (v1/→V1,v2/user/→User, else full/partial via thefullflag) and stores each v2 generator by itscomputeButtonNamestoken (bare, since basenames are unique) so#token+ the random-suggestion builder keep working. The SPA (web-app/src/lib/promptEngine.js) keeps the Full/Partial/User/V1 sections (owner's choice, not folder pills) but adds description tooltips,compareNamessort, and shortest-#tokendisplay. - Gotcha caught by the build. 10
v1/*generators import the sharedentityBasicKeywordsfrom the movedentity.js; smoke never loads v1 (the classifier skips it), so onlyvite buildflagged the unresolved../entity.js— repointed to../v2/subject/entity.js. Lesson added toCLAUDE.md+ the new architecture note: always run both gates.
Verification: npm run lint 0 errors, npm run smoke green (suggestion now emits suffix-resolved bare
tokens), npm --prefix web-app run build 492 modules green. Classic server + prompt-modules/ left
untouched (read-only legacy). Docs: new data/dynamic-prompts/README.md +
reference/dynamic-prompts-architecture.md, updated
reference/dynamic-prompts.md, status.md, CLAUDE.md. Bumped VERSION + package.json to 2.3.0.
Moved dynamic-prompts/ from src/ to data/ (2.2.2)
Owner: "move dynamic prompts from src to data — it's an exception." The #name generators are
executable .js but are authored like the rest of the prompt content, so they now live under
data/dynamic-prompts/ (the one deliberate src/→data/ exception, recorded in decisions/architecture.md
and CLAUDE.md). git mv src/dynamic-prompts data/dynamic-prompts (history preserved; v1/ +
user-submitted/ came along). Loader path edits: src/prompt-modules/dynamic-prompt.js requires now
prefixed ../../data/; core/nodeLoader.js joins rootDir/data/dynamic-prompts; core/browserLoader.js
glob → ../../data/dynamic-prompts/**/*.js. The generator files import shared helpers out of src/, so
their relative imports were rewritten (18 of them): top-level ../helpers/…→../../src/helpers/… and
../promptFilesAndSuggestions.js→../../src/…; v1/ ../../helpers/…→../../../src/helpers/…. The
first build caught exactly those 18 unresolved imports; after the rewrite npm run smoke (node + legacy
loaders) and npm --prefix web-app run build (browser glob, 370 modules) are both green, lint 0 errors.
Doc note: editing files via PowerShell Get-Content|Set-Content / positional Set-Content silently
no-op'd on this machine — [System.IO.File]::ReadAllText/WriteAllText was reliable (added to the
PowerShell-not-bash caution context).
Expansions: rename detail/legacy* + port _force-prefix (2.2.1)
Owner: "remove 'detail' from legacy since it's in the folder and require the prefix." Renamed
detail/legacy-detail→detail/legacy and detail/legacy-person-detail→detail/legacy-person (the detail/
folder now carries that meaning) and added a detail/_force-prefix marker, so the editor shows/inserts
<detail/legacy> / <detail/legacy-person>. Ported _force-prefix to the expansion side: generalized
nodeLoader markedDirs(marker, base) + new expansionForcedPrefixDirs(), a **/_force-prefix glob +
generalized markerDirs(files, marker, seg) in browserLoader, and fed it to computeButtonNames in
promptEngine. Updated the one code reference (src/dynamic-prompts/futuristic.js: <legacy-detail> →
<detail/legacy>) and the meta-script keys. As with lists, force-prefix is display-only — suffix resolution
still works — so the prefix is surfaced for context, not hard-required. npm test + vite build green.
Expansions brought to parity with the list system (2.2.0)
Owner: "we did a lot of work on lists with folder nesting and groups and tooltips and conventions … lets bring
what we can of that to expansions." Ported the portable parts of the keyword-list modernization to
data/expansions/, leaving out the list-only machinery that doesn't fit copy/paste snippets (per owner: groups
/ clickable folder pills aren't needed — expansions just copy/paste). Decisions captured via the question tool:
full parity, and leave expansion names/content as-is (zero-risk to existing <name> references).
What changed:
- Folder nesting + path-suffix resolution.
git mv'd the 9 flat expansions into category folders —detail/(legacy-detail, legacy-person-detail),style/(pixelart, dap),lighting/(rays, candlelight),subject/(coffecup, flower-pic),scene/(underwater-anime-irl) — basenames unchanged.readExpansionnow resolves a bare/partial<name>by path suffix via the sharedresolveName(), so every existing<rays>/<legacy-detail>reference in the dynamic prompts still resolves untouched. Discovery is recursive in both loaders (namesUnder()in nodeLoader;**/*.txtglob in browserLoader) and skips_-prefixed internal files (same convention as lists). - Description sidecars → tooltips. Added a
<name>.jsonper expansion + a<folder>.jsonper category (14 files), written by the new re-runnablescripts/expansion-meta/write-expansion-meta.mjs(mirror ofwrite-list-meta.mjs). Both loaders gainedreadExpansionMeta(name). - SPA token cloud categorized.
promptEngine.getBlocksbuilds the "Expansions" block as folder categories with a (non-clickable) category pill per folder + description tooltips and shortcomputeButtonNamestokens, mirroring the Lists block. The classic Pug editor stays flat (consistent with how it renders lists). - Docs. New
data/expansions/README.mdandnotes/reference/expansions-architecture.md(records what was ported and what was deliberately left out and why).
Not ported, on purpose: random-union groups, clickable group/folder pills, SFW/NSFW file splitting,
_force-prefix (all expansion basenames are unique). Content review: the 9 snippets are hand-authored and
benign — no safety filter needed.
Verification: npm run lint + npm run smoke (npm test) and vite build green. Version bumped 2.1.0 →
2.2.0 (MINOR; a feature). Committed on dev; master ship held for the owner's go-ahead per the deployment
hold.