Show max length in item tooltips

This commit is contained in:
Jage9
2026-02-22 03:50:52 -05:00
parent 82e78872d2
commit c7ba23f371
9 changed files with 21 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
// Maintainer-controlled web client version.
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
window.CHGRID_WEB_VERSION = "2026.02.22 R144";
window.CHGRID_WEB_VERSION = "2026.02.22 R145";
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
window.CHGRID_TIME_ZONE = "America/Detroit";

View File

@@ -68,6 +68,7 @@ export type ItemPropertyValueType = 'boolean' | 'text' | 'number' | 'list' | 'so
export type ItemPropertyMetadata = {
valueType?: ItemPropertyValueType;
tooltip?: string;
maxLength?: number;
range?: {
min: number;
max: number;
@@ -140,6 +141,12 @@ function normalizePropertyMetadataRecord(raw: Record<string, unknown> | undefine
if (typeof valueObj.tooltip === 'string' && valueObj.tooltip.trim().length > 0) {
metadata.tooltip = valueObj.tooltip.trim();
}
if (valueObj.maxLength !== undefined) {
const maxLength = Number(valueObj.maxLength);
if (Number.isFinite(maxLength) && maxLength > 0) {
metadata.maxLength = Math.floor(maxLength);
}
}
const range = valueObj.range;
if (range && typeof range === 'object') {
const rangeObj = range as Record<string, unknown>;

View File

@@ -819,6 +819,10 @@ function describeItemPropertyHelp(item: WorldItem, key: string): string {
}
}
if (metadata?.maxLength !== undefined) {
parts.push(`Max length: ${metadata.maxLength} characters.`);
}
parts.push(EDITABLE_ITEM_PROPERTY_KEYS.has(key) ? 'Editable.' : 'Read only.');
return parts.join(' ');
}

View File

@@ -50,6 +50,7 @@ export const welcomeMessageSchema = z.object({
z.object({
valueType: z.enum(['boolean', 'text', 'number', 'list', 'sound']).optional(),
tooltip: z.string().optional(),
maxLength: z.number().int().positive().optional(),
range: z
.object({
min: z.number(),

View File

@@ -64,7 +64,7 @@ TIME_ZONE_OPTIONS: tuple[str, ...] = (
DEFAULT_PARAMS: dict = {"timeZone": DEFAULT_TIME_ZONE, "use24Hour": False}
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item.", "maxLength": 80},
"timeZone": {"valueType": "list", "tooltip": "Timezone used when the clock speaks time."},
"use24Hour": {"valueType": "boolean", "tooltip": "Use 24 hour format instead of AM/PM."},
}
@@ -92,4 +92,3 @@ def use_item(item: WorldItem, nickname: str, clock_formatter: Callable[[dict], s
self_message=f"{item.title} says {display_time}.",
others_message=f"{nickname} checks {item.title}. {item.title} says {display_time}.",
)

View File

@@ -21,7 +21,7 @@ DEFAULT_TITLE = "Dice"
DEFAULT_PARAMS: dict = {"sides": 6, "number": 2}
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item.", "maxLength": 80},
"sides": {
"valueType": "number",
"tooltip": "Number of sides on each die.",

View File

@@ -43,7 +43,7 @@ CHANNEL_OPTIONS: tuple[str, ...] = ("stereo", "mono", "left", "right")
EFFECT_OPTIONS: tuple[str, ...] = ("reverb", "echo", "flanger", "high_pass", "low_pass", "off")
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item.", "maxLength": 80},
"streamUrl": {"valueType": "text", "tooltip": "Audio stream URL used by this radio."},
"enabled": {"valueType": "boolean", "tooltip": "Turns playback on or off for this radio."},
"mediaVolume": {

View File

@@ -21,10 +21,11 @@ DEFAULT_TITLE = "wheel"
DEFAULT_PARAMS: dict = {"spaces": "yes, no"}
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item.", "maxLength": 80},
"spaces": {
"valueType": "text",
"tooltip": "Comma-delimited list of wheel spaces. Example: yes, no, maybe.",
"maxLength": 4000,
},
}
@@ -35,6 +36,8 @@ def validate_update(_item: WorldItem, next_params: dict) -> dict:
spaces_raw = next_params.get("spaces", "")
if not isinstance(spaces_raw, str):
raise ValueError("spaces must be a comma-delimited string.")
if len(spaces_raw) > 4000:
raise ValueError("spaces must be 4000 characters or less.")
spaces = [token.strip() for token in spaces_raw.split(",") if token.strip()]
if not spaces:
raise ValueError("spaces must include at least one value, separated by commas.")
@@ -65,4 +68,3 @@ def use_item(item: WorldItem, nickname: str, _clock_formatter: Callable[[dict],
delayed_self_message=landed,
delayed_others_message=landed,
)

View File

@@ -47,7 +47,7 @@ DEFAULT_PARAMS: dict = {
EFFECT_OPTIONS: tuple[str, ...] = ("reverb", "echo", "flanger", "high_pass", "low_pass", "off")
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item.", "maxLength": 80},
"enabled": {"valueType": "boolean", "tooltip": "Turns this widget on or off."},
"directional": {"valueType": "boolean", "tooltip": "If on, emitted sound favors the facing direction."},
"facing": {