Appearance
Style Guide: Codebase Idiosyncrasies
This document comprehensively addresses all idiosyncrasies found in the webseriously codebase. Follow these patterns strictly to maintain consistency.
Table of Contents
- Names
- Formatting
- Ordering of Variables & Lists/Functions
- Miscellaneous
- Unique Symbols for Getters
- Writable Store Patterns
- Type Assertions
- Equality Operators
- Optional Chaining
- Array Methods
- String Methods
- Object Patterns
- Async/Await
- Svelte Reactive Statements
- File Organization
- Constants Usage
- Manager Instance Access
- Error Handling
- Comments in Code
- Svelte Component Structure
- Type Definitions
- Generic Types
Names
Prefixes
Critical: All prefixes must be followed exactly as specified.
| Prefix | Purpose | Example | Notes |
|---|---|---|---|
T_ | Type/Enum | T_Graph, T_Detail, T_Startup | Always uppercase, used for enums and type identifiers |
w_ | Writable Store | w_hierarchy, w_ancestry_focus, w_t_graph | Svelte writable stores |
S_ | State Class | S_Alteration, S_Widget, S_Element | State management classes |
G_ | Geometry | G_Point, G_Rectangle, G_Cluster | Geometry-related classes |
k | Constants | k.empty, k.hashLength, k.width.details | Single lowercase letter, instance of Constants class |
h | Hierarchy | h.thing_forHID() | Single lowercase letter, Hierarchy manager instance |
s | Stores | s.w_thing_title | Single lowercase letter, global Stores instance |
x | UX | x.update_grabs_forSearch() | Single lowercase letter, UX manager instance |
g | Geometry Manager | g.layout() | Single lowercase letter, Geometry manager instance |
p | Preferences | p.read_key() | Single lowercase letter, Preferences manager instance |
u | Utilities | u.cumulativeSum() | Single lowercase letter, Utilities instance |
e | Events | e.handle_s_mouseFor_t_control() | Single lowercase letter, Events instance |
c | Configuration | c.w_device_isMobile | Single lowercase letter, Configuration instance |
Variable Naming
- snake_case for most variables:
w_t_cluster_pager,w_show_details,ancestry_dict_byHID - camelCase for some specific cases (typically when following external library conventions)
- Boolean flags: Use descriptive prefixes:
has_:has_zoom_controls,has_details_button,has_every_detailis_:isAssembled,isShowing_countDots_ofTypeallow_:allow_graph_editing,allow_tree_mode,allow_autoSaveshow_:show_details,show_related,show_other_databases
- IDs: End with
_idorID:thing_id,ancestryHID,rootID - Counts: Start with
count_:count_details,count_rebuild - Dictionaries/Maps: Use pattern
{type}_dict_by{Key}:thing_dict_byHID,ancestry_dict_byKind_andHID,si_relationships_dict_byParentHID - Arrays/Lists: Use plural nouns:
things,relationships,predicates,ancestries - S_Items collections: Prefix with
si_:si_things,si_traits,si_tags,si_ancestries_dict_byThingHID
Function Naming
- snake_case for all functions:
thing_remember(),ancestry_edit_persistentAddAsChild(),restore_preferences() - Pattern:
{noun}_{verb}_{modifiers}():thing_forget_all(),relationships_translate_idsFromTo_forParents() - Getters: Use
getprefix or property syntax:get hasRoot(),get children_dots() - Static methods: Include class name or be clear:
Identifiable.newID(),remove_item_byHID()
Class Naming
- PascalCase:
Visibility,Hierarchy,Ancestry,Relationship - State classes: Prefix with
S_:S_Widget,S_Element,S_Alteration - Geometry classes: Prefix with
G_:G_Point,G_Rectangle,G_Cluster - Export pattern: Export singleton instance with lowercase single letter:
export const s = new Stores(),export const show = new Visibility()
Enum Naming
- PascalCase with
T_prefix:T_Graph,T_Detail,T_Startup - Enum values: camelCase or string literals:
T_Graph.radial,T_Graph.tree,T_Thing.generic
Formatting
Indentation
- Tabs only (not spaces) for indentation
- Tab width appears to be standard (typically 4 spaces equivalent)
Alignment
- Align equals signs in property declarations when in a group:
typescript
w_t_cluster_pager = writable<T_Cluster_Pager>(T_Cluster_Pager.sliders);
w_t_breadcrumbs = writable<T_Breadcrumbs>(T_Breadcrumbs.ancestry);
w_t_auto_adjust_graph = writable<T_Auto_Adjust_Graph | null>(null);
w_show_details = writable<boolean>(true);
w_show_tiny_dots = writable<boolean>(true);Spacing
- Single space after keywords:
if (condition),for (const item of items) - No space before colons in type annotations:
writable<T_Graph>,Array<T_Detail> - Spaces around operators:
a + b,x === y,flag ? value1 : value2 - No space before commas:
[a, b, c],function(a, b, c) - Space after commas:
[a, b, c],function(a, b, c)
Quotes
- Single quotes for strings in TypeScript:
'string','key' - Single quotes in Svelte script tags:
<script lang='ts'> - Single quotes in Svelte style attributes:
svelte
style='
position: absolute;
top: {top}px;
left: {left}px;
'Svelte-Specific Formatting
- Multi-line style attributes: Use single quotes with line breaks:
svelte
style='
position: absolute;
width: {width}px;
height: {height}px;
'- Component props: One per line for readability when multiple:
svelte
<Segmented name='counts-shown-options'
left={106}
allow_none={false}
allow_multiple={false}
width={segmented_width}
height={segmented_height}
origin={Point.y(tops[1])}
handle_selection={handle_counts_shown}
titles={[T_Counts_Shown.dots, T_Counts_Shown.numbers]}/>Template Styles: Props and Style Attributes
Component Props Ordering
Props passed to components are ordered by length of prop statement (the entire prop including its value, shortest first), enabling visual alignment and easier scanning:
svelte
<Separator name='counts-shown-separator'
length={width} // 14 chars
isHorizontal={true} // 18 chars
position={position} // 19 chars
has_gull_wings={true} // 21 chars
margin={k.details_margin} // 24 chars
origin={Point.y(tops[0])} // 25 chars
title='show list lengths as' // 27 chars
title_left={k.separator_title_left} // 33 chars
thickness={k.thickness.separator.details} // 40 chars
/>Note: When props are grouped logically (e.g., all boolean flags together), length-based ordering applies within each group.
Style Attribute Ordering
CSS properties within style attributes are ordered by length of property statement (the entire property including its value, shortest first):
svelte
<div style='
width: 17px; // 11 chars
height: 17px; // 12 chars
left: {color_left}px; // ~20 chars
position: {position}; // ~19 chars
border: 1px solid black; // 22 chars
border-radius: 50%; // 18 chars
z-index: {T_Layer.details}; // ~28 chars
background-color: {$w_separator_color} // ~33 chars
'>CSS custom properties (variables) are typically grouped together and ordered by length within that group:
svelte
<div style='
--hover-color: {hover_color}; // ~28 chars
--border-color: {border_color}; // ~30 chars
--selected-color: {selected_color}; // ~33 chars
--selected-text-color: {selected_text_color}; // ~38 chars
--hover-background-color: {hover_background_color}; // ~42 chars
--selected-hover-text-color: {selected_hover_text_color}; // ~48 chars
left: {left}px; // ~15 chars
height: {height}px; // ~17 chars
top: {origin.y}px; // ~18 chars
'>Benefits of length-based ordering:
- Creates visual alignment when using tabs
- Makes it easier to scan and find specific properties
- Maintains consistent formatting across the codebase
- Groups similar-length items together naturally
Boolean Coercion
- Use
!!for boolean coercion:if (!!thing),return !!other && this.id == other.id - Use
??for nullish coalescing:p.read_key(T_Preference.show_details) ?? false
Comments
- Block comments for section headers:
typescript
//////////////////////////////////////////////
// //
// reattaches components on/changes to: //
// //
// signal_rebuildGraph //
// w_ancestry_focus //
// //
//////////////////////////////////////////////- Inline comments for clarification:
// NB: hidden until implemented - JSDoc comments for public APIs with
@paramand@returns
Ordering of Variables & Lists/Functions
Length-Based Ordering Principle
Critical: Items are ordered by length of statement (or variable name), not alphabetically. Shorter items come first, longer items come later. This enables visual alignment with tabs and creates a clean, readable hierarchy.
- Within import statements: Items are ordered by the length of their identifier
- Class properties: Ordered by the length of the variable name
- Function parameters: When listed, ordered by length
- Array elements: When manually ordered, shorter items first
This pattern allows for tab-based alignment where the = signs or other delimiters line up visually, making the code easier to scan and maintain.
Import Order
Import statement groups follow this order (by category):
- Geometry classes (G_*)
- State classes (S_*)
- Persistable/Runtime classes (Thing, Ancestry, etc.)
- Type imports from Enumerations (T_*)
- Side-effect imports (
import './Extensions') - Manager instances (k, g, h, s, u, x, etc.)
- Utility imports (builds, busy, files, etc.)
- Type imports (
import type { Dictionary }) - External library imports (
import { get, writable } from 'svelte/store') - Local relative imports (
import { x } from '../managers/UX')
Within each import statement, items are ordered by length (shortest first):
Example:
typescript
import { T_Graph, T_Detail, T_Kinship, T_Startup, T_Breadcrumbs } from '../common/Global_Imports';
// Ordered by length: T_Graph (7), T_Detail (8), T_Kinship (9), T_Startup (9), T_Breadcrumbs (12)
import { T_Preference, T_Cluster_Pager, T_Auto_Adjust_Graph } from '../common/Global_Imports';
// Ordered by length: T_Preference (12), T_Cluster_Pager (14), T_Auto_Adjust_Graph (18)
import { g, k, p, s, g_graph_tree, features } from '../common/Global_Imports';
// Ordered by length: g (1), k (1), p (1), s (1), g_graph_tree (12), features (8)Class Property Order
- Private dictionaries/maps (ordered by length of variable name, shortest first)
- Public properties (ordered by length of variable name, shortest first)
- Constructor
- Static readonly symbols (if any, interspersed with related methods)
- Getters (grouped with related functionality)
- Methods (grouped by functionality)
Property ordering example (by length):
typescript
export class Visibility {
// Properties ordered by length of variable name:
w_t_graph = writable<T_Graph>(T_Graph.tree); // 10 chars
w_t_details = writable<Array<T_Detail>>([]); // 12 chars
w_t_trees = writable<Array<T_Kinship>>(); // 10 chars
w_t_countDots = writable<Array<T_Kinship>>(); // 14 chars
w_id_popupView = writable<string | null>(); // 15 chars
w_show_details = writable<boolean>(true); // 15 chars
w_show_related = writable<boolean>(false); // 16 chars
w_t_breadcrumbs = writable<T_Breadcrumbs>(T_Breadcrumbs.ancestry); // 16 chars
w_show_tiny_dots = writable<boolean>(true); // 17 chars
w_t_directionals = writable<boolean[]>([false, true]); // 17 chars
w_t_cluster_pager = writable<T_Cluster_Pager>(T_Cluster_Pager.sliders); // 19 chars
w_show_search_controls = writable<boolean>(false); // 21 chars
w_t_auto_adjust_graph = writable<T_Auto_Adjust_Graph | null>(null); // 21 chars
w_show_save_data_button = writable<boolean>(false); // 22 chars
w_show_other_databases = writable<boolean>(true); // 23 chars
debug_cursor = false; // 12 chars
}Note: The alignment tabs are used to line up the = signs, creating a visual column that makes scanning easier.
Example structure:
typescript
export class Visibility {
// Private dictionaries first (ordered by length)
private thing_dict_byHID: Record<Integer, Thing> = {};
private si_ancestries_dict_byThingHID: Record<Integer, S_Items<Ancestry>> = {};
// Public properties (ordered by length of variable name)
w_t_graph = writable<T_Graph>(T_Graph.tree);
w_show_details = writable<boolean>(true);
w_t_cluster_pager = writable<T_Cluster_Pager>(T_Cluster_Pager.sliders);
// Constructor
constructor() {
// ...
}
// Getters (ordered by length)
get children_dots(): boolean { return this.isShowing_countDots_ofType(T_Kinship.children); }
// Methods (ordered by length)
apply_queryStrings(queryStrings: URLSearchParams) {
// ...
}
}Function/Method Order
- Constructor (always first)
- Static methods (if any)
- Getters (grouped by related functionality, ordered by length of method name within groups)
- Core methods (public API, ordered by length of method name)
- Helper/private methods (internal implementation, ordered by length of method name)
Note: Within each category, methods are typically ordered by the length of their name (shortest first), though logical grouping may take precedence for related functionality.
Array/List Ordering
- Enum arrays: Maintain enum definition order when possible
- Control arrays: Use numeric keys for explicit ordering (see
left_widthspattern) - Sorted arrays: Use
.sort()with explicit comparator when order matters - Dictionary keys: No guaranteed order (use explicit ordering when needed)
- Manual ordering: When manually ordering array elements or object properties, order by length (shortest first) unless semantic ordering is required
Example of explicit ordering:
typescript
const left_widths = {
0: features.has_details_button ? 18 : -7, // details
1: 11, // recents
2: features.allow_tree_mode ? 54 : 0, // graph type
3: features.has_zoom_controls ? 100 : 34, // plus
// ...
};
lefts = u.cumulativeSum(Object.values(left_widths));Enum Value Ordering
Enum values are ordered by length of the enum key name (shortest first), unless order is semantically critical or explicitly documented otherwise.
- Critical enums: Order matters (documented with comments):
typescript
// DO NOT change the order of the following
export enum T_Order {
child,
other,
}- Non-critical enums: Order is unimportant (documented), but still follows length-based ordering:
typescript
// the order of the following is unimportant
export enum T_Graph {
radial = 'radial',
tree = 'tree',
}- Length-based ordering example:
typescript
export enum T_Search {
off, // 3 chars
enter, // 5 chars
results, // 7 chars
selected, // 8 chars
}
export enum T_Action {
add, // 3 chars
show, // 4 chars
move, // 4 chars
focus, // 5 chars
browse, // 6 chars
center, // 6 chars
delete, // 6 chars
}Note: Enum ordering generally follows length, but may have logical grouping within similar lengths (e.g., related concepts grouped together).
Case Statement Ordering
Case statements in switch blocks are ordered by length of the case value (shortest first), enabling visual alignment and easier scanning:
typescript
switch (name) {
case 'details': // 7 chars
this.w_show_details.set(flag);
break;
case 'related': // 7 chars
this.w_show_related.set(flag);
break;
case 'parents': // 7 chars
const mode = flag ? T_Kinship.parents : T_Kinship.children;
g_graph_tree.set_tree_types([mode]);
break;
}typescript
switch (disableOption) {
case 'search': // 6 chars
this.allow_search = false; break;
case 'details': // 7 chars
this.has_details_button = false; break;
case 'editGraph': // 9 chars
this.allow_graph_editing = false; break;
case 'tree_mode': // 9 chars
this.allow_tree_mode = false; break;
case 'auto_save': // 9 chars
this.allow_autoSave = false; break;
case 'editTitles': // 10 chars
this.allow_title_editing = false; break;
case 'horizontalScrolling': // 20 chars
this.allow_h_scrolling = false; break;
}Note: When case statements have the same length, they may be grouped logically or ordered alphabetically within that length group.
Miscellaneous
Unique Symbols for Getters
Use static readonly _____NAME: unique symbol; pattern before getters that need symbol-based identity:
typescript
static readonly _____ROOT: unique symbol;
get hasRoot(): boolean { return !!this.root; }Writable Store Patterns
- Initialize with type:
writable<T_Graph>(T_Graph.tree) - Nullable types:
writable<string | null>(null) - Array types:
writable<Array<T_Detail>>([]) - Boolean defaults:
writable<boolean>(true)orwritable<boolean>(false)
Type Assertions
- Use
asfor type assertions:types as Array<T_Kinship> - Prefer type guards when possible:
if (thing instanceof Thing)
Equality Operators
- Use
==for value equality (TypeScript strict mode handles type coercion) - Use
===when explicit type checking is needed - Use
!=and!==accordingly
Optional Chaining
- Use
?.for safe property access:ancestry?.thing,this.root?.id ?? null - Use
??for default values:p.read_key(T_Preference.show_details) ?? false
Array Methods
- Prefer
.map(),.filter(),.forEach()over traditional loops when appropriate - Use
.includes()for membership checks:selected.includes(title) - Use spread operator for array copying:
[...selected, title]
String Methods
- Use custom extensions:
.removeWhiteSpace(),.unCamelCase(),.hash() - Use template literals for multi-line strings
- Use
.join(k.comma)for comma-separated lists
Object Patterns
- Use
Object.fromEntries()for creating objects from arrays - Use
Object.entries()for iterating over objects - Use
Object.values()when order matters (with numeric keys)
Async/Await
- Use
async/awaitfor asynchronous operations - Use
Promisepatterns when needed - Handle errors appropriately (check for
NotReadyErrorspecifically in catch blocks)
Svelte Reactive Statements
- Use
$:for reactive statements - Use descriptive variable names in reactive blocks:
const _ = $w_t_graph; - Group related reactive updates together
File Organization
- One class per file (typically)
- Export singleton instance at bottom:
export const show = new Visibility(); - Type exports before class exports when in same file
Constants Usage
- Access via
kinstance:k.empty,k.width.details,k.font_size.info - Use constants for magic numbers and strings
- Group related constants in objects:
k.thickness.separator.details
Manager Instance Access
- Import from
Global_Imports:import { g, k, p, s, x } from '../common/Global_Imports' - Access properties/methods directly:
g.layout(),k.empty,s.w_hierarchy - Destructure when needed:
const { w_t_graph } = show;
Error Handling
- Check for specific error types:
if (error.constructor.name != 'NotReadyError') - Log errors with context:
debug.log_draw('message') - Use console.warn for non-fatal issues
Comments in Code
- Use
// NB:for important notes:// NB: hidden until implemented - Use block comments for section headers (see Formatting section)
- Use JSDoc for public APIs
- Inline comments for complex logic clarification
Svelte Component Structure
<script lang='ts'>block- Imports
- Component props (
export let) - Local constants/variables
- Reactive statements (
$:) - Functions
- Template
<style>block (if needed)
Type Definitions
- Use
typefor aliases:type Dictionary<K, V> = { [key: K]: V } - Use
interfacefor object shapes (when extending is needed) - Use
enumfor fixed sets of values (withT_prefix)
Generic Types
- Use descriptive single letters:
<T>,<K, V> - Use constraints when needed:
<T extends Identifiable> - Use
Array<T>instead ofT[]for consistency
Remember: Consistency is key. When in doubt, look at existing similar code and match its patterns.