pict-section-prompteditor 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,6 +9,7 @@ A prompt crafting and management section for the [Pict](https://github.com/fable
9
9
  ## Features
10
10
 
11
11
  - Typed prompts with markdown segments (Context / Request / Success Criteria and friends), plus **fixed preamble** segments for locked team standards
12
+ - A per-prompt "segment headings in the output" toggle, saved with the prompt: on for structured documents (`## Context` above each block), off for bare bodies
12
13
  - Weighted word list matrices: `[["Tyrannosaurus", 3], ["Diplodocus", 1]]` draws 75/25, with live share percentages as you curate
13
14
  - The `{~WordListEntry:Name~}` template expression (short form `{~WLE:~}`), with an optional miss default: `{~WLE:Name:fallback~}`
14
15
  - Generation runs the **real pict template engine**, so every pict expression works inside a prompt
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pict-section-prompteditor",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Prompt crafting and management section for Pict: typed prompts with segments (context, request, success criteria), curated word list matrices with weights, a {~WordListEntry:Name~} template expression for weighted-random generation, batch generation with zip download, and a pluggable data provider that defaults to in-memory AppData.",
5
5
  "main": "source/Pict-Section-PromptEditor.js",
6
6
  "files": [
@@ -31,6 +31,12 @@ function slug(pText)
31
31
 
32
32
  /**
33
33
  * Assemble a prompt's markdown source from its type's segments.
34
+ *
35
+ * Segment headings (## Context and friends) are on by default; a prompt that
36
+ * carries IncludeSegmentHeadings: false compiles bare segment bodies instead,
37
+ * so the author decides per prompt whether the structure shows in the output.
38
+ * The document title heading stays governed by the IncludeTitleHeading option.
39
+ *
34
40
  * @param {object} pPrompt - the prompt record
35
41
  * @param {object} pType - the resolved prompt type
36
42
  * @param {object} [pOptions] - { IncludeTitleHeading: true, SegmentHeadingLevel: 2 }
@@ -40,6 +46,7 @@ function assembleSource(pPrompt, pType, pOptions)
40
46
  {
41
47
  let tmpOptions = pOptions || {};
42
48
  let tmpIncludeTitle = (typeof tmpOptions.IncludeTitleHeading === 'undefined') ? true : !!tmpOptions.IncludeTitleHeading;
49
+ let tmpIncludeSegmentHeadings = !(pPrompt && pPrompt.IncludeSegmentHeadings === false);
43
50
  let tmpHeading = '#'.repeat(Math.max(1, Math.min(6, Number(tmpOptions.SegmentHeadingLevel) || 2)));
44
51
  let tmpSegments = (pType && Array.isArray(pType.Segments)) ? pType.Segments : [];
45
52
  let tmpBodies = (pPrompt && pPrompt.Segments) ? pPrompt.Segments : {};
@@ -60,7 +67,9 @@ function assembleSource(pPrompt, pType, pOptions)
60
67
  if (tmpSegment.Optional || tmpSegment.Fixed) { continue; }
61
68
  tmpBody = '(nothing written for this segment yet)';
62
69
  }
63
- tmpParts.push(tmpHeading + ' ' + String(tmpSegment.Name || tmpSegment.Key) + '\n\n' + tmpBody);
70
+ tmpParts.push(tmpIncludeSegmentHeadings
71
+ ? (tmpHeading + ' ' + String(tmpSegment.Name || tmpSegment.Key) + '\n\n' + tmpBody)
72
+ : tmpBody);
64
73
  }
65
74
 
66
75
  return tmpParts.join('\n\n') + '\n';
@@ -16,9 +16,11 @@
16
16
  * drawn by {~WordListEntry:Name~} is weight / sum(weights).
17
17
  *
18
18
  * Prompt { Key, Title, TypeKey, Segments: { segmentKey: markdown },
19
- * Author, Meta, CreatedAt, UpdatedAt }
19
+ * IncludeSegmentHeadings, Author, Meta, CreatedAt, UpdatedAt }
20
20
  * A crafted prompt. Segments hold the markdown for each of the prompt
21
- * type's segments, keyed by segment Key. Meta is an opaque object the
21
+ * type's segments, keyed by segment Key. IncludeSegmentHeadings (default
22
+ * true) decides whether the compiler writes the ## segment headings above
23
+ * each body in the assembled output. Meta is an opaque object the
22
24
  * provider round-trips untouched -- the seam where a host hangs ratings,
23
25
  * version pointers, or anything else.
24
26
  *
@@ -208,6 +210,7 @@ class InMemoryPromptProvider extends PromptDataProvider
208
210
  Title: String(tmpDraft.Title || 'Untitled prompt'),
209
211
  TypeKey: String(tmpDraft.TypeKey),
210
212
  Segments: (tmpDraft.Segments && typeof tmpDraft.Segments === 'object') ? this._clone(tmpDraft.Segments) : {},
213
+ IncludeSegmentHeadings: (tmpDraft.IncludeSegmentHeadings === false) ? false : true,
211
214
  Author: tmpDraft.Author || null,
212
215
  Meta: (typeof tmpDraft.Meta === 'undefined') ? {} : this._clone(tmpDraft.Meta),
213
216
  CreatedAt: tmpNow,
@@ -225,6 +228,7 @@ class InMemoryPromptProvider extends PromptDataProvider
225
228
  if (typeof tmpPatch.Title !== 'undefined') { tmpPrompt.Title = String(tmpPatch.Title); }
226
229
  if (typeof tmpPatch.TypeKey !== 'undefined') { tmpPrompt.TypeKey = String(tmpPatch.TypeKey); }
227
230
  if (typeof tmpPatch.Segments !== 'undefined') { tmpPrompt.Segments = this._clone(tmpPatch.Segments || {}); }
231
+ if (typeof tmpPatch.IncludeSegmentHeadings !== 'undefined') { tmpPrompt.IncludeSegmentHeadings = (tmpPatch.IncludeSegmentHeadings === false) ? false : true; }
228
232
  if (typeof tmpPatch.Meta !== 'undefined') { tmpPrompt.Meta = this._clone(tmpPatch.Meta); }
229
233
  tmpPrompt.UpdatedAt = this._now();
230
234
  return Promise.resolve(this._clone(tmpPrompt));
@@ -136,6 +136,8 @@ const _DefaultConfiguration =
136
136
  .pspe-editor-head { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
137
137
  .pspe-editor-title { flex: 1; min-width: 200px; font-weight: 650; font-size: 15px; }
138
138
  .pspe-type-desc { font-size: 12px; color: var(--theme-color-text-muted, #97a1ab); margin: -6px 0 12px; }
139
+ .pspe-editor-opts { margin: -4px 0 12px; }
140
+ .pspe-editor-opt { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--theme-color-text-muted, #97a1ab); cursor: pointer; user-select: none; }
139
141
 
140
142
  .pspe-segment {
141
143
  border: 1px solid var(--theme-color-border-light, #e7ecf0); border-radius: 10px;
@@ -254,6 +256,7 @@ const _DefaultConfiguration =
254
256
  {~TS:PromptEditor-PromptActions:Record.ActionsSlot~}
255
257
  </div>
256
258
  <div class="pspe-type-desc">{~D:Record.TypeDescription~}</div>
259
+ {~TS:PromptEditor-EditorOptions:Record.OptionsSlot~}
257
260
  {~TS:PromptEditor-Segment:Record.Segments~}
258
261
  {~TS:PromptEditor-GenerateBar:Record.GenerateSlot~}
259
262
  {~TS:PromptEditor-PreviewPanel:Record.PreviewPanelSlot~}`
@@ -282,6 +285,17 @@ const _DefaultConfiguration =
282
285
  Hash: 'PromptEditor-PromptActions',
283
286
  Template: /*html*/`<button class="pspe-btn pspe-btn-sm" title="Duplicate this prompt" onclick="_Pict.views['{~D:Record.ViewHash~}'].duplicatePrompt()">Duplicate</button><button class="pspe-btn pspe-btn-sm pspe-btn-danger" onclick="_Pict.views['{~D:Record.ViewHash~}'].deletePrompt()">Delete</button>`
284
287
  },
288
+ {
289
+ Hash: 'PromptEditor-EditorOptions',
290
+ Template: /*html*/`
291
+ <div class="pspe-editor-opts">
292
+ <label class="pspe-editor-opt" title="When off, the assembled prompt is just the segment bodies, no ## headings above them.">
293
+ <input type="checkbox" {~D:Record.SegmentHeadingsChecked~}
294
+ onchange="_Pict.views['{~D:Record.ViewHash~}'].setSegmentHeadings(this.checked)">
295
+ Segment headings in the output
296
+ </label>
297
+ </div>`
298
+ },
285
299
  {
286
300
  Hash: 'PromptEditor-Segment',
287
301
  Template: /*html*/`
@@ -909,6 +923,11 @@ class PictViewPromptEditor extends libPictView
909
923
  SelectedAttr: (pTypeOption.Key === pPrompt.TypeKey) ? 'selected' : ''
910
924
  })),
911
925
  ActionsSlot: pReadOnly ? [] : [{ ViewHash: this.Hash }],
926
+ OptionsSlot: pReadOnly ? [] :
927
+ [{
928
+ ViewHash: this.Hash,
929
+ SegmentHeadingsChecked: (pPrompt.IncludeSegmentHeadings === false) ? '' : 'checked'
930
+ }],
912
931
  Segments: tmpSegments,
913
932
  GenerateSlot: pReadOnly ? [] :
914
933
  [{
@@ -1077,6 +1096,7 @@ class PictViewPromptEditor extends libPictView
1077
1096
  TypeKey: tmpPrompt.TypeKey,
1078
1097
  Title: tmpPrompt.Title + ' (copy)',
1079
1098
  Segments: JSON.parse(JSON.stringify(tmpPrompt.Segments || {})),
1099
+ IncludeSegmentHeadings: tmpPrompt.IncludeSegmentHeadings !== false,
1080
1100
  Meta: JSON.parse(JSON.stringify(tmpPrompt.Meta || {})),
1081
1101
  Author: this.options.CurrentUser
1082
1102
  })
@@ -1123,6 +1143,24 @@ class PictViewPromptEditor extends libPictView
1123
1143
  .catch((pError) => this._toast('Segment save failed: ' + pError.message, 'error'));
1124
1144
  }
1125
1145
 
1146
+ // The per-prompt "segment headings in the output" toggle: saves with the
1147
+ // prompt, and the compiler reads it on every assemble (preview, generate,
1148
+ // zip). No re-render needed - the checkbox itself holds the new state -
1149
+ // but an open preview re-rolls so what you see matches what you would get.
1150
+ setSegmentHeadings(pChecked)
1151
+ {
1152
+ let tmpPrompt = this._activePrompt();
1153
+ if (!tmpPrompt) { return; }
1154
+ tmpPrompt.IncludeSegmentHeadings = !!pChecked;
1155
+ this._provider.updatePrompt(tmpPrompt.Key, { IncludeSegmentHeadings: !!pChecked })
1156
+ .then((pSaved) =>
1157
+ {
1158
+ this._fire('onPromptSaved', pSaved);
1159
+ if (this._ui.Preview && this._ui.Preview.PromptKey === tmpPrompt.Key) { this.previewOnce(); }
1160
+ })
1161
+ .catch((pError) => this._toast('Save failed: ' + pError.message, 'error'));
1162
+ }
1163
+
1126
1164
  toggleSegmentPreview(pSegmentKey)
1127
1165
  {
1128
1166
  let tmpPrompt = this._activePrompt();