Merge pull request #219 from gamosoft/features/fix-bold-drift
Features/fix bold drift
This commit is contained in:
commit
536a9aa80b
|
|
@ -964,21 +964,45 @@ def get_template_content(notes_dir: str, template_name: str) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Matches custom strftime placeholders like {{date:%Y%m%d}} or {{time:%H%M%S}}.
|
||||||
|
# The three prefixes mirror the bare {{date}}, {{time}}, {{datetime}} placeholders;
|
||||||
|
# disallowing '{' and '}' inside the format keeps parsing unambiguous and
|
||||||
|
# guarantees we never swallow an adjacent placeholder.
|
||||||
|
_STRFTIME_PLACEHOLDER_RE = re.compile(r'\{\{(date|time|datetime):([^{}]+)\}\}')
|
||||||
|
|
||||||
|
|
||||||
def apply_template_placeholders(content: str, note_path: str) -> str:
|
def apply_template_placeholders(content: str, note_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
Replace template placeholders with actual values.
|
Replace template placeholders with actual values.
|
||||||
|
|
||||||
Supported placeholders:
|
Built-in named placeholders (fixed default formats):
|
||||||
{{date}} - Current date (YYYY-MM-DD)
|
{{date}} - Current date (YYYY-MM-DD)
|
||||||
{{time}} - Current time (HH:MM:SS)
|
{{time}} - Current time (HH:MM:SS)
|
||||||
{{datetime}} - Current datetime (YYYY-MM-DD HH:MM:SS)
|
{{datetime}} - Current datetime (YYYY-MM-DD HH:MM:SS)
|
||||||
{{timestamp}} - Unix timestamp
|
{{timestamp}} - Unix timestamp (seconds)
|
||||||
{{year}} - Current year (YYYY)
|
{{year}} - Current year (YYYY)
|
||||||
{{month}} - Current month (MM)
|
{{month}} - Current month (MM)
|
||||||
{{day}} - Current day (DD)
|
{{day}} - Current day (DD)
|
||||||
{{title}} - Note name without extension
|
{{title}} - Note name without extension
|
||||||
{{folder}} - Parent folder name
|
{{folder}} - Parent folder name
|
||||||
|
|
||||||
|
Custom date/time formats (strftime escape hatch):
|
||||||
|
{{date:FMT}}, {{time:FMT}}, {{datetime:FMT}}
|
||||||
|
|
||||||
|
Any of the three date/time placeholders above also accepts an
|
||||||
|
optional ":FMT" suffix where FMT is a Python strftime() format
|
||||||
|
string. Pick the prefix whose default format covers the same
|
||||||
|
components you want to format. Examples:
|
||||||
|
|
||||||
|
{{datetime:%Y%m%d%H%M%S}} -> 20260506154200 (filename-safe stamp)
|
||||||
|
{{date:%d/%m/%Y}} -> 06/05/2026 (European date)
|
||||||
|
{{date:%A}} -> Wednesday (full weekday name)
|
||||||
|
{{time:%H%M%S}} -> 154200 (compact time)
|
||||||
|
{{date:%V}} -> 19 (ISO week number)
|
||||||
|
|
||||||
|
Invalid format strings are left in the output unchanged so a typo
|
||||||
|
is visible rather than silently swallowed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
content: Template content with placeholders
|
content: Template content with placeholders
|
||||||
note_path: Path of the note being created
|
note_path: Path of the note being created
|
||||||
|
|
@ -989,6 +1013,15 @@ def apply_template_placeholders(content: str, note_path: str) -> str:
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
note = Path(note_path)
|
note = Path(note_path)
|
||||||
|
|
||||||
|
def _strftime_sub(match: 're.Match[str]') -> str:
|
||||||
|
fmt = match.group(2)
|
||||||
|
try:
|
||||||
|
return now.strftime(fmt)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return match.group(0)
|
||||||
|
|
||||||
|
content = _STRFTIME_PLACEHOLDER_RE.sub(_strftime_sub, content)
|
||||||
|
|
||||||
replacements = {
|
replacements = {
|
||||||
'{{date}}': now.strftime('%Y-%m-%d'),
|
'{{date}}': now.strftime('%Y-%m-%d'),
|
||||||
'{{time}}': now.strftime('%H:%M:%S'),
|
'{{time}}': now.strftime('%H:%M:%S'),
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,7 @@ Create notes from reusable templates with dynamic placeholder replacement.
|
||||||
- `{{day}}` - Current day (DD)
|
- `{{day}}` - Current day (DD)
|
||||||
- `{{title}}` - Note name (without extension)
|
- `{{title}}` - Note name (without extension)
|
||||||
- `{{folder}}` - Parent folder name
|
- `{{folder}}` - Parent folder name
|
||||||
|
- `{{date:FORMAT}}` / `{{time:FORMAT}}` / `{{datetime:FORMAT}}` - Custom format using any Python `strftime()` string, e.g. `{{datetime:%Y%m%d%H%M%S}}` → `20251126143045`. See [Templates](TEMPLATES.md#custom-datetime-formats) for the full recipe list.
|
||||||
|
|
||||||
### Example Template
|
### Example Template
|
||||||
```markdown
|
```markdown
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,39 @@ Templates support dynamic placeholders that are replaced when you create a note:
|
||||||
| `{{title}}` | Note name (without .md) | `Weekly Meeting` |
|
| `{{title}}` | Note name (without .md) | `Weekly Meeting` |
|
||||||
| `{{folder}}` | Parent folder name | `Projects` |
|
| `{{folder}}` | Parent folder name | `Projects` |
|
||||||
|
|
||||||
|
### Custom date/time formats
|
||||||
|
|
||||||
|
Need a different format than the defaults above? Any of the three
|
||||||
|
date/time placeholders — `{{date}}`, `{{time}}`, `{{datetime}}` — accepts
|
||||||
|
an optional `:FORMAT` suffix to override its default format. `FORMAT` is
|
||||||
|
any [Python `strftime()` format string](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes),
|
||||||
|
so anything `strftime` can do, your template can do.
|
||||||
|
|
||||||
|
> **One rule to remember:** pick the prefix whose default already covers
|
||||||
|
> the components you want to format. Use `{{date:…}}` for date-only
|
||||||
|
> formats, `{{time:…}}` for time-only formats, and `{{datetime:…}}` when
|
||||||
|
> you need both.
|
||||||
|
|
||||||
|
#### Common recipes
|
||||||
|
|
||||||
|
| Template | Result | Use case |
|
||||||
|
|---|---|---|
|
||||||
|
| `{{date:%Y%m%d}}` | `20251126` | Date stamp for filenames |
|
||||||
|
| `{{time:%H%M%S}}` | `143045` | Time stamp for filenames |
|
||||||
|
| `{{datetime:%Y%m%d%H%M%S}}` | `20251126143045` | Sortable, filename-safe full stamp |
|
||||||
|
| `{{datetime:%Y%m%dT%H%M%S}}` | `20251126T143045` | ISO-8601 basic, no separators |
|
||||||
|
| `{{datetime:%Y-%m-%dT%H:%M:%S}}` | `2025-11-26T14:30:45` | ISO-8601 extended |
|
||||||
|
| `{{date:%d/%m/%Y}}` | `26/11/2025` | European-style date |
|
||||||
|
| `{{date:%m/%d/%Y}}` | `11/26/2025` | US-style date |
|
||||||
|
| `{{date:%A}}` | `Wednesday` | Full weekday name |
|
||||||
|
| `{{date:%a}}` | `Wed` | Short weekday name |
|
||||||
|
| `{{date:%B}}` | `November` | Full month name |
|
||||||
|
| `{{date:%V}}` | `48` | ISO week number |
|
||||||
|
| `{{date:%j}}` | `330` | Day of year (1–366) |
|
||||||
|
|
||||||
|
> Invalid format strings (typos) are left in the output unchanged so the
|
||||||
|
> mistake is visible rather than silently swallowed.
|
||||||
|
|
||||||
### Example Template
|
### Example Template
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
|
|
|
||||||
|
|
@ -703,15 +703,15 @@
|
||||||
to { opacity: 1; }
|
to { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Syntax highlight colors using theme variables */
|
/* Syntax highlight colors (color-only — see issue #212) */
|
||||||
.syntax-overlay .md-heading { color: var(--accent-primary); font-weight: 600; }
|
.syntax-overlay .md-heading { color: var(--accent-primary); }
|
||||||
.syntax-overlay .md-bold { color: var(--text-primary); font-weight: 700; }
|
.syntax-overlay .md-bold { color: var(--syntax-bold, #fbbf24); }
|
||||||
.syntax-overlay .md-italic { color: var(--text-secondary); font-style: italic; }
|
.syntax-overlay .md-italic { color: var(--syntax-italic, #a78bfa); }
|
||||||
.syntax-overlay .md-code { color: var(--accent-secondary, var(--accent-primary)); background: var(--bg-tertiary); border-radius: 2px; }
|
.syntax-overlay .md-code { color: var(--accent-secondary, var(--accent-primary)); background: var(--bg-tertiary); border-radius: 2px; }
|
||||||
.syntax-overlay .md-link { color: var(--link-color, var(--accent-primary)); }
|
.syntax-overlay .md-link { color: var(--link-color, var(--accent-primary)); }
|
||||||
.syntax-overlay .md-link-url { color: var(--text-tertiary); }
|
.syntax-overlay .md-link-url { color: var(--text-tertiary); }
|
||||||
.syntax-overlay .md-list { color: var(--accent-primary); }
|
.syntax-overlay .md-list { color: var(--accent-primary); }
|
||||||
.syntax-overlay .md-blockquote { color: var(--text-tertiary); font-style: italic; }
|
.syntax-overlay .md-blockquote{ color: var(--text-tertiary); }
|
||||||
.syntax-overlay .md-hr { color: var(--border-primary); }
|
.syntax-overlay .md-hr { color: var(--border-primary); }
|
||||||
.syntax-overlay .md-wikilink { color: var(--link-internal, var(--accent-primary)); }
|
.syntax-overlay .md-wikilink { color: var(--link-internal, var(--accent-primary)); }
|
||||||
.syntax-overlay .md-frontmatter { color: var(--text-tertiary); }
|
.syntax-overlay .md-frontmatter { color: var(--text-tertiary); }
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,12 @@
|
||||||
--error: #ff0088;
|
--error: #ff0088;
|
||||||
--warning: #ffc600;
|
--warning: #ffc600;
|
||||||
|
|
||||||
|
/* Bold + italic — Cobalt2 native orange + cyan.
|
||||||
|
Heading is yellow (warm), so italic uses Cobalt2's signature cyan
|
||||||
|
instead of "lighter primary" to preserve warm/cool hierarchy. */
|
||||||
|
--syntax-bold: #ff9d00; /* cobalt2 orange */
|
||||||
|
--syntax-italic: #9effff; /* cobalt2 cyan */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@
|
||||||
--error: #f87171;
|
--error: #f87171;
|
||||||
--warning: #fbbf24;
|
--warning: #fbbf24;
|
||||||
|
|
||||||
|
/* Bold + italic — warm emphasis + lighter primary for italic (cohesive with heading) */
|
||||||
|
--syntax-bold: #fbbf24; /* amber/yellow */
|
||||||
|
--syntax-italic: #93c5fd; /* lighter blue — same family as heading, distinct by luminance */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #ff5555;
|
--error: #ff5555;
|
||||||
--warning: #f1fa8c;
|
--warning: #f1fa8c;
|
||||||
|
|
||||||
|
/* Bold + italic — Dracula yellow + lighter purple (cohesive with purple heading) */
|
||||||
|
--syntax-bold: #f1fa8c; /* Dracula yellow */
|
||||||
|
--syntax-italic: #d8b4fe; /* lighter purple — same family as heading */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #fb4934; /* Red */
|
--error: #fb4934; /* Red */
|
||||||
--warning: #fabd2f; /* Yellow */
|
--warning: #fabd2f; /* Yellow */
|
||||||
|
|
||||||
|
/* Bold + italic — Gruvbox yellow + lighter peach (cohesive with orange heading) */
|
||||||
|
--syntax-bold: #fabd2f; /* gruvbox yellow */
|
||||||
|
--syntax-italic: #fdba74; /* lighter peach — same family as orange heading */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@
|
||||||
--error: #ff6b8a;
|
--error: #ff6b8a;
|
||||||
--warning: #ffd447;
|
--warning: #ffd447;
|
||||||
|
|
||||||
|
/* Bold + italic — amber CRT alert + lighter matrix green (same green family) */
|
||||||
|
--syntax-bold: #ffd447; /* amber CRT alert (historically authentic — amber CRTs existed) */
|
||||||
|
--syntax-italic: #86efac; /* lighter green — same family as matrix-green heading */
|
||||||
|
|
||||||
/* Shadows — heavy, like a dim room */
|
/* Shadows — heavy, like a dim room */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.55);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.55);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.65);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.65);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@
|
||||||
--error: #ef4444;
|
--error: #ef4444;
|
||||||
--warning: #f59e0b;
|
--warning: #f59e0b;
|
||||||
|
|
||||||
|
/* Bold + italic — warm emphasis + deeper primary for italic (cohesive on white) */
|
||||||
|
--syntax-bold: #b45309; /* deep amber */
|
||||||
|
--syntax-italic: #1d4ed8; /* deeper blue — same family as heading, distinct by depth */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #d9534f;
|
--error: #d9534f;
|
||||||
--warning: #f0ad4e;
|
--warning: #f0ad4e;
|
||||||
|
|
||||||
|
/* Bold + italic — deep amber + deeper matcha green (cohesive with green heading on lime) */
|
||||||
|
--syntax-bold: #a16207; /* deep amber — warm contrast on lime */
|
||||||
|
--syntax-italic: #365314; /* deeper green — same family as matcha heading */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(58, 77, 34, 0.1);
|
--shadow-sm: 0 1px 2px 0 rgba(58, 77, 34, 0.1);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(58, 77, 34, 0.15);
|
--shadow-md: 0 4px 6px -1px rgba(58, 77, 34, 0.15);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #f92672;
|
--error: #f92672;
|
||||||
--warning: #e6db74;
|
--warning: #e6db74;
|
||||||
|
|
||||||
|
/* Bold + italic — Monokai yellow + lighter cyan (cohesive with cyan heading) */
|
||||||
|
--syntax-bold: #e6db74; /* Monokai yellow (strings) */
|
||||||
|
--syntax-italic: #a5f3fc; /* lighter cyan — same family as heading */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #bf616a;
|
--error: #bf616a;
|
||||||
--warning: #ebcb8b;
|
--warning: #ebcb8b;
|
||||||
|
|
||||||
|
/* Bold + italic — aurora yellow + lighter frost (cohesive with frost-cyan heading) */
|
||||||
|
--syntax-bold: #ebcb8b; /* nord13 — aurora yellow */
|
||||||
|
--syntax-italic: #bae6fd; /* lighter frost — same family as heading, distinct by luminance */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@
|
||||||
--error: #f07171;
|
--error: #f07171;
|
||||||
--warning: #ef7c2a;
|
--warning: #ef7c2a;
|
||||||
|
|
||||||
|
/* Bold + italic — Ubuntu gold + lighter peach (cohesive with orange heading) */
|
||||||
|
--syntax-bold: #ffce6b; /* warm gold */
|
||||||
|
--syntax-italic: #fdba74; /* lighter peach — same family as orange heading */
|
||||||
|
|
||||||
/* Shadows — widget.shadow */
|
/* Shadows — widget.shadow */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(6, 1, 4, 0.55);
|
--shadow-sm: 0 1px 2px 0 rgba(6, 1, 4, 0.55);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(6, 1, 4, 0.6);
|
--shadow-md: 0 4px 6px -1px rgba(6, 1, 4, 0.6);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #e51400;
|
--error: #e51400;
|
||||||
--warning: #f6a623;
|
--warning: #f6a623;
|
||||||
|
|
||||||
|
/* Bold + italic — VS brown-amber + deeper VS blue (cohesive with blue heading on white) */
|
||||||
|
--syntax-bold: #795e26; /* VS function brown-amber */
|
||||||
|
--syntax-italic: #1e40af; /* deeper blue — same family as heading, distinct by depth */
|
||||||
|
|
||||||
/* Shadows */
|
/* Shadows */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.08);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.08);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.12);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.12);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@
|
||||||
--error: #ff5252;
|
--error: #ff5252;
|
||||||
--warning: #ffb74d;
|
--warning: #ffb74d;
|
||||||
|
|
||||||
|
/* Bold + italic — warm amber + lighter Vue green (cohesive with green heading) */
|
||||||
|
--syntax-bold: #ffb74d; /* warm amber */
|
||||||
|
--syntax-italic: #86efac; /* lighter green — same family as heading, distinct by luminance */
|
||||||
|
|
||||||
/* Shadows - deeper for high contrast */
|
/* Shadows - deeper for high contrast */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.6);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.6);
|
||||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.7);
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.7);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue