pict-section-form 1.1.0 → 1.1.2

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.
Files changed (45) hide show
  1. package/docs/_cover.md +3 -0
  2. package/docs/_sidebar.md +5 -0
  3. package/docs/_version.json +3 -3
  4. package/docs/examples/README.md +3 -0
  5. package/docs/examples/change_tracking/change_tracking.js +9437 -9427
  6. package/docs/examples/diagram_form/README.md +116 -0
  7. package/docs/examples/diagram_form/diagram_form_example.min.js +42 -0
  8. package/docs/examples/diagram_form/excalidraw-iframe-host.html +25 -0
  9. package/docs/examples/diagram_form/excalidraw-wrapper.css +1 -0
  10. package/docs/examples/diagram_form/excalidraw-wrapper.min.js +3327 -0
  11. package/docs/examples/diagram_form/index.html +116 -0
  12. package/docs/examples/diagram_form/manifest.json +13 -0
  13. package/docs/examples/diagram_form/react-vendor.min.js +9 -0
  14. package/docs/examples/dynamic_analysis/dynamic_analysis_application.js +6549 -6550
  15. package/docs/examples/gradebook/gradebook_application.min.js +2 -2
  16. package/docs/examples/ndt_field_test/ndt_field_test.js +9437 -9427
  17. package/docs/examples/richtext_form/README.md +132 -0
  18. package/docs/examples/richtext_form/index.html +82 -0
  19. package/docs/examples/richtext_form/richtext_form_example.min.js +42 -0
  20. package/docs/examples/scope_mathematics/scope_mathematics.js +9437 -9427
  21. package/docs/examples/simple_table/simple_tabular_application.min.js +2 -2
  22. package/docs/examples/superhero_studio/README.md +199 -0
  23. package/docs/examples/superhero_studio/excalidraw-iframe-host.html +25 -0
  24. package/docs/examples/superhero_studio/excalidraw-wrapper.css +1 -0
  25. package/docs/examples/superhero_studio/excalidraw-wrapper.min.js +3327 -0
  26. package/docs/examples/superhero_studio/index.html +166 -0
  27. package/docs/examples/superhero_studio/manifest.json +13 -0
  28. package/docs/examples/superhero_studio/react-vendor.min.js +9 -0
  29. package/docs/examples/superhero_studio/superhero_studio_example.min.js +42 -0
  30. package/docs/index.html +2 -2
  31. package/docs/retold-catalog.json +45 -1
  32. package/docs/retold-keyword-index.json +11843 -7677
  33. package/example_applications/diagram_form/DiagramForm-Example-Application.js +145 -0
  34. package/example_applications/diagram_form/html/index.html +116 -0
  35. package/example_applications/diagram_form/package.json +29 -0
  36. package/example_applications/richtext_form/RichTextForm-Example-Application.js +176 -0
  37. package/example_applications/richtext_form/html/index.html +82 -0
  38. package/example_applications/richtext_form/package.json +28 -0
  39. package/example_applications/superhero_studio/SuperheroStudio-Example-Application.js +313 -0
  40. package/example_applications/superhero_studio/html/index.html +166 -0
  41. package/example_applications/superhero_studio/package.json +29 -0
  42. package/example_applications/superhero_studio/superheroes/Superheroes.js +386 -0
  43. package/package.json +3 -9
  44. package/source/providers/inputs/Pict-Provider-Input-Diagram.js +1 -31
  45. package/source/providers/inputs/Pict-Provider-Input-RichText.js +1 -32
@@ -0,0 +1,145 @@
1
+ /**
2
+ * DiagramForm-Example-Application.js
3
+ *
4
+ * Demonstrates the Diagram InputType from pict-section-excalidraw inside a
5
+ * pict-section-form. The form boots with every Diagram field in VIEW mode
6
+ * (inline themed SVG). A page button toggles into edit mode (Excalidraw).
7
+ *
8
+ * Theme demo:
9
+ *
10
+ * The saved SVG carries `var(--diagram-ink, #1B1F23)` etc., with hex
11
+ * fallbacks. A "Theme" button on the page swaps a <style> tag that defines
12
+ * those custom properties — the view-mode SVG visibly recolors without
13
+ * re-rendering anything. No editor bundle touched.
14
+ */
15
+
16
+ const libPictApplication = require('pict-application');
17
+ const libPictSectionForm = require('../../source/Pict-Section-Form.js');
18
+
19
+ const _FormDescriptors =
20
+ {
21
+ 'ArchDiagram':
22
+ {
23
+ Name: 'Architecture Diagram',
24
+ Hash: 'ArchDiagram',
25
+ DataType: 'String',
26
+ PictForm:
27
+ {
28
+ Section: 'Diagram',
29
+ Row: 1,
30
+ Width: 12,
31
+ InputType: 'Diagram',
32
+ Diagram:
33
+ {
34
+ ThemeColors: true,
35
+ Height: '480px',
36
+ EditorImplementation: 'react'
37
+ }
38
+ }
39
+ }
40
+ };
41
+
42
+ // A minimal pre-baked SVG (notebook style, hex fallbacks already in place via
43
+ // themeifySVG) so the form has something to display in view mode at boot. In a
44
+ // real app this would come from the record being edited.
45
+ const _DemoSvg =
46
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 200">
47
+ <metadata><!-- payload-type:application/vnd.excalidraw+json --><!-- payload-start --> e30= <!-- payload-end --></metadata>
48
+ <g>
49
+ <rect x="20" y="40" width="100" height="50" rx="6" stroke="var(--diagram-ink, #1B1F23)" fill="var(--diagram-paper, #FDFCF7)" stroke-width="2"/>
50
+ <text x="70" y="72" text-anchor="middle" style="fill: var(--diagram-ink, #1B1F23); font-family: sans-serif;">Client</text>
51
+
52
+ <path d="M 120 65 L 220 65" stroke="var(--diagram-accent, #E66C2C)" fill="none" stroke-width="2" marker-end="url(#arrow)"/>
53
+
54
+ <rect x="220" y="40" width="120" height="50" rx="6" stroke="var(--diagram-ink, #1B1F23)" fill="var(--diagram-paper, #FDFCF7)" stroke-width="2"/>
55
+ <text x="280" y="72" text-anchor="middle" style="fill: var(--diagram-ink, #1B1F23); font-family: sans-serif;">API Server</text>
56
+
57
+ <defs>
58
+ <marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
59
+ <path d="M 0 0 L 8 4 L 0 8 z" fill="var(--diagram-accent, #E66C2C)"/>
60
+ </marker>
61
+ </defs>
62
+ </g>
63
+ </svg>`;
64
+
65
+ const _FormManifest =
66
+ {
67
+ Scope: 'DiagramDemoForm',
68
+ Sections:
69
+ [
70
+ { Hash: 'Diagram', Name: 'Architecture' }
71
+ ],
72
+ Descriptors: _FormDescriptors
73
+ };
74
+
75
+ class DiagramFormApplication extends libPictSectionForm.PictFormApplication
76
+ {
77
+ constructor(pFable, pOptions, pServiceHash)
78
+ {
79
+ super(pFable, pOptions, pServiceHash);
80
+
81
+ // Register the Diagram input provider. It lives in pict-section-form
82
+ // and lazy-requires pict-section-excalidraw on the first edit toggle —
83
+ // the view-mode SVG path doesn't load the editor bundle at all.
84
+ let libDiagramInput = libPictSectionForm.DiagramInput;
85
+ this.pict.addProvider(
86
+ libDiagramInput.default_configuration.ProviderIdentifier,
87
+ libDiagramInput.default_configuration,
88
+ libDiagramInput
89
+ );
90
+
91
+ this.pict.AppData.DiagramDemoForm = { ArchDiagram: _DemoSvg };
92
+ }
93
+
94
+ onAfterInitializeAsync(fCallback)
95
+ {
96
+ if (this.pict.views.PictFormMetacontroller)
97
+ {
98
+ this.pict.views.PictFormMetacontroller.viewMarshalDestination = 'AppData.DiagramDemoForm';
99
+ }
100
+ super.onAfterInitializeAsync(() =>
101
+ {
102
+ try { this.marshalDataFromAppDataToDynamicViews(); }
103
+ catch (pErr) { if (this.log) this.log.warn('[diagram_form] initial marshal failed', { error: pErr.message }); }
104
+ return fCallback();
105
+ });
106
+ }
107
+
108
+ demo_toggleMode(pInputHash)
109
+ {
110
+ let tmpProvider = this.pict.providers['Pict-Input-Diagram'];
111
+ if (!tmpProvider) return;
112
+ tmpProvider.toggleMode(pInputHash, (pErr) =>
113
+ {
114
+ if (pErr && this.log) this.log.warn('[diagram_form demo] toggleMode error', { error: pErr.message });
115
+ this._refreshModeLabel(pInputHash);
116
+ });
117
+ }
118
+
119
+ _refreshModeLabel(pInputHash)
120
+ {
121
+ let tmpProvider = this.pict.providers['Pict-Input-Diagram'];
122
+ if (!tmpProvider) return;
123
+ let tmpMode = tmpProvider.getMode(pInputHash);
124
+ let tmpLabel = (tmpMode === 'edit') ? 'Done' : 'Edit';
125
+ let tmpBtn = (typeof document !== 'undefined') ? document.getElementById('toggle-' + pInputHash) : null;
126
+ if (tmpBtn) tmpBtn.textContent = tmpLabel;
127
+ }
128
+ }
129
+
130
+ module.exports = DiagramFormApplication;
131
+
132
+ // Extend the parent's default_configuration so MainViewportViewIdentifier
133
+ // (= "PictFormMetacontroller") survives — without it the form has no
134
+ // auto-render target and the page comes up blank.
135
+ module.exports.default_configuration = Object.assign({},
136
+ libPictSectionForm.PictFormApplication.default_configuration,
137
+ {
138
+ Name: 'Diagram Form Example',
139
+ Hash: 'DiagramFormExample',
140
+ pict_configuration:
141
+ {
142
+ Product: 'DiagramForm-Example',
143
+ DefaultFormManifest: _FormManifest
144
+ }
145
+ });
@@ -0,0 +1,116 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Diagram Form Input — Pict Example</title>
7
+
8
+ <style id="PICT-CSS"></style>
9
+ <style id="diagram-theme">
10
+ /* Theme tokens that the saved SVG resolves to. Default = notebook. */
11
+ :root
12
+ {
13
+ --diagram-ink: #1B1F23;
14
+ --diagram-paper: #FDFCF7;
15
+ --diagram-accent: #E66C2C;
16
+ --diagram-highlight: #FFD966;
17
+ --diagram-deemphasis: #A0A0A0;
18
+ --diagram-link: #2E7D74;
19
+ }
20
+ </style>
21
+ <style>
22
+ *, *::before, *::after { box-sizing: border-box; }
23
+ body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: #FAF8F4; color: #2A2520; }
24
+
25
+ .pict-example-header { display: flex; align-items: stretch; background: #264653; border-bottom: 3px solid #E76F51; }
26
+ .pict-example-badge { background: #E76F51; color: #FAEDCD; padding: 0.6rem 1rem; font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; }
27
+ .pict-example-app-name { padding: 0.6rem 1rem; color: #FAEDCD; font-size: 1.1rem; font-weight: 600; }
28
+ .pict-example-module { margin-left: auto; padding: 0.6rem 1rem; color: #D4A373; font-size: 0.75rem; letter-spacing: 0.03em; align-self: center; }
29
+
30
+ .page-content { padding: 2rem; max-width: 920px; margin: 0 auto; }
31
+ .intro { color: #5A4A3C; font-size: 0.9rem; line-height: 1.6; margin-bottom: 24px; }
32
+ .intro code { background: #F0E8DA; padding: 1px 6px; border-radius: 3px; font-family: 'SFMono-Regular', Menlo, monospace; font-size: 0.85em; }
33
+
34
+ .controls { background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 6px; padding: 14px 16px; margin-bottom: 20px; display: flex; flex-wrap: wrap; gap: 12px; align-items: center; font-size: 0.85rem; }
35
+ .controls button { padding: 6px 14px; font-size: 0.85rem; font-weight: 600; color: #FAEDCD; background: #264653; border: none; border-radius: 4px; cursor: pointer; }
36
+ .controls button:hover { background: #E76F51; }
37
+ .controls .swatch { display: inline-flex; gap: 6px; align-items: center; padding: 4px 10px; background: #F4EEE2; border-radius: 4px; cursor: pointer; user-select: none; }
38
+ .controls .swatch::before { content: ''; display: inline-block; width: 12px; height: 12px; border-radius: 2px; border: 1px solid rgba(0,0,0,0.1); background: var(--swatch-color, #fff); }
39
+
40
+ #Pict-Form-Container { background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 6px; padding: 20px; min-height: 280px; }
41
+
42
+ .notes { margin-top: 24px; padding: 14px 16px; background: #FFF8E7; border-left: 3px solid #E9C46A; border-radius: 3px; font-size: 0.82rem; color: #5A4A3C; }
43
+ .notes strong { color: #2A2520; }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <header class="pict-example-header">
48
+ <span class="pict-example-badge">Pict Example</span>
49
+ <span class="pict-example-app-name">Diagram Form Input</span>
50
+ <span class="pict-example-module">pict-section-excalidraw</span>
51
+ </header>
52
+
53
+ <main class="page-content">
54
+ <p class="intro">
55
+ A pict-section-form field whose <code>InputType</code> is <code>Diagram</code>. Boots in
56
+ <strong>view</strong> mode — the saved SVG is written inline, with theme-driven colors.
57
+ Click <em>Edit</em> to flip into Excalidraw (lazy-loads the editor bundle the first time).
58
+ Click a theme swatch — the view-mode SVG visibly re-colors without a re-render.
59
+ </p>
60
+
61
+ <div class="controls">
62
+ <button id="toggle-ArchDiagram" onclick="_Pict.PictApplication.demo_toggleMode('ArchDiagram')">Edit</button>
63
+
64
+ <span class="swatch" style="--swatch-color:#1B1F23" onclick="window.applyTheme('notebook')">Notebook</span>
65
+ <span class="swatch" style="--swatch-color:#EAEAEA" onclick="window.applyTheme('dark')">Dark</span>
66
+ <span class="swatch" style="--swatch-color:#5DA9E9" onclick="window.applyTheme('blueprint')">Blueprint</span>
67
+ </div>
68
+
69
+ <div id="Pict-Form-Container"></div>
70
+
71
+ <div class="notes">
72
+ <strong>Verifying:</strong>
73
+ In view mode, change theme — the inline SVG re-colors via CSS only. In edit mode, draw
74
+ something + click <em>Done</em>; the new SVG is themeified on save, so it picks up the
75
+ same CSS variables. Inspect the hidden input's value in DevTools to see the
76
+ <code>var(--diagram-ink, #...)</code> rewriting in action.
77
+ </div>
78
+ </main>
79
+
80
+ <link rel="stylesheet" href="./excalidraw-wrapper.css">
81
+ <script src="./pict.min.js"></script>
82
+ <!-- Excalidraw vendor bundle (React + Excalidraw). The Diagram InputType's
83
+ editor mode looks for window.PictSectionExcalidrawVendor; without these
84
+ tags, switching the field to edit shows a "wrapper not loaded" notice. -->
85
+ <script src="./react-vendor.min.js"></script>
86
+ <script src="./excalidraw-wrapper.min.js"></script>
87
+ <script src="./diagram_form_example.min.js"></script>
88
+ <script>
89
+ const _Themes =
90
+ {
91
+ notebook: { ink:'#1B1F23', paper:'#FDFCF7', accent:'#E66C2C', highlight:'#FFD966', deemphasis:'#A0A0A0', link:'#2E7D74' },
92
+ dark: { ink:'#EAEAEA', paper:'#1B1F23', accent:'#FFB570', highlight:'#FFD966', deemphasis:'#666666', link:'#7FC4BD' },
93
+ blueprint: { ink:'#08296B', paper:'#E5F0FB', accent:'#5DA9E9', highlight:'#F4D35E', deemphasis:'#8AA3B6', link:'#1F77B4' }
94
+ };
95
+ window.applyTheme = function(pName)
96
+ {
97
+ let tmpStyle = document.getElementById('diagram-theme');
98
+ let tmpT = _Themes[pName] || _Themes.notebook;
99
+ tmpStyle.textContent =
100
+ ':root { ' +
101
+ '--diagram-ink:' + tmpT.ink + ';' +
102
+ '--diagram-paper:' + tmpT.paper + ';' +
103
+ '--diagram-accent:' + tmpT.accent + ';' +
104
+ '--diagram-highlight:' + tmpT.highlight + ';' +
105
+ '--diagram-deemphasis:' + tmpT.deemphasis + ';' +
106
+ '--diagram-link:' + tmpT.link + ';' +
107
+ ' }';
108
+ };
109
+
110
+ Pict.safeOnDocumentReady(() =>
111
+ {
112
+ Pict.safeLoadPictApplication(window.DiagramFormExample, 1);
113
+ });
114
+ </script>
115
+ </body>
116
+ </html>
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "diagram_form_example",
3
+ "version": "1.0.0",
4
+ "description": "Pict-section-form Diagram InputType demo — Excalidraw (edit mode) + inline themed SVG (view mode).",
5
+ "main": "DiagramForm-Example-Application.js",
6
+ "scripts": {
7
+ "start": "node DiagramForm-Example-Application.js",
8
+ "build": "npx quack build && npx quack copy"
9
+ },
10
+ "author": "steven velozo <steven@velozo.com>",
11
+ "license": "MIT",
12
+ "devDependencies": {},
13
+ "copyFilesSettings": {
14
+ "whenFileExists": "overwrite"
15
+ },
16
+ "copyFiles": [
17
+ { "from": "./html/*", "to": "./dist/" },
18
+ { "from": "../../node_modules/pict/dist/*", "to": "./dist/" },
19
+ { "from": "../../node_modules/pict-section-excalidraw/vendor/excalidraw-built/*", "to": "./dist/" }
20
+ ],
21
+ "retold": {
22
+ "ExampleApplication": {
23
+ "Stage": true,
24
+ "Title": "Diagram Form Input",
25
+ "Summary": "A pict-section-form field whose InputType is Diagram — boots in view mode (inline SVG, theme-recolorable), toggles into edit mode (Excalidraw) on demand. Demonstrates the themeify pipeline: change the --diagram-* CSS variables and the view-mode SVG visibly re-colors without a re-render.",
26
+ "Complexity": "Intermediate"
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * RichTextForm-Example-Application.js
3
+ *
4
+ * Demonstrates the RichText InputType from pict-section-markdowneditor inside
5
+ * a pict-section-form. The form boots with every RichText field in VIEW mode
6
+ * (rendered markdown). A "Edit" button on the page calls
7
+ * `provider.setMode(hash, 'edit')` to flip a single field into the markdown
8
+ * editor; "Done" flips it back.
9
+ *
10
+ * Image handling has three live demo modes:
11
+ * - AllowImages=false → image paste is rejected with a toast
12
+ * - AllowImages=true, no uploader → base64 inline (default editor behaviour)
13
+ * - AllowImages=true + uploader → a stubbed `uploadImage` on the application
14
+ * hands back a fake CDN URL after 600ms.
15
+ */
16
+
17
+ const libPictApplication = require('pict-application');
18
+ const libPictSectionForm = require('../../source/Pict-Section-Form.js');
19
+
20
+ const _FormDescriptors =
21
+ {
22
+ 'Body':
23
+ {
24
+ Name: 'Body',
25
+ Hash: 'Body',
26
+ DataType: 'String',
27
+ PictForm:
28
+ {
29
+ Section: 'Article',
30
+ Row: 1,
31
+ Width: 12,
32
+ InputType: 'RichText',
33
+ RichText:
34
+ {
35
+ AllowImages: true,
36
+ ImageUploader: 'uploadImage',
37
+ Height: '320px'
38
+ }
39
+ }
40
+ }
41
+ };
42
+
43
+ const _FormManifest =
44
+ {
45
+ Scope: 'RichTextDemoForm',
46
+ Sections:
47
+ [
48
+ {
49
+ Hash: 'Article',
50
+ Name: 'Article Body'
51
+ }
52
+ ],
53
+ Descriptors: _FormDescriptors
54
+ };
55
+
56
+ class RichTextFormApplication extends libPictSectionForm.PictFormApplication
57
+ {
58
+ constructor(pFable, pOptions, pServiceHash)
59
+ {
60
+ super(pFable, pOptions, pServiceHash);
61
+
62
+ // Register the RichText input provider. The provider lives in
63
+ // pict-section-form (where the InputType + form templates are defined);
64
+ // it lazy-requires pict-section-markdowneditor on the first edit toggle
65
+ // so a never-edited form pays nothing for CodeMirror.
66
+ let libRichTextInput = libPictSectionForm.RichTextInput;
67
+ this.pict.addProvider(
68
+ libRichTextInput.default_configuration.ProviderIdentifier,
69
+ libRichTextInput.default_configuration,
70
+ libRichTextInput
71
+ );
72
+
73
+ // Seed AppData with a record so the form has something to render in view mode.
74
+ this.pict.AppData.RichTextDemoForm =
75
+ {
76
+ Body: '## Welcome\n\nThis form is in **view** mode by default. Click *Edit* to swap a field into the markdown editor.\n\n- Boot-time DOM is just rendered markdown — no CodeMirror loaded.\n- First `setMode("edit")` lazy-loads the editor bundle.\n- Image uploads are routed through `uploadImage(file, descriptor, cb)`.'
77
+ };
78
+
79
+ // Allow-images toggle backing flag (read by uploadImage stub).
80
+ this._imageUploaderEnabled = true;
81
+ }
82
+
83
+ onAfterInitializeAsync(fCallback)
84
+ {
85
+ if (this.pict.views.PictFormMetacontroller)
86
+ {
87
+ this.pict.views.PictFormMetacontroller.viewMarshalDestination = 'AppData.RichTextDemoForm';
88
+ }
89
+ super.onAfterInitializeAsync(() =>
90
+ {
91
+ try { this.marshalDataFromAppDataToDynamicViews(); }
92
+ catch (pErr) { if (this.log) this.log.warn('[richtext_form] initial marshal failed', { error: pErr.message }); }
93
+ return fCallback();
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Stubbed image uploader. Resolves to a fake CDN URL after a brief delay so
99
+ * the demo shows the upload-in-progress UX. Real apps wire this to their own
100
+ * storage (S3, a content-delivery API, etc.).
101
+ *
102
+ * The signature matches the pict-section-markdowneditor onImageUpload
103
+ * contract: return true if we'll handle it; call fCallback(err, url).
104
+ */
105
+ uploadImage(pFile, pDescriptor, fCallback)
106
+ {
107
+ if (!this._imageUploaderEnabled)
108
+ {
109
+ // Returning false from the inner provider tells the markdown editor
110
+ // to fall through to base64 inline.
111
+ return false;
112
+ }
113
+
114
+ setTimeout(() =>
115
+ {
116
+ let tmpFakeURL = '/uploads/' + Date.now() + '-' + (pFile.name || 'image.png').replace(/[^A-Za-z0-9._-]/g, '_');
117
+ fCallback(null, tmpFakeURL);
118
+ if (this.log) this.log.info('[richtext_form demo] uploadImage resolved', { url: tmpFakeURL });
119
+ }, 600);
120
+
121
+ return true;
122
+ }
123
+
124
+ // ----------------------------------------------------------------------------
125
+ // Demo control surface — invoked from inline onclick handlers in index.html.
126
+ // ----------------------------------------------------------------------------
127
+
128
+ demo_toggleMode(pInputHash)
129
+ {
130
+ let tmpProvider = this.pict.providers['Pict-Input-RichText'];
131
+ if (!tmpProvider) return;
132
+ tmpProvider.toggleMode(pInputHash, (pErr) =>
133
+ {
134
+ if (pErr && this.log) this.log.warn('[richtext_form demo] toggleMode error', { error: pErr.message });
135
+ this._refreshModeLabel(pInputHash);
136
+ });
137
+ }
138
+
139
+ demo_setAllowImages(pInputHash, pAllowed)
140
+ {
141
+ let tmpDescriptor = _FormDescriptors[pInputHash];
142
+ if (tmpDescriptor) tmpDescriptor.PictForm.RichText.AllowImages = !!pAllowed;
143
+ }
144
+
145
+ demo_setUploaderEnabled(pEnabled)
146
+ {
147
+ this._imageUploaderEnabled = !!pEnabled;
148
+ }
149
+
150
+ _refreshModeLabel(pInputHash)
151
+ {
152
+ let tmpProvider = this.pict.providers['Pict-Input-RichText'];
153
+ if (!tmpProvider) return;
154
+ let tmpMode = tmpProvider.getMode(pInputHash);
155
+ let tmpLabel = (tmpMode === 'edit') ? 'Done' : 'Edit';
156
+ let tmpBtn = (typeof document !== 'undefined') ? document.getElementById('toggle-' + pInputHash) : null;
157
+ if (tmpBtn) tmpBtn.textContent = tmpLabel;
158
+ }
159
+ }
160
+
161
+ module.exports = RichTextFormApplication;
162
+
163
+ // Extend the parent's default_configuration so MainViewportViewIdentifier
164
+ // (= "PictFormMetacontroller") survives — without it the form has no
165
+ // auto-render target and the page comes up blank.
166
+ module.exports.default_configuration = Object.assign({},
167
+ libPictSectionForm.PictFormApplication.default_configuration,
168
+ {
169
+ Name: 'RichText Form Example',
170
+ Hash: 'RichTextFormExample',
171
+ pict_configuration:
172
+ {
173
+ Product: 'RichTextForm-Example',
174
+ DefaultFormManifest: _FormManifest
175
+ }
176
+ });
@@ -0,0 +1,82 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>RichText Form Input — Pict Example</title>
7
+
8
+ <style id="PICT-CSS"></style>
9
+ <style>
10
+ *, *::before, *::after { box-sizing: border-box; }
11
+ body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: #FAF8F4; color: #2A2520; }
12
+
13
+ .pict-example-header { display: flex; align-items: stretch; background: #264653; border-bottom: 3px solid #E76F51; }
14
+ .pict-example-badge { background: #E76F51; color: #FAEDCD; padding: 0.6rem 1rem; font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; }
15
+ .pict-example-app-name { padding: 0.6rem 1rem; color: #FAEDCD; font-size: 1.1rem; font-weight: 600; }
16
+ .pict-example-module { margin-left: auto; padding: 0.6rem 1rem; color: #D4A373; font-size: 0.75rem; letter-spacing: 0.03em; align-self: center; }
17
+
18
+ .page-content { padding: 2rem; max-width: 920px; margin: 0 auto; }
19
+ .intro { color: #5A4A3C; font-size: 0.9rem; line-height: 1.6; margin-bottom: 24px; }
20
+ .intro code { background: #F0E8DA; padding: 1px 6px; border-radius: 3px; font-family: 'SFMono-Regular', Menlo, monospace; font-size: 0.85em; }
21
+
22
+ .controls { background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 6px; padding: 14px 16px; margin-bottom: 20px; display: flex; flex-wrap: wrap; gap: 16px; align-items: center; font-size: 0.85rem; }
23
+ .controls label { display: flex; align-items: center; gap: 6px; cursor: pointer; }
24
+ .controls button { padding: 6px 14px; font-size: 0.85rem; font-weight: 600; color: #FAEDCD; background: #264653; border: none; border-radius: 4px; cursor: pointer; }
25
+ .controls button:hover { background: #E76F51; }
26
+
27
+ #Pict-Form-Container { background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 6px; padding: 20px; }
28
+
29
+ /* The form has one section + one input. */
30
+ .notes { margin-top: 24px; padding: 14px 16px; background: #FFF8E7; border-left: 3px solid #E9C46A; border-radius: 3px; font-size: 0.82rem; color: #5A4A3C; }
31
+ .notes strong { color: #2A2520; }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <header class="pict-example-header">
36
+ <span class="pict-example-badge">Pict Example</span>
37
+ <span class="pict-example-app-name">RichText Form Input</span>
38
+ <span class="pict-example-module">pict-section-markdowneditor</span>
39
+ </header>
40
+
41
+ <main class="page-content">
42
+ <p class="intro">
43
+ A pict-section-form field whose <code>InputType</code> is <code>RichText</code>. Boots in
44
+ <strong>view</strong> mode — just rendered markdown, no CodeMirror loaded. Click <em>Edit</em>
45
+ to flip into the editor (lazy-loads the markdown editor bundle on first toggle).
46
+ </p>
47
+
48
+ <div class="controls">
49
+ <button id="toggle-Body" onclick="_Pict.PictApplication.demo_toggleMode('Body')">Edit</button>
50
+
51
+ <label>
52
+ <input type="checkbox" id="allow-images" checked onchange="_Pict.PictApplication.demo_setAllowImages('Body', this.checked)">
53
+ Allow image paste
54
+ </label>
55
+
56
+ <label>
57
+ <input type="checkbox" id="use-uploader" checked onchange="_Pict.PictApplication.demo_setUploaderEnabled(this.checked)">
58
+ Use ImageUploader stub
59
+ <span style="color:#8A7F72; margin-left:6px; font-size:0.75rem;">(off = base64 inline)</span>
60
+ </label>
61
+ </div>
62
+
63
+ <div id="Pict-Form-Container"></div>
64
+
65
+ <div class="notes">
66
+ <strong>Verifying:</strong>
67
+ Open DevTools → Network. With <em>Edit</em> never clicked, no CodeMirror request fires. Click <em>Edit</em>
68
+ once and the markdown-editor bundle loads. Click <em>Done</em> to return to view mode — the editor view
69
+ is torn down, but its content lives in the field's hidden form input.
70
+ </div>
71
+ </main>
72
+
73
+ <script src="./pict.min.js"></script>
74
+ <script src="./richtext_form_example.min.js"></script>
75
+ <script>
76
+ Pict.safeOnDocumentReady(() =>
77
+ {
78
+ Pict.safeLoadPictApplication(window.RichtextFormExample || window.RichTextFormExample, 1);
79
+ });
80
+ </script>
81
+ </body>
82
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "richtext_form_example",
3
+ "version": "1.0.0",
4
+ "description": "Pict-section-form RichText InputType demo — markdown editor (edit mode) + rendered markdown (view mode), with pluggable image uploader.",
5
+ "main": "RichTextForm-Example-Application.js",
6
+ "scripts": {
7
+ "start": "node RichTextForm-Example-Application.js",
8
+ "build": "npx quack build && npx quack copy"
9
+ },
10
+ "author": "steven velozo <steven@velozo.com>",
11
+ "license": "MIT",
12
+ "devDependencies": {},
13
+ "copyFilesSettings": {
14
+ "whenFileExists": "overwrite"
15
+ },
16
+ "copyFiles": [
17
+ { "from": "./html/*", "to": "./dist/" },
18
+ { "from": "../../node_modules/pict/dist/*", "to": "./dist/" }
19
+ ],
20
+ "retold": {
21
+ "ExampleApplication": {
22
+ "Stage": true,
23
+ "Title": "RichText Form Input",
24
+ "Summary": "A pict-section-form field whose InputType is RichText — boots in view mode (rendered markdown), toggles into edit mode (CodeMirror) on demand. Demonstrates the AllowImages knob and the pluggable ImageUploader override.",
25
+ "Complexity": "Intermediate"
26
+ }
27
+ }
28
+ }