retold 4.0.4 → 4.0.8

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 (93) hide show
  1. package/.claude/launch.json +47 -0
  2. package/.claude/settings.local.json +45 -107
  3. package/CLAUDE.md +9 -7
  4. package/README.md +9 -7
  5. package/Retold-Modules-Manifest.json +616 -0
  6. package/docs/README.md +7 -1
  7. package/docs/{cover.md → _cover.md} +1 -1
  8. package/docs/_sidebar.md +30 -3
  9. package/docs/architecture/architecture.md +5 -2
  10. package/docs/architecture/dependencies/_generate-graph.js +186 -0
  11. package/docs/architecture/dependencies/_generate-svg.js +364 -0
  12. package/docs/architecture/dependencies/in-ecosystem-dependency-graph-generation.md +97 -0
  13. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.json +3168 -0
  14. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.md +221 -0
  15. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.svg +664 -0
  16. package/docs/architecture/documentation-style-guide.md +65 -0
  17. package/docs/architecture/example-app-style-guide.md +154 -0
  18. package/docs/architecture/modules.md +33 -12
  19. package/docs/architecture/templating/data-access.md +196 -0
  20. package/docs/architecture/templating/data-formatting.md +350 -0
  21. package/docs/architecture/templating/data-generation.md +72 -0
  22. package/docs/architecture/templating/debugging.md +181 -0
  23. package/docs/architecture/templating/entity.md +99 -0
  24. package/docs/architecture/templating/iteration.md +170 -0
  25. package/docs/architecture/templating/jellyfish-deep-dive.md +271 -0
  26. package/docs/architecture/templating/jellyfish-templates.md +476 -0
  27. package/docs/architecture/templating/logic.md +185 -0
  28. package/docs/architecture/templating/ref-breakpoint.md +38 -0
  29. package/docs/architecture/templating/ref-data.md +51 -0
  30. package/docs/architecture/templating/ref-dateonlyformat.md +43 -0
  31. package/docs/architecture/templating/ref-dateonlyymd.md +39 -0
  32. package/docs/architecture/templating/ref-datetimeformat.md +59 -0
  33. package/docs/architecture/templating/ref-datetimeymd.md +44 -0
  34. package/docs/architecture/templating/ref-dejs.md +42 -0
  35. package/docs/architecture/templating/ref-digits.md +36 -0
  36. package/docs/architecture/templating/ref-dj.md +50 -0
  37. package/docs/architecture/templating/ref-dollars.md +36 -0
  38. package/docs/architecture/templating/ref-dt.md +38 -0
  39. package/docs/architecture/templating/ref-dvbk.md +46 -0
  40. package/docs/architecture/templating/ref-dwaf.md +45 -0
  41. package/docs/architecture/templating/ref-dwtf.md +45 -0
  42. package/docs/architecture/templating/ref-entity.md +47 -0
  43. package/docs/architecture/templating/ref-hce.md +29 -0
  44. package/docs/architecture/templating/ref-hcs.md +38 -0
  45. package/docs/architecture/templating/ref-join.md +45 -0
  46. package/docs/architecture/templating/ref-joinunique.md +34 -0
  47. package/docs/architecture/templating/ref-ls.md +37 -0
  48. package/docs/architecture/templating/ref-lv.md +38 -0
  49. package/docs/architecture/templating/ref-lvt.md +33 -0
  50. package/docs/architecture/templating/ref-ne.md +40 -0
  51. package/docs/architecture/templating/ref-pascalcaseidentifier.md +41 -0
  52. package/docs/architecture/templating/ref-pict.md +42 -0
  53. package/docs/architecture/templating/ref-pluckjoinunique.md +39 -0
  54. package/docs/architecture/templating/ref-rn.md +35 -0
  55. package/docs/architecture/templating/ref-rns.md +35 -0
  56. package/docs/architecture/templating/ref-sbr.md +36 -0
  57. package/docs/architecture/templating/ref-solve.md +46 -0
  58. package/docs/architecture/templating/ref-tbda.md +41 -0
  59. package/docs/architecture/templating/ref-tbr.md +43 -0
  60. package/docs/architecture/templating/ref-tbt.md +46 -0
  61. package/docs/architecture/templating/ref-template.md +40 -0
  62. package/docs/architecture/templating/ref-tfa.md +32 -0
  63. package/docs/architecture/templating/ref-tfm.md +43 -0
  64. package/docs/architecture/templating/ref-tif.md +45 -0
  65. package/docs/architecture/templating/ref-tifabs.md +41 -0
  66. package/docs/architecture/templating/ref-ts.md +41 -0
  67. package/docs/architecture/templating/ref-tsfm.md +42 -0
  68. package/docs/architecture/templating/ref-tswp.md +45 -0
  69. package/docs/architecture/templating/ref-tvs.md +48 -0
  70. package/docs/architecture/templating/ref-view.md +40 -0
  71. package/docs/architecture/templating/ref-vrs.md +39 -0
  72. package/docs/architecture/templating/solvers.md +153 -0
  73. package/docs/architecture/templating/template-composition.md +196 -0
  74. package/docs/architecture/templating/template-expressions.md +217 -0
  75. package/docs/architecture/templating/views.md +154 -0
  76. package/docs/examples/todolist/todo-list.md +1 -1
  77. package/docs/modules/apps.md +26 -0
  78. package/docs/modules/pict.md +18 -0
  79. package/docs/modules/utility.md +23 -1
  80. package/docs/retold-catalog.json +2541 -307
  81. package/docs/retold-keyword-index.json +267578 -117399
  82. package/modules/CLAUDE.md +1 -0
  83. package/modules/Checkout.sh +1 -0
  84. package/modules/Diff.sh +86 -0
  85. package/modules/Include-Retold-Module-List.sh +4 -2
  86. package/modules/Status.sh +1 -0
  87. package/modules/Update.sh +1 -0
  88. package/modules/apps/Apps.md +1 -0
  89. package/modules/utility/Utility.md +1 -0
  90. package/package.json +9 -11
  91. package/docs/retold-building-documentation.md +0 -33
  92. package/modules/.claude/settings.local.json +0 -52
  93. package/modules/Retold-Modules.md +0 -24
@@ -0,0 +1,476 @@
1
+ # Jellyfish Templates
2
+
3
+ Jellyfish is the template engine at the heart of Pict. It processes expressions embedded in strings, resolving data, evaluating logic, and composing output from reusable parts. Every Pict view, form section, and content section uses Jellyfish templates to produce its rendered output.
4
+
5
+ The name comes from the delimiters: `{~` and `~}` wrap every expression, giving template strings a distinctive visual rhythm.
6
+
7
+ ## What a Template Looks Like
8
+
9
+ A Jellyfish template is a string containing literal text mixed with expressions. Each expression starts with `{~`, followed by a tag, a colon, parameters, and closes with `~}`.
10
+
11
+ ```
12
+ {~TAG:parameters~}
13
+ ```
14
+
15
+ For example, a template that renders a user profile card:
16
+
17
+ ```html
18
+ <div class="card">
19
+ <h2>{~D:Record.DisplayName~}</h2>
20
+ <p>{~D:Record.Email~}</p>
21
+ <span>Member since {~DateTimeFormat:Record.CreateDate^MMMM YYYY~}</span>
22
+ </div>
23
+ ```
24
+
25
+ When rendered with a record like `{ DisplayName: 'Alice Chen', Email: 'alice@example.com', CreateDate: '2023-01-15T00:00:00Z' }`, this produces:
26
+
27
+ ```html
28
+ <div class="card">
29
+ <h2>Alice Chen</h2>
30
+ <p>alice@example.com</p>
31
+ <span>Member since January 2023</span>
32
+ </div>
33
+ ```
34
+
35
+ ## The Expression Anatomy
36
+
37
+ Every expression follows the same structure:
38
+
39
+ ```
40
+ {~ExpressionTag:parameter1:parameter2~}
41
+ ```
42
+
43
+ - **ExpressionTag** identifies which template expression handles this pattern. Most expressions have a long form and a shorthand (`Data` and `D`, `Template` and `T`, `Solve` and `S`).
44
+ - **Parameters** are separated by colons. What each parameter means depends on the expression. A data address like `AppData.User.Name` resolves against Pict's state. A literal like `Hello` is used as-is. A caret (`^`) separates sub-parameters within a single parameter.
45
+
46
+ ## Data Addressing
47
+
48
+ The most fundamental concept in Jellyfish is the **data address** -- a dot-notation path that resolves against Pict's unified state object.
49
+
50
+ When you write `{~D:AppData.User.Name~}`, the engine looks up `AppData.User.Name` in a state object that combines several namespaces:
51
+
52
+ | Namespace | Source | Purpose |
53
+ |-----------|--------|---------|
54
+ | `AppData` | `pict.AppData` | Persistent application state |
55
+ | `Bundle` | `pict.Bundle` | Configuration and supporting data |
56
+ | `TempData` | `pict.TempData` | Transient caches and intermediate values |
57
+ | `Record` | Current record | The data object passed to the template |
58
+ | `Context` | Context array | Hierarchical context accessible by index |
59
+ | `Scope` | Sticky scope | State carried through template processing |
60
+ | `Pict` | The Pict instance | Access to Pict itself (also aliased as `Fable`) |
61
+
62
+ Addresses use dot notation to traverse nested objects and bracket notation for arrays:
63
+
64
+ ```
65
+ AppData.Users[0].Name → first user's name
66
+ Record.Address.City → city from the current record
67
+ Bundle.Config.DefaultTheme → a configuration value
68
+ Pict.UUID → the Pict instance UUID
69
+ ```
70
+
71
+ Manyfest handles the resolution. Missing paths return `undefined` rather than throwing -- templates never crash on absent data.
72
+
73
+ ## Core Expression Categories
74
+
75
+ Jellyfish ships with 44 built-in template expressions organized into logical categories.
76
+
77
+ ### Data Access
78
+
79
+ The simplest expressions resolve a value from the address space and return it as a string.
80
+
81
+ ```html
82
+ <!-- Basic data access -->
83
+ <h1>{~D:AppData.PageTitle~}</h1>
84
+
85
+ <!-- With a fallback template if the value is empty -->
86
+ <h1>{~DWTF:Record.Title:FallbackTitle~}</h1>
87
+
88
+ <!-- With a literal fallback string -->
89
+ <h1>{~DWAF:Record.Title^Untitled~}</h1>
90
+
91
+ <!-- JSON serialization -->
92
+ <script>var config = {~DJ:AppData.Config~};</script>
93
+ ```
94
+
95
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
96
+
97
+ ### Data Formatting
98
+
99
+ Formatting expressions transform values for display -- numbers, dates, currencies, and strings.
100
+
101
+ ```html
102
+ <!-- Currency -->
103
+ <span>{~Dollars:Record.Price~}</span>
104
+
105
+ <!-- Formatted number -->
106
+ <span>{~Digits:Record.Quantity~}</span>
107
+
108
+ <!-- Date formatting -->
109
+ <time>{~DateTimeFormat:Record.PublishedDate^MMMM Do, YYYY~}</time>
110
+
111
+ <!-- Join multiple values -->
112
+ <p>{~J:, ^Record.City^Record.State^Record.Country~}</p>
113
+ ```
114
+
115
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
116
+
117
+ ### Template Composition
118
+
119
+ Templates can render other templates, enabling reuse and modular design.
120
+
121
+ ```html
122
+ <!-- Render a named template -->
123
+ {~T:UserCard:AppData.CurrentUser~}
124
+
125
+ <!-- Render a template whose name is stored in data -->
126
+ {~TBR:Record.TemplateName~}
127
+
128
+ <!-- Render a template string stored in data -->
129
+ {~TBDA:Record.TemplateContent~}
130
+
131
+ <!-- Look up a record in a map, then render a template with it -->
132
+ {~TFM:DinoCard:AppData.DinosaurMap:Record.IDDinosaur~}
133
+ ```
134
+
135
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
136
+
137
+ ### Iteration
138
+
139
+ Template sets render a template once for each item in a collection. The current item becomes `Record` inside the loop.
140
+
141
+ ```html
142
+ <!-- Render a template for each item in an array -->
143
+ <ul>
144
+ {~TS:ProductRow:AppData.Products~}
145
+ </ul>
146
+
147
+ <!-- With extra payload data available to each iteration -->
148
+ {~TSWP:ItemRow:AppData.Items:AppData.DisplayConfig~}
149
+
150
+ <!-- Iterate over values of an object or array -->
151
+ {~TVS:ValueDisplay:AppData.CategoryMap~}
152
+ ```
153
+
154
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
155
+
156
+ ### Logic and Conditionals
157
+
158
+ Conditional expressions compare values and render templates when conditions are met.
159
+
160
+ ```html
161
+ <!-- Render a template if two data values are equal -->
162
+ {~TIf:SuccessMessage::AppData.StatusCode^==^AppData.ExpectedCode~}
163
+
164
+ <!-- Compare against a literal value -->
165
+ {~TIfAbs:AdminPanel::AppData.UserRole^==^admin~}
166
+
167
+ <!-- Output literal text if a value is truthy -->
168
+ {~NE:Record.HasAvatar^<img src="avatar.png">~}
169
+
170
+ <!-- HTML comment toggling for conditional display -->
171
+ {~HCS:Record.ShowSection~}
172
+ <div class="optional-section">Content here</div>
173
+ {~HCE:Record.ShowSection~}
174
+ ```
175
+
176
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
177
+
178
+ ### Solvers
179
+
180
+ Solver expressions evaluate mathematical expressions using Fable's expression parser.
181
+
182
+ ```html
183
+ <!-- Inline math -->
184
+ <span>Total: {~S:Price*Quantity:Record~}</span>
185
+
186
+ <!-- Built-in functions -->
187
+ <span>Area: {~S:ROUND(PI()*Radius*Radius,2):Record~}</span>
188
+
189
+ <!-- String concatenation in expressions -->
190
+ <span>{~S:CONCAT("Total: $",Price):Record~}</span>
191
+
192
+ <!-- Evaluate an equation stored in data -->
193
+ {~SBR:AppData.PricingFormula:AppData.OrderData~}
194
+ ```
195
+
196
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
197
+
198
+ ### Entity Access
199
+
200
+ The Entity expression fetches records from Meadow REST APIs and renders them with a template. This is inherently asynchronous.
201
+
202
+ ```html
203
+ <!-- Fetch a Book by ID and render it -->
204
+ {~E:Book^42^BookCard~}
205
+
206
+ <!-- Fetch using a dynamic ID from data -->
207
+ {~E:Book^Record.IDBook^BookCard~}
208
+ ```
209
+
210
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
211
+
212
+ ### Views and Self-Reference
213
+
214
+ Templates can render Pict views inline or reference the Pict instance for JavaScript interop.
215
+
216
+ ```html
217
+ <!-- Render a Pict view -->
218
+ {~V:HeaderView~}
219
+
220
+ <!-- Render a view while carrying scope through -->
221
+ {~VRS:ChildView~}
222
+
223
+ <!-- Reference the Pict instance in inline JS -->
224
+ <button onclick="{~P~}.views['MyView'].doSomething()">Click</button>
225
+ ```
226
+
227
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
228
+
229
+ ### Debugging
230
+
231
+ Debugging expressions help during development -- they log values, insert breakpoints, and render object trees as HTML.
232
+
233
+ ```html
234
+ <!-- Log a message during template processing -->
235
+ {~LS:Processing user section~}
236
+
237
+ <!-- Log a resolved value with type info -->
238
+ {~LV:AppData.CurrentUser~}
239
+
240
+ <!-- Insert a debugger breakpoint -->
241
+ {~Breakpoint~}
242
+
243
+ <!-- Render an object as an interactive HTML tree -->
244
+ {~DT:AppData.DebugData~}
245
+ ```
246
+
247
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
248
+
249
+ ### Data Generation
250
+
251
+ Generate random values for testing, unique IDs, or placeholder content.
252
+
253
+ ```html
254
+ <!-- Random number between 1 and 100 -->
255
+ <span>{~RN:1,100~}</span>
256
+
257
+ <!-- Zero-padded random number string -->
258
+ <code>{~RNS:8~}</code>
259
+ ```
260
+
261
+ See [Template Expressions](architecture/templating/template-expressions.md) for the full reference.
262
+
263
+ ## Using Templates Programmatically
264
+
265
+ ### Setup
266
+
267
+ ```javascript
268
+ const libPict = require('pict');
269
+
270
+ let _Pict = new libPict({
271
+ Product: 'MyApp',
272
+ ProductVersion: '1.0.0'
273
+ });
274
+ ```
275
+
276
+ ### Parsing a Template String
277
+
278
+ The most direct way to use Jellyfish is `parseTemplate`. Pass a template string and an optional record object.
279
+
280
+ ```javascript
281
+ _Pict.AppData.SiteName = 'Bookstore';
282
+
283
+ // Simple data resolution
284
+ let tmpResult = _Pict.parseTemplate('Welcome to {~D:AppData.SiteName~}!');
285
+ // 'Welcome to Bookstore!'
286
+
287
+ // With a record
288
+ tmpResult = _Pict.parseTemplate(
289
+ '{~D:Record.Title~} by {~D:Record.Author~}',
290
+ { Title: 'Dune', Author: 'Frank Herbert' }
291
+ );
292
+ // 'Dune by Frank Herbert'
293
+ ```
294
+
295
+ ### Registering Named Templates
296
+
297
+ Named templates let you define reusable fragments and reference them by hash.
298
+
299
+ ```javascript
300
+ // Register a template
301
+ _Pict.TemplateProvider.addTemplate('BookRow',
302
+ '<tr><td>{~D:Record.Title~}</td><td>{~D:Record.Author~}</td></tr>');
303
+
304
+ // Render by hash
305
+ let tmpResult = _Pict.parseTemplateByHash('BookRow',
306
+ { Title: 'Neuromancer', Author: 'William Gibson' });
307
+ // '<tr><td>Neuromancer</td><td>William Gibson</td></tr>'
308
+ ```
309
+
310
+ ### Iterating Over Collections
311
+
312
+ Template sets render a template once per item in an array.
313
+
314
+ ```javascript
315
+ _Pict.TemplateProvider.addTemplate('BookRow',
316
+ '<tr><td>{~D:Record.Title~}</td><td>{~Dollars:Record.Price~}</td></tr>');
317
+
318
+ _Pict.AppData.Books = [
319
+ { Title: 'Dune', Price: 12.99 },
320
+ { Title: 'Neuromancer', Price: 9.99 },
321
+ { Title: 'Snow Crash', Price: 14.99 }
322
+ ];
323
+
324
+ let tmpResult = _Pict.parseTemplate(
325
+ '<table>{~TS:BookRow:AppData.Books~}</table>');
326
+ // '<table><tr><td>Dune</td><td>$12.99</td></tr>...'
327
+ ```
328
+
329
+ ### Composing Templates
330
+
331
+ Templates can reference other templates, building complex output from simple parts.
332
+
333
+ ```javascript
334
+ _Pict.TemplateProvider.addTemplate('BookRow',
335
+ '<li>{~D:Record.Title~} ({~Dollars:Record.Price~})</li>');
336
+
337
+ _Pict.TemplateProvider.addTemplate('BookList',
338
+ '<h2>{~D:AppData.ListTitle~}</h2><ul>{~TS:BookRow:AppData.Books~}</ul>');
339
+
340
+ _Pict.AppData.ListTitle = 'Science Fiction';
341
+ let tmpResult = _Pict.parseTemplateByHash('BookList');
342
+ ```
343
+
344
+ ### Solvers and Computed Values
345
+
346
+ The Solve expression evaluates math expressions with variable references resolved from data.
347
+
348
+ ```javascript
349
+ _Pict.AppData.Order = { Quantity: 3, UnitPrice: 24.99, TaxRate: 0.08 };
350
+
351
+ let tmpResult = _Pict.parseTemplate(
352
+ 'Subtotal: {~S:ROUND(Quantity*UnitPrice,2):AppData.Order~}');
353
+ // 'Subtotal: 74.97'
354
+
355
+ tmpResult = _Pict.parseTemplate(
356
+ 'Tax: {~S:ROUND(Quantity*UnitPrice*TaxRate,2):AppData.Order~}');
357
+ // 'Tax: 6.00'
358
+ ```
359
+
360
+ ### Asynchronous Rendering
361
+
362
+ Some expressions (Entity, View) are asynchronous. Use a callback for templates containing async expressions.
363
+
364
+ ```javascript
365
+ _Pict.parseTemplate(
366
+ '<div>{~E:Book^42^BookCard~}</div>',
367
+ {},
368
+ (pError, pResult) =>
369
+ {
370
+ if (pError)
371
+ {
372
+ console.error(pError);
373
+ return;
374
+ }
375
+ console.log(pResult);
376
+ });
377
+ ```
378
+
379
+ ### Conditional Rendering
380
+
381
+ ```javascript
382
+ _Pict.TemplateProvider.addTemplate('AdminBadge', '<span class="badge">Admin</span>');
383
+
384
+ _Pict.AppData.User = { Role: 'admin', Name: 'Alice' };
385
+
386
+ let tmpResult = _Pict.parseTemplate(
387
+ '{~D:AppData.User.Name~} {~TIfAbs:AdminBadge::AppData.User.Role^==^admin~}');
388
+ // 'Alice <span class="badge">Admin</span>'
389
+ ```
390
+
391
+ ## How Views Use Templates
392
+
393
+ Pict views are the primary consumers of Jellyfish templates. A view declares templates in its configuration and renders them during its lifecycle.
394
+
395
+ ```javascript
396
+ let _MyView = _Pict.addView('ProductView',
397
+ {
398
+ ViewIdentifier: 'ProductView',
399
+ DefaultRenderable: 'Product-List',
400
+ DefaultDestinationAddress: '#product-container',
401
+ Templates: [
402
+ {
403
+ Hash: 'Product-List',
404
+ Template: '<div class="products">{~TS:Product-Card:AppData.Products~}</div>'
405
+ },
406
+ {
407
+ Hash: 'Product-Card',
408
+ Template: [
409
+ '<div class="card">',
410
+ ' <h3>{~D:Record.Name~}</h3>',
411
+ ' <p>{~Dollars:Record.Price~}</p>',
412
+ ' <button onclick="{~P~}.views[\'ProductView\'].addToCart({~D:Record.IDProduct~})">',
413
+ ' Add to Cart',
414
+ ' </button>',
415
+ '</div>'
416
+ ].join('\n')
417
+ }
418
+ ],
419
+ Renderables: [
420
+ {
421
+ RenderableHash: 'Product-List',
422
+ TemplateHash: 'Product-List',
423
+ DestinationAddress: '#product-container'
424
+ }
425
+ ]
426
+ });
427
+ ```
428
+
429
+ When the view renders, it parses `Product-List`, which iterates over `AppData.Products` using the `Product-Card` template for each item. The `{~P~}` expression generates the correct JavaScript reference to the Pict instance so that click handlers can call back into view methods.
430
+
431
+ Views can also be embedded in other templates using `{~V:ViewHash~}`, enabling hierarchical composition of UI components.
432
+
433
+ ## Creating Custom Expressions
434
+
435
+ You can extend Jellyfish with your own template expressions. Create a class that extends `pict-template` and register it with Pict.
436
+
437
+ ```javascript
438
+ const libPictTemplate = require('pict-template');
439
+
440
+ class UpperCaseExpression extends libPictTemplate
441
+ {
442
+ constructor(pFable, pOptions, pServiceHash)
443
+ {
444
+ super(pFable, pOptions, pServiceHash);
445
+
446
+ this.addPattern('{~Upper:', '~}');
447
+ this.addPattern('{~UC:', '~}');
448
+ }
449
+
450
+ render(pTemplateHash, pRecord, pContextArray, pScope, pState)
451
+ {
452
+ let tmpValue = this.resolveStateFromAddress(
453
+ pTemplateHash.trim(), pRecord, pContextArray, null, pScope, pState);
454
+ return (typeof tmpValue === 'string') ? tmpValue.toUpperCase() : '';
455
+ }
456
+ }
457
+
458
+ module.exports = UpperCaseExpression;
459
+ module.exports.template_hash = 'Upper';
460
+ ```
461
+
462
+ Register it:
463
+
464
+ ```javascript
465
+ _Pict.addTemplate(require('./UpperCase-Expression.js'));
466
+
467
+ _Pict.parseTemplate('{~UC:Record.Name~}', { Name: 'alice' });
468
+ // 'ALICE'
469
+ ```
470
+
471
+ Custom expressions follow the same lifecycle as built-in ones. They have access to `this.pict`, `this.log`, and `this.resolveStateFromAddress()`. For async work, override `renderAsync` in addition to `render`.
472
+
473
+ ## Further Reading
474
+
475
+ - [Jellyfish Deep Dive](architecture/templating/jellyfish-deep-dive.md) -- Detailed internals of the template engine
476
+ - [Template Expressions](architecture/templating/template-expressions.md) -- All 44 built-in expressions with links to individual reference pages
@@ -0,0 +1,185 @@
1
+ # Logic Expressions
2
+
3
+ Logic expressions evaluate conditions and control what content appears in the rendered output. They compare values, check for truthiness, and conditionally render templates.
4
+
5
+ ## TemplateIf (TIf)
6
+
7
+ Compares two data-resolved values using an operator. If the comparison is true, renders a named template.
8
+
9
+ **Tags:** `{~TemplateIf:TEMPLATE_HASH:DATA_ADDRESS:LEFT^OPERATOR^RIGHT~}` `{~TIf:TEMPLATE_HASH:DATA_ADDRESS:LEFT^OPERATOR^RIGHT~}`
10
+
11
+ **Parameters:**
12
+
13
+ | Parameter | Description |
14
+ |-----------|-------------|
15
+ | TEMPLATE_HASH | Hash of the template to render if the condition is true |
16
+ | DATA_ADDRESS | Address of data to pass as Record to the template (can be empty) |
17
+ | LEFT | Address to resolve for the left side of the comparison |
18
+ | OPERATOR | Comparison operator (see table below) |
19
+ | RIGHT | Address to resolve for the right side of the comparison |
20
+
21
+ **Operators:**
22
+
23
+ | Operator | Meaning |
24
+ |----------|---------|
25
+ | `==` | Loose equality |
26
+ | `===` | Strict equality |
27
+ | `!=` | Loose inequality |
28
+ | `!==` | Strict inequality |
29
+ | `>` | Greater than |
30
+ | `>=` | Greater than or equal |
31
+ | `<` | Less than |
32
+ | `<=` | Less than or equal |
33
+ | `TRUE` | Left value is exactly `true` (right is ignored) |
34
+ | `FALSE` | Left value is exactly `false` (right is ignored) |
35
+ | `LNGT` | Left value's `.length` is greater than right value |
36
+ | `LENGTH_GREATER_THAN` | Same as LNGT |
37
+ | `LNLT` | Left value's `.length` is less than right value |
38
+ | `LENGTH_LESS_THAN` | Same as LNLT |
39
+
40
+ **Examples:**
41
+
42
+ ```javascript
43
+ _Pict.TemplateProvider.addTemplate('SuccessMsg',
44
+ '<span class="success">Operation succeeded!</span>');
45
+
46
+ _Pict.TemplateProvider.addTemplate('HighValueBadge',
47
+ '<span class="badge">Premium</span>');
48
+
49
+ // Compare two data values
50
+ _Pict.AppData.Response = { StatusCode: 200, ExpectedCode: 200 };
51
+ _Pict.parseTemplate(
52
+ '{~TIf:SuccessMsg::AppData.Response.StatusCode^==^AppData.Response.ExpectedCode~}');
53
+ // '<span class="success">Operation succeeded!</span>'
54
+
55
+ // Numeric comparison
56
+ _Pict.parseTemplate(
57
+ '{~TIf:HighValueBadge:Record:Record.OrderTotal^>^Record.Threshold~}',
58
+ { OrderTotal: 500, Threshold: 100 });
59
+ // '<span class="badge">Premium</span>'
60
+
61
+ // Boolean check
62
+ _Pict.parseTemplate(
63
+ '{~TIf:SuccessMsg::AppData.IsReady^TRUE^~}');
64
+
65
+ // Length check
66
+ _Pict.TemplateProvider.addTemplate('HasItems', '<p>Items found</p>');
67
+ _Pict.parseTemplate(
68
+ '{~TIf:HasItems::AppData.Results^LNGT^0~}');
69
+ ```
70
+
71
+ When the condition is false, the expression returns an empty string.
72
+
73
+ ---
74
+
75
+ ## TemplateIfAbsolute (TIfAbs)
76
+
77
+ Like TemplateIf, but the right side of the comparison is a literal value, not a data address.
78
+
79
+ **Tags:** `{~TemplateIfAbsolute:TEMPLATE_HASH:DATA_ADDRESS:LEFT^OPERATOR^LITERAL~}` `{~TIfAbs:TEMPLATE_HASH:DATA_ADDRESS:LEFT^OPERATOR^LITERAL~}`
80
+
81
+ **Parameters:**
82
+
83
+ | Parameter | Description |
84
+ |-----------|-------------|
85
+ | TEMPLATE_HASH | Hash of the template to render if the condition is true |
86
+ | DATA_ADDRESS | Address of data to pass as Record to the template (can be empty) |
87
+ | LEFT | Address to resolve for the left side |
88
+ | OPERATOR | Comparison operator (same table as TemplateIf) |
89
+ | LITERAL | A literal value to compare against (not resolved from data) |
90
+
91
+ **Examples:**
92
+
93
+ ```javascript
94
+ _Pict.TemplateProvider.addTemplate('AdminPanel',
95
+ '<div class="admin">{~D:AppData.User.Name~} (Admin)</div>');
96
+
97
+ _Pict.TemplateProvider.addTemplate('GuestWelcome',
98
+ '<p>Welcome, guest!</p>');
99
+
100
+ _Pict.AppData.User = { Name: 'Alice', Role: 'admin' };
101
+
102
+ // Compare against a literal string
103
+ _Pict.parseTemplate('{~TIfAbs:AdminPanel::AppData.User.Role^==^admin~}');
104
+ // '<div class="admin">Alice (Admin)</div>'
105
+
106
+ _Pict.parseTemplate('{~TIfAbs:GuestWelcome::AppData.User.Role^==^guest~}');
107
+ // '' (condition false)
108
+
109
+ // Compare against a literal number
110
+ _Pict.parseTemplate('{~TIfAbs:SuccessMsg::AppData.StatusCode^==^200~}');
111
+ ```
112
+
113
+ This is the most common conditional pattern -- comparing a data value against a known constant.
114
+
115
+ ---
116
+
117
+ ## NotEmpty (NE)
118
+
119
+ Checks if a value is truthy. If so, outputs a literal string. Otherwise outputs nothing.
120
+
121
+ **Tags:** `{~NotEmpty:ADDRESS^LITERAL_OUTPUT~}` `{~NE:ADDRESS^LITERAL_OUTPUT~}`
122
+
123
+ **Parameters:**
124
+
125
+ | Parameter | Description |
126
+ |-----------|-------------|
127
+ | ADDRESS | Path to a value to check for truthiness |
128
+ | LITERAL_OUTPUT | A literal string to output if the value is truthy (separated by `^`) |
129
+
130
+ **Examples:**
131
+
132
+ ```html
133
+ <!-- Add a line break after a value if it exists -->
134
+ {~D:Record.AddressLine2~}{~NE:Record.AddressLine2^<br/>~}
135
+
136
+ <!-- Show a separator between items -->
137
+ {~D:Record.FirstName~}{~NE:Record.MiddleName^ ~}{~D:Record.MiddleName~} {~D:Record.LastName~}
138
+
139
+ <!-- Conditional CSS class -->
140
+ <div class="item{~NE:Record.IsActive^ active~}">
141
+ ```
142
+
143
+ ```javascript
144
+ _Pict.AppData.Dog = { Name: 'Fido', Age: 5 };
145
+
146
+ _Pict.parseTemplate('Has name: {~NE:AppData.Dog.Name^yes~}');
147
+ // 'Has name: yes'
148
+
149
+ _Pict.parseTemplate('Has breed: {~NE:AppData.Dog.Breed^yes~}');
150
+ // 'Has breed: '
151
+ ```
152
+
153
+ NotEmpty is lightweight and inline. It does not render a template -- it outputs a fixed string. Use it for small conditional fragments like separators, CSS classes, or HTML attributes. For larger conditional blocks, use TemplateIf or TemplateIfAbsolute.
154
+
155
+ ## Combining Conditionals
156
+
157
+ Jellyfish does not have an `else` or `if/else` expression. Instead, use two complementary conditions or HTML comment toggling.
158
+
159
+ ### Pattern: Two TemplateIf Expressions
160
+
161
+ ```javascript
162
+ _Pict.TemplateProvider.addTemplate('ActiveBadge',
163
+ '<span class="active">Active</span>');
164
+ _Pict.TemplateProvider.addTemplate('InactiveBadge',
165
+ '<span class="inactive">Inactive</span>');
166
+
167
+ let tmpTemplate = [
168
+ '{~TIfAbs:ActiveBadge::Record.Status^==^active~}',
169
+ '{~TIfAbs:InactiveBadge::Record.Status^==^inactive~}'
170
+ ].join('');
171
+ ```
172
+
173
+ ### Pattern: HTML Comment Toggling
174
+
175
+ ```html
176
+ {~HCS:Record.IsAdmin~}
177
+ <div class="admin-controls">Admin only content</div>
178
+ {~HCE:Record.IsAdmin~}
179
+
180
+ {~HCS:Record.IsAdmin:1~}
181
+ <div class="user-controls">Regular user content</div>
182
+ {~HCE:Record.IsAdmin:1~}
183
+ ```
184
+
185
+ When `IsAdmin` is true, the admin content is visible and the user content is commented out. When false, the opposite occurs.
@@ -0,0 +1,38 @@
1
+ # Reference: Breakpoint
2
+
3
+ Inserts a JavaScript `debugger` statement. Returns empty string.
4
+
5
+ **Tags:** `{~Breakpoint~}` or `{~Breakpoint:LABEL~}`
6
+
7
+ **Source:** `pict/source/templates/debugging/Pict-Template-Breakpoint.js`
8
+
9
+ ## Syntax
10
+
11
+ ```
12
+ {~Breakpoint~}
13
+ {~Breakpoint:LABEL~}
14
+ ```
15
+
16
+ ## Parameters
17
+
18
+ | Parameter | Required | Description |
19
+ |-----------|----------|-------------|
20
+ | LABEL | No | Optional label logged with the breakpoint |
21
+
22
+ ## Behavior
23
+
24
+ - Calls `debugger;` -- pauses execution when DevTools are open
25
+ - Logs a stack trace
26
+ - Returns empty string (no visible output)
27
+
28
+ ## Examples
29
+
30
+ ```html
31
+ {~Breakpoint~}
32
+ {~Breakpoint:before-render~}
33
+ ```
34
+
35
+ ## Related
36
+
37
+ - [LS](architecture/templating/ref-ls.md) -- Log a message
38
+ - [LV](architecture/templating/ref-lv.md) -- Log a value