diff --git a/LLM_GUIDE.md b/LLM_GUIDE.md new file mode 100644 index 0000000..5376cad --- /dev/null +++ b/LLM_GUIDE.md @@ -0,0 +1,2138 @@ +# YDNATL - LLM Developer Guide + +**Version:** 1.0.x +**Language:** Python 3.8+ +**Dependencies:** None (pure Python, uses only stdlib) + +## Overview + +YDNATL (You Don't Need Another Template Language) is a Python library for programmatic HTML generation. It uses a declarative, class-based API similar to Flutter's widget system. All elements are subclasses of `HTMLElement` and support method chaining, context managers, and serialization. + +## Core Concepts + +### 1. Element Hierarchy +- All HTML elements inherit from `HTMLElement` +- Elements can have children (other elements) +- Elements have attributes (HTML attributes like id, class, etc.) +- Elements have text content +- Some elements are self-closing (br, img, hr, input, etc.) + +### 2. Rendering +- `render()` method converts element tree to HTML string +- Supports compact (default) and pretty (indented) output +- Automatic HTML entity escaping for security + +### 3. Serialization +- Elements can be serialized to JSON (`to_json()`) +- Elements can be deserialized from JSON (`from_json()`) +- Elements can be parsed from HTML strings (`from_html()`) + +--- + +## Import Patterns + +### Import Everything (Quick Start) +```python +from ydnatl import * +``` + +### Selective Imports (Recommended for Production) +```python +from ydnatl import HTMLElement, Fragment, from_html +from ydnatl.tags.text import H1, Paragraph, Span +from ydnatl.tags.layout import Div, Section +from ydnatl.tags.form import Form, Input, Button +``` + +### Import from Core +```python +from ydnatl.core.element import HTMLElement +from ydnatl.core.fragment import Fragment +from ydnatl.core.parser import from_html +``` + +--- + +## HTMLElement Class + +**Location:** `ydnatl.core.element.HTMLElement` + +### Constructor + +```python +HTMLElement( + *children, # Variable number of child elements or text + tag: str = "div", # HTML tag name + text: str = "", # Text content + self_closing: bool = False, # Whether element is self-closing + **attributes # HTML attributes as keyword arguments +) +``` + +**Special Attribute Handling:** +- `class_name` → renders as `class` attribute +- `data_*` attributes → preserved with hyphens (data-value) +- Boolean attributes (checked, disabled) → rendered without values when True +- `style` → can be string or managed via style helper methods + +**Example:** +```python +div = HTMLElement( + H1("Title"), + Paragraph("Content"), + tag="div", + id="main", + class_name="container", + data_value="123" +) +``` + +### Properties + +| Property | Type | Description | Mutable | +|----------------|--------------------------|-------------------|-----------------------| +| `tag` | str | HTML tag name | Yes | +| `children` | List[HTMLElement \| str] | Child elements | Yes (but use methods) | +| `text` | str | Text content | Yes | +| `attributes` | Dict[str, str] | HTML attributes | Yes (but use methods) | +| `self_closing` | bool | Self-closing flag | Yes | + +--- + +## Element Manipulation Methods + +### Adding Children + +#### append(*children) → self +Adds children to end of children list. + +```python +div = Div() +div.append(H1("Title")) +div.append(Paragraph("Para 1"), Paragraph("Para 2")) +``` + +**Parameters:** +- `*children`: Variable number of HTMLElement or str + +**Returns:** self (for chaining) + +#### prepend(*children) → self +Adds children to beginning of children list. + +```python +div = Div(Paragraph("Second")) +div.prepend(H1("First")) # H1 now comes before Paragraph +``` + +**Parameters:** +- `*children`: Variable number of HTMLElement or str + +**Returns:** self (for chaining) + +### Removing Children + +#### clear() → self +Removes all children. + +```python +div.clear() # Now has no children +``` + +**Returns:** self (for chaining) + +#### pop(index: int = -1) → HTMLElement +Removes and returns child at index. + +```python +last_child = div.pop() # Remove last child +first_child = div.pop(0) # Remove first child +``` + +**Parameters:** +- `index`: int (default: -1) - Index of child to remove + +**Returns:** Removed HTMLElement + +#### remove_all(condition: Callable) → self +Removes all children matching condition. + +```python +# Remove all paragraphs +div.remove_all(lambda el: el.tag == "p") + +# Remove elements with specific class +div.remove_all(lambda el: el.get_attribute("class_name") == "hidden") +``` + +**Parameters:** +- `condition`: Callable[[HTMLElement], bool] - Function that returns True for elements to remove + +**Returns:** self (for chaining) + +### Querying Children + +#### filter(condition: Callable) → Iterator[HTMLElement] +Returns iterator of children matching condition. + +```python +# Get all paragraphs +paragraphs = list(div.filter(lambda el: el.tag == "p")) + +# Get elements with specific attribute +highlighted = list(div.filter(lambda el: el.has_attribute("highlight"))) +``` + +**Parameters:** +- `condition`: Callable[[HTMLElement], bool] + +**Returns:** Iterator[HTMLElement] + +#### find_by_attribute(key: str, value: str) → Optional[HTMLElement] +Finds first child with matching attribute. + +```python +main = div.find_by_attribute("id", "main") +container = div.find_by_attribute("class_name", "container") +``` + +**Parameters:** +- `key`: str - Attribute name +- `value`: str - Attribute value to match + +**Returns:** HTMLElement or None + +#### first() → Optional[HTMLElement] +Returns first child. + +```python +first = div.first() +``` + +**Returns:** HTMLElement or None + +#### last() → Optional[HTMLElement] +Returns last child. + +```python +last = div.last() +``` + +**Returns:** HTMLElement or None + +#### count_children() → int +Returns number of direct children. + +```python +count = div.count_children() +``` + +**Returns:** int + +### Modifying Children + +#### replace_child(index: int, new_child: HTMLElement) → self +Replaces child at index with new child. + +```python +div.replace_child(0, H1("New Title")) +``` + +**Parameters:** +- `index`: int - Index of child to replace +- `new_child`: HTMLElement - New child element + +**Returns:** self (for chaining) + +--- + +## Attribute Management Methods + +### add_attribute(key: str, value: str) → self +Adds or updates single attribute. + +```python +div.add_attribute("id", "main") +div.add_attribute("data-value", "123") +``` + +**Parameters:** +- `key`: str - Attribute name +- `value`: str - Attribute value + +**Returns:** self (for chaining) + +### add_attributes(attributes: List[Tuple[str, str]]) → self +Adds multiple attributes at once. + +```python +div.add_attributes([ + ("id", "main"), + ("class", "container"), + ("role", "main") +]) +``` + +**Parameters:** +- `attributes`: List[Tuple[str, str]] - List of (key, value) tuples + +**Returns:** self (for chaining) + +### remove_attribute(key: str) → self +Removes an attribute. + +```python +div.remove_attribute("data-old") +``` + +**Parameters:** +- `key`: str - Attribute name to remove + +**Returns:** self (for chaining) + +### get_attribute(key: str) → Optional[str] +Gets attribute value. + +```python +id_value = div.get_attribute("id") +class_name = div.get_attribute("class_name") +``` + +**Parameters:** +- `key`: str - Attribute name + +**Returns:** str or None + +### has_attribute(key: str) → bool +Checks if attribute exists. + +```python +if div.has_attribute("id"): + print("Has ID") +``` + +**Parameters:** +- `key`: str - Attribute name + +**Returns:** bool + +### get_attributes(*keys: str) → Dict[str, str] +Gets multiple attributes or all attributes. + +```python +all_attrs = div.get_attributes() +some_attrs = div.get_attributes("id", "class_name") +``` + +**Parameters:** +- `*keys`: str - Attribute names (if empty, returns all) + +**Returns:** Dict[str, str] + +### generate_id() → str +Generates unique ID if element doesn't have one. + +```python +element_id = div.generate_id() # Returns existing or generates new +``` + +**Returns:** str - The element's ID + +--- + +## CSS Style Management Methods + +### add_style(key: str, value: str) → self +Adds or updates single inline style. + +```python +div.add_style("color", "red") +div.add_style("font-size", "16px") +``` + +**Parameters:** +- `key`: str - CSS property name (kebab-case or camelCase) +- `value`: str - CSS property value + +**Returns:** self (for chaining) + +### add_styles(styles: Dict[str, str]) → self +Adds multiple inline styles at once. + +```python +div.add_styles({ + "color": "blue", + "background-color": "#f0f0f0", + "padding": "20px", + "margin": "10px" +}) +``` + +**Parameters:** +- `styles`: Dict[str, str] - Dictionary of CSS property: value pairs + +**Returns:** self (for chaining) + +### get_style(key: str) → Optional[str] +Gets value of specific inline style. + +```python +color = div.get_style("color") +padding = div.get_style("padding") +``` + +**Parameters:** +- `key`: str - CSS property name + +**Returns:** str or None + +### remove_style(key: str) → self +Removes specific inline style. + +```python +div.remove_style("margin") +``` + +**Parameters:** +- `key`: str - CSS property name to remove + +**Returns:** self (for chaining) + +--- + +## Rendering Methods + +### render(pretty: bool = False, _indent: int = 0) → str +Renders element to HTML string. + +```python +# Compact (default) +html = div.render() +#

Title

Content

+ +# Pretty (indented) +html = div.render(pretty=True) +#
+#

Title

+#

Content

+#
+``` + +**Parameters:** +- `pretty`: bool (default: False) - Enable pretty printing with indentation +- `_indent`: int (default: 0) - Internal parameter for indentation level + +**Returns:** str - HTML string + +**Note:** `_indent` is for internal use. Don't set it manually. + +### __str__() → str +String representation (calls render() with pretty=False). + +```python +html = str(div) +# Same as: html = div.render() +``` + +**Returns:** str - Compact HTML string + +--- + +## Serialization Methods + +### to_dict() → Dict +Converts element to dictionary representation. + +```python +data = div.to_dict() +# { +# "tag": "div", +# "self_closing": False, +# "attributes": {"id": "main"}, +# "text": "", +# "children": [...] +# } +``` + +**Returns:** Dict with keys: +- `tag`: str +- `self_closing`: bool +- `attributes`: Dict[str, str] +- `text`: str +- `children`: List[Dict] + +### to_json(indent: Optional[int] = None) → str +Converts element to JSON string. + +```python +# Compact JSON +json_str = div.to_json() + +# Pretty JSON +json_str = div.to_json(indent=2) +``` + +**Parameters:** +- `indent`: Optional[int] - Number of spaces for indentation (None = compact) + +**Returns:** str - JSON string + +### from_dict(data: Dict) → HTMLElement (classmethod) +Creates element from dictionary. + +```python +data = { + "tag": "div", + "self_closing": False, + "attributes": {"id": "main"}, + "text": "Content", + "children": [] +} +element = HTMLElement.from_dict(data) +``` + +**Parameters:** +- `data`: Dict - Dictionary with element data + +**Returns:** HTMLElement + +**Raises:** +- `ValueError` if data is invalid or missing required fields + +### from_json(json_str: str) → HTMLElement (classmethod) +Creates element from JSON string. + +```python +json_str = '{"tag": "div", "text": "Hello", "children": []}' +element = HTMLElement.from_json(json_str) +``` + +**Parameters:** +- `json_str`: str - JSON string + +**Returns:** HTMLElement + +**Raises:** +- `ValueError` if JSON is invalid + +### from_html(html_str: str, fragment: bool = False) → Union[HTMLElement, List[HTMLElement]] (classmethod) +Parses HTML string to YDNATL element(s). + +```python +# Single element +element = HTMLElement.from_html('
Content
') + +# Multiple root elements (fragment) +elements = HTMLElement.from_html('

Title

Text

', fragment=True) +``` + +**Parameters:** +- `html_str`: str - HTML string to parse +- `fragment`: bool (default: False) - If True, returns list of elements + +**Returns:** +- If `fragment=False`: HTMLElement or None +- If `fragment=True`: List[HTMLElement] + +--- + +## Parser Function + +### from_html(html_str: str, fragment: bool = False) → Union[HTMLElement, List[HTMLElement], None] + +**Location:** `ydnatl.core.parser.from_html` or `ydnatl.from_html` + +Standalone function for parsing HTML strings. + +```python +from ydnatl import from_html + +# Parse single element +element = from_html('
Content
') + +# Parse fragment +elements = from_html('

One

Two

', fragment=True) +``` + +**Parameters:** +- `html_str`: str - HTML string to parse +- `fragment`: bool (default: False) - If True, returns list of elements + +**Returns:** +- If `fragment=False`: HTMLElement or None +- If `fragment=True`: List[HTMLElement] + +**Features:** +- Handles all HTML5 elements +- Converts `class` attribute to `class_name` +- Preserves all attributes including data-* +- Handles self-closing tags (br, img, hr, input, etc.) +- Handles HTML entities (&, <, etc.) +- Strips whitespace-only text nodes + +--- + +## Utility Methods + +### clone() → HTMLElement +Creates deep copy of element. + +```python +original = Div(H1("Title")) +copy = original.clone() +# Modifying copy won't affect original +``` + +**Returns:** HTMLElement - Deep copy + +### __enter__() → self +Enables use as context manager. + +```python +with Div(id="container") as div: + div.append(H1("Title")) + div.append(Paragraph("Content")) +``` + +**Returns:** self + +### __exit__(exc_type, exc_val, exc_tb) → None +Exits context manager. + +**Parameters:** Standard context manager parameters + +--- + +## Event Callbacks + +### on_load() → None +Called when element is loaded. Override in subclass. + +```python +class MyElement(HTMLElement): + def on_load(self): + print("Element loaded") + # Custom initialization logic +``` + +### on_before_render() → None +Called before element is rendered. Override in subclass. + +```python +class MyElement(HTMLElement): + def on_before_render(self): + print("About to render") + # Pre-render logic +``` + +### on_after_render() → None +Called after element is rendered. Override in subclass. + +```python +class MyElement(HTMLElement): + def on_after_render(self): + print("Rendered") + # Post-render logic +``` + +### on_unload() → None +Called when element is unloaded. Override in subclass. + +```python +class MyElement(HTMLElement): + def on_unload(self): + print("Element unloaded") + # Cleanup logic +``` + +--- + +## Fragment Class + +**Location:** `ydnatl.core.fragment.Fragment` + +Special element that renders only its children without a wrapper tag. + +### Constructor + +```python +Fragment(*children, **kwargs) +``` + +**Note:** Any attributes passed are ignored (Fragment doesn't render a tag). + +### Usage + +```python +from ydnatl import Fragment, H1, Paragraph + +# Without Fragment - adds wrapper +content = Div(H1("Title"), Paragraph("Text")) +# Output:

Title

Text

+ +# With Fragment - no wrapper +content = Fragment(H1("Title"), Paragraph("Text")) +# Output:

Title

Text

+``` + +### Methods +Inherits all methods from HTMLElement. + +**Important:** `render()` outputs only children without wrapper tags. + +--- + +## Tag Classes by Module + +All tag classes follow the same constructor pattern as HTMLElement: + +```python +TagName(*children, **attributes) +``` + +### ydnatl.tags.text + +**Headings:** +- `H1(*children, **attributes)` +- `H2(*children, **attributes)` +- `H3(*children, **attributes)` +- `H4(*children, **attributes)` +- `H5(*children, **attributes)` +- `H6(*children, **attributes)` + +**Text Elements:** +- `Paragraph(*children, **attributes)` - `

` +- `Span(*children, **attributes)` - `` +- `Bold(*children, **attributes)` - `` +- `Strong(*children, **attributes)` - `` +- `Italic(*children, **attributes)` - `` +- `Em(*children, **attributes)` - `` +- `Underline(*children, **attributes)` - `` +- `Strikethrough(*children, **attributes)` - `` +- `Small(*children, **attributes)` - `` +- `Mark(*children, **attributes)` - `` +- `Del(*children, **attributes)` - `` +- `Ins(*children, **attributes)` - `` +- `Subscript(*children, **attributes)` - `` +- `Superscript(*children, **attributes)` - `` + +**Code/Technical:** +- `Code(*children, **attributes)` - `` +- `Pre(*children, **attributes)` - `

`
+- `Kbd(*children, **attributes)` - ``
+- `Samp(*children, **attributes)` - ``
+- `Var(*children, **attributes)` - ``
+
+**Semantic:**
+- `Blockquote(*children, **attributes)` - `
` +- `Quote(*children, **attributes)` - `` +- `Cite(*children, **attributes)` - `` +- `Abbr(*children, **attributes)` - `` +- `Dfn(*children, **attributes)` - `` +- `Time(*children, **attributes)` - `