mirror of
https://git.srhtnin.garden/sarah/SaraView.git
synced 2026-10-15 02:43:17 +02:00
Properties: colours by type, readable keys, lines between
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ca37409e37
commit
81ebcd66a1
3 files changed
+101
-14
No files matched your search
+55
-9
@@ -23,6 +23,37 @@ OpenOrClose = re.compile(r"^---\s*$")
|
|||||||
PropertyLine = re.compile(r"^([^:\s][^:]*?)\s*:\s*(.*)$")
|
PropertyLine = re.compile(r"^([^:\s][^:]*?)\s*:\s*(.*)$")
|
||||||
TaskLine = re.compile(r"^(\s*(?:[-*+]|\d+[.)])\s+)\[( |x|X)\]")
|
TaskLine = re.compile(r"^(\s*(?:[-*+]|\d+[.)])\s+)\[( |x|X)\]")
|
||||||
BooleanValue = re.compile(r"^(true|false)$", re.IGNORECASE)
|
BooleanValue = re.compile(r"^(true|false)$", re.IGNORECASE)
|
||||||
|
Quoted = re.compile(r"""^(["']).*\1$""")
|
||||||
|
IntegerValue = re.compile(r"^[+-]?\d+$")
|
||||||
|
FloatValue = re.compile(r"^[+-]?(\d+\.\d*([eE][+-]?\d+)?|\.\d+([eE][+-]?\d+)?|\d+[eE][+-]?\d+)$")
|
||||||
|
DateValue = re.compile(r"^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?)?$")
|
||||||
|
|
||||||
|
|
||||||
|
def ValueKind(Value: str) -> str:
|
||||||
|
"""What a value is, for its colour: quotes always make it text ("42" is
|
||||||
|
text, 42 a number), like YAML. Same rules as Properties.js's ValueKind."""
|
||||||
|
if Value == "":
|
||||||
|
return "Empty"
|
||||||
|
if Quoted.match(Value):
|
||||||
|
return "String"
|
||||||
|
if BooleanValue.match(Value):
|
||||||
|
return "Boolean"
|
||||||
|
if IntegerValue.match(Value):
|
||||||
|
return "Integer"
|
||||||
|
if FloatValue.match(Value):
|
||||||
|
return "Float"
|
||||||
|
if DateValue.match(Value):
|
||||||
|
return "Date"
|
||||||
|
return "String"
|
||||||
|
|
||||||
|
|
||||||
|
def ShownValue(Value: str) -> str:
|
||||||
|
"""A value as shown: without the quotes that only said "this is text"."""
|
||||||
|
return Value[1:-1] if Quoted.match(Value) else Value
|
||||||
|
|
||||||
|
|
||||||
|
def KindColour(Kind: str) -> str:
|
||||||
|
return {"Integer": Palette.Peach, "Float": Palette.Yellow, "Date": Palette.Blue}.get(Kind, Palette.Green)
|
||||||
|
|
||||||
# Ticks a box: called with the line (0-based, in the whole note) to flip.
|
# Ticks a box: called with the line (0-based, in the whole note) to flip.
|
||||||
OnToggle = Optional[Callable[[int], None]]
|
OnToggle = Optional[Callable[[int], None]]
|
||||||
@@ -93,26 +124,41 @@ def _Checkbox(Checked: bool, Line: int, Toggle: OnToggle) -> ft.Checkbox:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _Value(Value: str, FontSize: int, MakeMarkdown: Callable[[str], ft.Control]) -> ft.Control:
|
||||||
|
"""A value in its kind's colour; one with Markdown in it (a link, bold)
|
||||||
|
is rendered as Markdown instead."""
|
||||||
|
Shown = ShownValue(Value)
|
||||||
|
if re.search(r"[\[*_`~]|https?://", Shown):
|
||||||
|
return MakeMarkdown(Shown)
|
||||||
|
return ft.Text(Shown, color=KindColour(ValueKind(Value)), size=FontSize, selectable=True)
|
||||||
|
|
||||||
|
|
||||||
def PropertiesView(Found: Parsed, FontSize: int, MakeMarkdown: Callable[[str], ft.Control],
|
def PropertiesView(Found: Parsed, FontSize: int, MakeMarkdown: Callable[[str], ft.Control],
|
||||||
Toggle: OnToggle) -> ft.Control:
|
Toggle: OnToggle) -> ft.Control:
|
||||||
"""The panel. MakeMarkdown renders a value (links, bold)."""
|
"""The panel. MakeMarkdown renders a value with Markdown in it (links, bold)."""
|
||||||
Rows: list[ft.Control] = []
|
Rows: list[ft.Control] = []
|
||||||
for Item in Found.Properties:
|
for Item in Found.Properties:
|
||||||
if BooleanValue.match(Item.Value):
|
if BooleanValue.match(Item.Value):
|
||||||
Shown: ft.Control = _Checkbox(Item.Value.lower() == "true", Item.Line, Toggle)
|
Shown: ft.Control = _Checkbox(Item.Value.lower() == "true", Item.Line, Toggle)
|
||||||
elif Item.List:
|
elif Item.List:
|
||||||
Shown = ft.Row([ft.Container(ft.Text(Entry, color=Palette.Mauve, size=FontSize - 1), bgcolor=Palette.Surface0,
|
Shown = ft.Row([ft.Container(ft.Text(ShownValue(Entry), color=KindColour(ValueKind(Entry)), size=FontSize - 1),
|
||||||
border_radius=4, padding=ft.Padding.symmetric(horizontal=6, vertical=1))
|
bgcolor=Palette.Surface0, border_radius=4,
|
||||||
|
padding=ft.Padding.symmetric(horizontal=6, vertical=1))
|
||||||
for Entry in Item.List], wrap=True, spacing=4, run_spacing=4)
|
for Entry in Item.List], wrap=True, spacing=4, run_spacing=4)
|
||||||
elif Item.Value:
|
elif Item.Value:
|
||||||
Shown = MakeMarkdown(Item.Value)
|
Shown = _Value(Item.Value, FontSize, MakeMarkdown)
|
||||||
else:
|
else:
|
||||||
Shown = ft.Text("Empty", color=Palette.Overlay0, italic=True, size=FontSize - 1)
|
Shown = ft.Text("Empty", color=Palette.Overlay0, italic=True, size=FontSize - 1)
|
||||||
Rows.append(ft.Row([
|
Rows.append(ft.Container(
|
||||||
ft.Container(ft.Text(Item.Key, color=Palette.Overlay0, size=FontSize - 1, max_lines=1,
|
ft.Row([
|
||||||
overflow=ft.TextOverflow.ELLIPSIS), expand=3),
|
ft.Container(ft.Text(Item.Key, color=Palette.Text, size=FontSize - 1, max_lines=1,
|
||||||
ft.Container(Shown, expand=7, alignment=ft.Alignment.CENTER_LEFT),
|
overflow=ft.TextOverflow.ELLIPSIS), expand=3),
|
||||||
], spacing=12, vertical_alignment=ft.CrossAxisAlignment.CENTER))
|
ft.Container(Shown, expand=7, alignment=ft.Alignment.CENTER_LEFT),
|
||||||
|
], spacing=12, vertical_alignment=ft.CrossAxisAlignment.CENTER),
|
||||||
|
# A line between properties, like the web view's.
|
||||||
|
border=ft.Border.only(top=ft.BorderSide(1, Palette.Surface1)) if Rows else None,
|
||||||
|
padding=ft.Padding.symmetric(vertical=4),
|
||||||
|
))
|
||||||
return ft.Container(
|
return ft.Container(
|
||||||
ft.Column([ft.Text("Properties", color=Palette.Overlay0, size=FontSize - 1), *Rows], spacing=2, tight=True),
|
ft.Column([ft.Text("Properties", color=Palette.Overlay0, size=FontSize - 1), *Rows], spacing=2, tight=True),
|
||||||
bgcolor=Palette.Mantle,
|
bgcolor=Palette.Mantle,
|
||||||
|
|||||||
@@ -46,6 +46,23 @@ export function IsBoolean(Value) {
|
|||||||
return /^(true|false)$/i.test(Value);
|
return /^(true|false)$/i.test(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// What a value is, for its colour: quotes always make it text ("42" is text,
|
||||||
|
// 42 a number), like YAML. Same rules as the client's Properties.ValueKind.
|
||||||
|
export function ValueKind(Value) {
|
||||||
|
if (Value === "") return "Empty";
|
||||||
|
if (/^(["']).*\1$/.test(Value)) return "String";
|
||||||
|
if (IsBoolean(Value)) return "Boolean";
|
||||||
|
if (/^[+-]?\d+$/.test(Value)) return "Integer";
|
||||||
|
if (/^[+-]?(\d+\.\d*([eE][+-]?\d+)?|\.\d+([eE][+-]?\d+)?|\d+[eE][+-]?\d+)$/.test(Value)) return "Float";
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?)?$/.test(Value)) return "Date";
|
||||||
|
return "String";
|
||||||
|
}
|
||||||
|
|
||||||
|
// A value as shown: without the quotes that only said "this is text".
|
||||||
|
export function ShownValue(Value) {
|
||||||
|
return /^(["']).*\1$/.test(Value) ? Value.slice(1, -1) : Value;
|
||||||
|
}
|
||||||
|
|
||||||
// The note without its properties block (what Markdown renders).
|
// The note without its properties block (what Markdown renders).
|
||||||
export function WithoutProperties(Text) {
|
export function WithoutProperties(Text) {
|
||||||
const Parsed = ParseProperties(Text);
|
const Parsed = ParseProperties(Text);
|
||||||
@@ -99,9 +116,13 @@ export function PropertiesHtml(Parsed, RenderInline, Escape, CanEdit) {
|
|||||||
const Disabled = CanEdit ? "" : " disabled";
|
const Disabled = CanEdit ? "" : " disabled";
|
||||||
Shown = `<input type="checkbox" class="PropertyCheckbox" data-line="${Line}"${Checked}${Disabled}>`;
|
Shown = `<input type="checkbox" class="PropertyCheckbox" data-line="${Line}"${Checked}${Disabled}>`;
|
||||||
} else if (List) {
|
} else if (List) {
|
||||||
Shown = List.map((Item) => `<span class="PropertyChip">${RenderInline(Item)}</span>`).join("");
|
Shown = List.map(
|
||||||
|
(Item) => `<span class="PropertyChip PropertyKind${ValueKind(Item)}">${RenderInline(ShownValue(Item))}</span>`,
|
||||||
|
).join("");
|
||||||
|
} else if (Value) {
|
||||||
|
Shown = `<span class="PropertyKind${ValueKind(Value)}">${RenderInline(ShownValue(Value))}</span>`;
|
||||||
} else {
|
} else {
|
||||||
Shown = Value ? RenderInline(Value) : `<span class="PropertyEmpty">Empty</span>`;
|
Shown = `<span class="PropertyEmpty">Empty</span>`;
|
||||||
}
|
}
|
||||||
return `<div class="PropertyRow"><span class="PropertyKey">${Escape(Key)}</span><span class="PropertyValue">${Shown}</span></div>`;
|
return `<div class="PropertyRow"><span class="PropertyKey">${Escape(Key)}</span><span class="PropertyValue">${Shown}</span></div>`;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -527,7 +527,11 @@ img,
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 2px 0;
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PropertyRow + .PropertyRow {
|
||||||
|
border-top: 1px solid var(--Border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.PropertyKey {
|
.PropertyKey {
|
||||||
@@ -536,7 +540,7 @@ img,
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: var(--TextMuted);
|
color: var(--Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.PropertyValue {
|
.PropertyValue {
|
||||||
@@ -552,7 +556,23 @@ img,
|
|||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: var(--SurfaceAlt);
|
background: var(--SurfaceAlt);
|
||||||
color: var(--Purple);
|
}
|
||||||
|
|
||||||
|
/* Values by kind (Properties.js ValueKind): text, whole and decimal numbers, dates. */
|
||||||
|
.PropertyKindString {
|
||||||
|
color: var(--Green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.PropertyKindInteger {
|
||||||
|
color: var(--Orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.PropertyKindFloat {
|
||||||
|
color: var(--Yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.PropertyKindDate {
|
||||||
|
color: var(--Blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
.PropertyEmpty {
|
.PropertyEmpty {
|
||||||
|
|||||||
Reference in new issue
Block a user