Skip to content

Conversation

@arbuthnot-eth
Copy link

@arbuthnot-eth arbuthnot-eth commented Aug 26, 2025

  • Add {% if %}...{% else %}...{% endif %} conditional logic support
  • Add {% set variable = expression %} variable assignment functionality
  • Support comparison operators (==, !=, >, <, >=, <=) in conditions
  • Enable schema variables and nested property access in templates
  • Add {{literal:text}} prefix support for string/number/boolean literals
  • Maintain backward compatibility with existing template syntax
  • Extend template compiler to process assignments before logic structures

This enhancement allows users to create more dynamic and flexible templates with conditional content, custom variable definitions, and precise literal control while maintaining full backward compatibility (purely additive changes), adding requests from #585

- Add {% if %}...{% else %}...{% endif %} conditional logic support
- Add {% set variable = expression %} variable assignment functionality
- Support comparison operators (==, !=, >, <, >=, <=) in conditions
- Enable schema variables and nested property access in templates
- Maintain backward compatibility with existing template syntax
- Extend template compiler to process assignments before logic structures

This enhancement allows users to create more dynamic and flexible templates
with conditional content and custom variable definitions.
…l ops, and conditional sets

- Add expression evaluator supporting (), == != > < >= <=, not/and/or and !/&&/||, dotted/bracket access, and schema: identifiers.
- Rework IF handling to support elif/elseif chains and deep nesting reliably.
- Allow {% set %} to accept full expressions and execute only in selected branches.
- Clean up set tag whitespace (inline vs full-line) and improve render order.

fix(variables): handle non-string values and harden escapeHtml
- Prevents "unsafe.replace is not a function" in popup when opening the variables panel.
- Coerce variable values to strings and escape safely during rendering.
Copy link
Author

@arbuthnot-eth arbuthnot-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Implements a production-ready logic layer for templates with a dedicated expression evaluator and a nesting-aware IF processor.
  • New evaluator: parentheses, comparisons, logical ops (word and symbol), dotted/bracket paths, schema: identifiers.
  • IF tags: elif/elseif support and correct handling of nested blocks.
  • SET tags: full expression support; assignments execute only in chosen branches.
  • Whitespace: remove entire set lines when isolated, keep surrounding text for inline tags.
  • Compiler: phased processing to avoid side effects from non-selected branches.

Why

  • Unlock complex, readable template logic without brittle regex parsing.
  • Ensure correctness with nesting, precedence, and truthiness consistent across the codebase.

Testing
Copy and paste Test_NoteContent.txt into your Template Note Content and it will create a note that outputs tests.

File Changes

  1. src/utils/expression-evaluator.ts (new)
  • Added a safe, self-contained expression parser/evaluator for template logic.
  • Supports: parentheses, comparison operators (==, !=, >, <, >=, <=), logical operators (not/and/or and !/&&/||), JS-like truthiness, dotted/bracket variable access, and schema: identifiers.
  • Exposes evaluateExpression (general values) and evaluateBoolean (conditions), plus isTruthy.
  1. src/utils/tags/if.ts (updated)
  • Replaced ad-hoc condition logic with the shared evaluator.
  • Added full support for elif and elseif chains.
  • Delegates to a nesting-aware IF processor in the compiler and cleans up branch selection and rendering.
  1. src/utils/tags/set.ts (updated)
  • Set assignment now uses evaluateExpression for robust parsing (strings, numbers, booleans, variable access, parentheses, logical ops).
  • Preserves existing behavior for filter pipelines by delegating expressions containing "|" to the filter engine.
  1. src/utils/template-compiler.ts (updated)
  • Introduced a nesting-aware IF parser (processIfBlocks) to correctly handle nested if/elif/elseif/else without regex pitfalls.
  • Processes text in phases to ensure correct side effects:
    • Renders content before/after IF blocks with current variables.
    • Resolves the selected IF branch (handling nested logic), then renders.
    • Ensures sets inside branches only execute for the chosen branch.
  • Improved removal of {% set %} tags:
    • Strips the entire line when a set is the only content.
    • Removes only the tag when inline with surrounding text.
  • Minor: adjusted processing order to avoid prematurely executing assignments in non-selected branches.
  1. src/utils/string-utils.ts (updated)
  • Fix variables panel crash when values are non-strings (e.g., arrays/objects).
  • formatVariables now coerces values safely (JSON.stringify fallback) before rendering.
  • escapeHtml now accepts any input and stringifies before escaping to prevent unsafe.replace errors.

Backwards compatibility

  • Filters inside IF conditions are not evaluated directly; use set-then-compare (documented and enforced consistently).
  • Existing templates using basic IF/ELSE and SET semantics continue to work; behavior is stricter and more predictable with nesting.

Docs

  • Consider a brief doc update to Templates/Variables to call out operator support and the “set then compare” pattern for filters.

Implements Issue #586, #585

…rendering logic

- Clean up trailing whitespace in rendered output to ensure consistent formatting.
- Adjust logic for processing IF blocks to handle leading and trailing blank lines more effectively.
- Refine variable rendering to prevent unwanted spacing in tag-only lines.
@kepano
Copy link
Collaborator

kepano commented Aug 27, 2025

Thanks! I'll take a look.

Did you consider prompt variables? I'm wondering how you evaluate those because the results from Interpreter are not immediately available when the extension is loaded.

@arbuthnot-eth
Copy link
Author

arbuthnot-eth commented Aug 27, 2025

Did you consider prompt variables? I'm wondering how you evaluate those because the results from Interpreter are not immediately available when the extension is loaded.

It is currently not possible to set a variable using {% set %} equal to an interpreter/LLM response.

This doesn't work because:

  • {% set %} is processed during template compilation (synchronous)
  • Interpreter responses are only available after the LLM call completes (asynchronous)
  • The {% set %} tag tries to evaluate {{prompt:"..."}} before the interpreter has run

It could be possible with these changes:

  • Modify replacePromptVariables() to also update the variables object
  • Add a mechanism to defer {% set %} evaluation for prompt-containing expressions
  • Implement a re-compilation system after interpreter results

But I'm not that deep yet haha


I am exploring prompt variables and will soon have another PR that modifies processPrompt, collectPromptVariables, and replacePromptVariables functions so syntax is supported for something like:

{% set myPrompt = "Calculate 9 divided by 3" %}
{{prompt:myPrompt}}

Another low risk, medium effort, high value change!

…improvements

- Fix expression evaluator to properly handle quoted strings vs logical expressions
- Add comprehensive error detection for missing variables in prompt references
- Implement retry button with proper styling and error state management
- Improve template compiler variable processing consistency
- Enhance set tag filter detection to avoid conflicts with logical operators
- Fix prompt variable collection with better regex and variable resolution
- Add replace filter support for parentheses-wrapped replacement patterns
- Clean up LLM response processing to remove code block artifacts
…n- Add isLocalOnly toggle (UI + type)\n- Load/save from sync and local with size-based fallback\n- Always update template_list and template_local_list; avoid stale entries\n- Remove stale copies when switching storage; delete updates both lists\n- Export/import supports local templates; import writes them to local storage\n- Fix replace filter callback and default replacement\n- Simplify popup to use centralized loadTemplates()
@arbuthnot-eth
Copy link
Author

Major Functional Enhancements & Bug Fixes!

Expression Evaluator Improvements:

  • Fixed quoted string handling to prevent logical operators inside quotes from triggering complex
    parsing
  • Added proper detection for quoted vs unquoted expressions to avoid false positives

Template Interpreter Error Handling:

  • Implemented comprehensive missing variable detection for unquoted prompt references
  • Added error collection system that tracks and reports all missing variables before LLM execution
  • Created retry functionality with proper UI state management for failed interpreter runs
  • Enhanced prompt variable collection with robust variable resolution and nested object access

Template Compiler Consistency:

  • Unified variable processing to always use processSimpleVariable for consistent filter handling
  • Removed redundant custom variable handling that was bypassing filter processing

Set Tag Filter Logic:

  • Added intelligent filter pipe detection that distinguishes between logical OR (||) and filter pipes
    (|)
  • Implemented proper quote-aware parsing to avoid conflicts with quoted content containing pipes

Prompt Variable Processing:

  • Enhanced regex to support escaped quotes within quoted prompt text
  • Improved placeholder-to-key mapping system for better variable tracking
  • Added fallback mechanisms for edge cases in variable resolution

Bug Fixes

Replace Filter Enhancements:

  • Added support for parentheses-wrapped replacement patterns like ("old":"new")
  • Fixed parameter handling for unused variables in escape processing
  • Improved regex replacement pattern handling

LLM Response Cleanup:

  • Implemented automatic removal of code block markers (json, markdown) from LLM responses
  • Enhanced response processing to handle escaped newlines and carriage returns

UI State Management:

  • Fixed button state transitions between processing, error, and success states
  • Ensured proper cleanup and reset of UI elements between interpreter runs
  • Added proper enabling/disabling of clip buttons during interpreter operations

UI/UX Improvements

Retry Button Implementation:

  • Created retry button with consistent styling matching the interpret button
  • Added proper error state visual indicators with red coloring
  • Implemented smart positioning and spacing for the retry functionality

Error State Styling:

  • Added --text-error-hover CSS variables for both light and dark themes
  • Enhanced error button styling with hover effects
  • Improved visual feedback for failed interpreter states

The core focus was on making the template interpreter more robust, providing better error feedback, and
ensuring consistent behavior across all template processing components.

@arbuthnot-eth
Copy link
Author

arbuthnot-eth commented Aug 30, 2025

feat(templates): add local-only storage and sync/local consistency

Highlights

- Add isLocalOnly toggle (UI + type)
- Load/save from sync and local with size-based fallback
- Always update template_list and template_local_list; avoid stale entries
- Remove stale copies when switching storage; delete updates both lists
- Export/import supports local templates; import writes them to local storage
- Fix replace filter callback and default replacement
- Simplify popup to use centralized loadTemplates()

Testing

This testfile now includes all template logic tests plus comprehensive prompt variable testing. Only a usable template with local storage feature due to size >8 kB. Sync storage fails with a template of this size!
Two error control variables (You can independently toggle each error type):
- failTest: controls the existing template logic errors (like the neverSet variable test)
- errorTest: controls the prompt variable error scenarios
- Set both to false in the template for a completely clean run, or set to true to test an error state

Copy link
Collaborator

@kepano kepano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local storage option should be split into a separate PR. It's a significantly different feature set that isn't necessary for template logic to be useful. It's okay for tests to be in different templates.

@kepano
Copy link
Collaborator

kepano commented Sep 3, 2025

A few questions from exploring this PR:

  • What is the intended use case for literal variables? Where do you see them being useful?
  • Continuing the Twig-like formatting I wonder if we should at least consider a containment operator so that we can deal with substrings and arrays
  • Have you considered how {% for %} loops would integrate? (a related feature I have been thinking about)

Issue I noticed:

  • If the conditional statement contains quotation marks it's being interpreted as a prompt variable and showing Interpreter

@stdword

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants