Update git remote domain from privategit.hanson.xyz to git.internal.hanson.xyz, remove port 3322
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,5 +3,5 @@
|
||||
To start a new RSpade project:
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules ssh://git@privategit.hanson.xyz:3322/brianhansonxyz/rspade_project.git your-project-name
|
||||
git clone --recurse-submodules ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade_project.git your-project-name
|
||||
```
|
||||
|
||||
@@ -14,8 +14,7 @@ SYNOPSIS
|
||||
class User_Card extends Jqhtml_Component {
|
||||
async on_load() {
|
||||
// Load async data - ONLY modify this.data
|
||||
this.data = await fetch('/api/user/' + this.args.id)
|
||||
.then(r => r.json());
|
||||
this.data = await User_Controller.get({id: this.args.id});
|
||||
}
|
||||
|
||||
on_ready() {
|
||||
@@ -43,6 +42,81 @@ DESCRIPTION
|
||||
- Double-render pattern for loading states
|
||||
- No build configuration required (auto-discovered)
|
||||
|
||||
COMPONENT COMPLEXITY TIERS
|
||||
Three tiers guide which patterns to use for a new component:
|
||||
|
||||
Static Components (template-only, no JS file):
|
||||
Pure display from this.args. No lifecycle hooks needed.
|
||||
Use for: badges, labels, static cards, layout wrappers.
|
||||
|
||||
<Define:Status_Badge>
|
||||
<span class="badge <%= this.args.css_class %>">
|
||||
<%= this.args.label %>
|
||||
</span>
|
||||
</Define:Status_Badge>
|
||||
|
||||
Simple Components (on_load + reload):
|
||||
Fetch data in on_load(), re-fetch with reload() when this.args change.
|
||||
Most common pattern. Framework handles caching and re-render.
|
||||
|
||||
class User_List extends Jqhtml_Component {
|
||||
on_create() {
|
||||
this.data.users = [];
|
||||
}
|
||||
async on_load() {
|
||||
this.data = await User_Controller.list({
|
||||
filter: this.args.filter
|
||||
});
|
||||
}
|
||||
on_ready() {
|
||||
this.$sid('filter_btn').on('click', () => {
|
||||
this.args.filter = 'active';
|
||||
this.reload(); // Re-fetches via on_load()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Complex Components (on_load + jQuery DOM manipulation):
|
||||
Use on_load() for initial/cached data, then jQuery for incremental
|
||||
mutations after initialization. Avoids unnecessary network round-trips
|
||||
and preserves user state (selections, scroll position, input).
|
||||
|
||||
class Chat_Widget extends Jqhtml_Component {
|
||||
async on_load() {
|
||||
this.data = await Chat_Controller.get_messages({
|
||||
channel: this.args.channel
|
||||
});
|
||||
}
|
||||
on_ready() {
|
||||
// Incremental updates via DOM, not reload()
|
||||
this.socket = new WebSocket(this.args.ws_url);
|
||||
this.socket.onmessage = (e) => {
|
||||
const msg = JSON.parse(e.data);
|
||||
this.$sid('messages').append(
|
||||
'<div class="message">' + Rsx.escape(msg.text) + '</div>'
|
||||
);
|
||||
};
|
||||
}
|
||||
on_stop() {
|
||||
this.socket.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
this.state Convention:
|
||||
this.state is not a framework property - it has no special behavior.
|
||||
Use it as convention for mutable UI state that changes after
|
||||
initialization, distinct from this.data (immutable after on_load).
|
||||
|
||||
on_create() {
|
||||
this.state = { selected_ids: [], is_expanded: false };
|
||||
}
|
||||
on_ready() {
|
||||
this.$sid('toggle').on('click', () => {
|
||||
this.state.is_expanded = !this.state.is_expanded;
|
||||
this.$sid('panel').toggle(this.state.is_expanded);
|
||||
});
|
||||
}
|
||||
|
||||
SEMANTIC-FIRST DESIGN
|
||||
JQHTML is designed for mechanical thinkers who think structurally
|
||||
rather than visually. Components represent logical concepts, not
|
||||
@@ -553,8 +627,7 @@ DOUBLE-RENDER PATTERN
|
||||
}
|
||||
|
||||
async on_load() {
|
||||
this.data.products = await fetch('/api/products')
|
||||
.then(r => r.json());
|
||||
this.data.products = await Product_Controller.list();
|
||||
// Automatic re-render happens after this completes
|
||||
}
|
||||
|
||||
@@ -581,7 +654,7 @@ LOADING STATE PATTERN (CRITICAL)
|
||||
this.data.state = {loading: true};
|
||||
this.render(); // WRONG: Manual render call
|
||||
|
||||
const response = await $.ajax({...}); // WRONG: $.ajax()
|
||||
const response = await $.ajax({url: '...', ...}); // WRONG: $.ajax()
|
||||
|
||||
if (response.success) {
|
||||
this.data = {
|
||||
@@ -684,8 +757,7 @@ JAVASCRIPT COMPONENT CLASS
|
||||
async on_load() {
|
||||
// Fetch data - NO DOM manipulation allowed!
|
||||
// ONLY update this.data
|
||||
this.data.products = await fetch('/api/products')
|
||||
.then(r => r.json());
|
||||
this.data.products = await Product_Controller.list();
|
||||
// Template re-renders automatically with new data
|
||||
}
|
||||
|
||||
@@ -1360,9 +1432,9 @@ EXAMPLES
|
||||
class Product_Card extends Jqhtml_Component {
|
||||
async on_load() {
|
||||
// Load product data
|
||||
const id = this.args.product_id;
|
||||
this.data = await fetch(`/api/products/${id}`)
|
||||
.then(r => r.json());
|
||||
this.data = await Product_Controller.get({
|
||||
id: this.args.product_id
|
||||
});
|
||||
// Template re-renders automatically with data
|
||||
}
|
||||
|
||||
@@ -1399,8 +1471,7 @@ EXAMPLES
|
||||
|
||||
class Todo_List extends Jqhtml_Component {
|
||||
async on_load() {
|
||||
this.data.todos = await fetch('/api/todos')
|
||||
.then(r => r.json());
|
||||
this.data = await Todo_Controller.list();
|
||||
}
|
||||
|
||||
on_ready() {
|
||||
@@ -1438,15 +1509,9 @@ EXAMPLES
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const data = {
|
||||
await Contact_Controller.send({
|
||||
email: this.$sid('email').val(),
|
||||
message: this.$sid('message').val(),
|
||||
};
|
||||
|
||||
await fetch('/contact', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
this.$.html('<p>Thank you!</p>');
|
||||
|
||||
@@ -86,7 +86,7 @@ if ! git remote get-url rspade_upstream >/dev/null 2>&1; then
|
||||
# Auto-configure remote for project mode (when system/ is a submodule)
|
||||
if [ "$IS_PROJECT_MODE" = false ] && [ -f "../.gitmodules" ] && grep -q "path = system" ../.gitmodules 2>/dev/null; then
|
||||
echo "→ Configuring rspade_upstream remote (first-time setup)..."
|
||||
UPSTREAM_URL="ssh://git@privategit.hanson.xyz:3322/brianhansonxyz/rspade_system.git"
|
||||
UPSTREAM_URL="ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade_system.git"
|
||||
if git remote add rspade_upstream "$UPSTREAM_URL" 2>&1; then
|
||||
echo " ✓ Remote configured: $UPSTREAM_URL"
|
||||
echo ""
|
||||
|
||||
@@ -595,12 +595,19 @@ class Toggle_Button extends Component {
|
||||
### Lifecycle
|
||||
|
||||
1. **on_create()** → Setup defaults (sync) - `this.data.rows = []; this.data.loading = true;`
|
||||
2. **render** → Template executes
|
||||
3. **on_render()** → Hide uninitialized UI (sync)
|
||||
4. **on_load()** → Fetch data into `this.data` (async)
|
||||
5. **on_ready()** → DOM manipulation safe (async)
|
||||
2. **render** → Template executes (top-down: parent before children)
|
||||
3. **on_render()** → Fires after render, BEFORE children ready (top-down, sync)
|
||||
4. **on_load()** → Fetch data into `this.data` (bottom-up, parallel siblings, async)
|
||||
5. **on_ready()** → All children guaranteed ready (bottom-up, async)
|
||||
6. **on_stop()** → Teardown when destroyed (sync)
|
||||
|
||||
If `on_load()` modifies `this.data`, component renders twice (defaults → populated).
|
||||
If `on_load()` modifies `this.data`, component renders twice (defaults → populated). on_ready() fires once after final render.
|
||||
|
||||
### Component Complexity Tiers
|
||||
|
||||
- **Static**: Template-only, no JS file. Pure display from this.args.
|
||||
- **Simple**: on_load() fetches this.data, `reload()` to re-fetch when this.args change.
|
||||
- **Complex**: on_load() for initial/cached data, then jQuery DOM manipulation for incremental mutations post-initialization.
|
||||
|
||||
### Component API
|
||||
|
||||
@@ -612,11 +619,12 @@ If `on_load()` modifies `this.data`, component renders twice (defaults → popul
|
||||
| `this.$sid('name')` | jQuery | Child with `$sid="name"` |
|
||||
| `this.sid('name')` | Component/null | Child component instance |
|
||||
|
||||
**reload() vs render():**
|
||||
```
|
||||
reload() = on_load() → render() → on_ready() ← ALWAYS USE THIS
|
||||
render() = template only (no on_ready) ← NEVER USE
|
||||
```
|
||||
**Lifecycle Methods:**
|
||||
- `reload()` - Reset this.data to on_create() defaults → on_load() → render() → on_ready(). Use when this.args changed.
|
||||
- `render()` / `redraw()` - Re-execute template → wait for children → on_ready(). Does NOT re-run on_load(). UI-only updates.
|
||||
- `stop()` - Destroy component and all children. Calls on_stop() if defined.
|
||||
|
||||
**render() destroys child DOM**: All child elements and child components are recreated. DOM event handlers on children are lost and must be re-registered.
|
||||
|
||||
After mutations, call `this.reload()` - the server round-trip is intentional:
|
||||
```javascript
|
||||
@@ -626,11 +634,15 @@ async add_item() {
|
||||
}
|
||||
```
|
||||
|
||||
**Event handlers** go in `on_ready()` - they auto-reattach after reload. **WRONG:** Event delegation like `this.$.on('click', '[data-sid="btn"]', handler)` to "survive" render calls - use `reload()` instead.
|
||||
|
||||
**this.data rules (enforced):** Writable only in `on_create()` (defaults) and `on_load()` (fetched data). Read-only elsewhere.
|
||||
|
||||
**on_render():** Ignore - use `on_ready()` for post-render work.
|
||||
### Event Handler Placement
|
||||
|
||||
| What | Where | Why |
|
||||
|------|-------|-----|
|
||||
| `this.on('event', ...)` | `on_create()` | Persists across renders; on_ready() risks infinite loops from event replay |
|
||||
| `this.sid('child').on('event')` | `on_ready()` | Child component events |
|
||||
| `this.$sid('elem').on('click')` | `on_render()` or `on_ready()` | Child DOM recreated on render, must re-attach |
|
||||
|
||||
### Loading Pattern
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
Clone the RSpade project with the framework submodule:
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules ssh://git@privategit.hanson.xyz:3322/brianhansonxyz/rspade_project.git /path/to/project
|
||||
git clone --recurse-submodules ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade_project.git /path/to/project
|
||||
cd /path/to/project
|
||||
```
|
||||
|
||||
**Alternative method** (clone then initialize submodules):
|
||||
|
||||
```bash
|
||||
git clone ssh://git@privategit.hanson.xyz:3322/brianhansonxyz/rspade_project.git /path/to/project
|
||||
git clone ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade_project.git /path/to/project
|
||||
cd /path/to/project
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
@@ -20,7 +20,7 @@ When starting a new project from this template:
|
||||
bin/framework-upstream setup
|
||||
```
|
||||
|
||||
This configures `ssh://git@192.168.0.3:3322/brianhansonxyz/rspade-publish.git` as the `framework-upstream` remote.
|
||||
This configures `ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade-publish.git` as the `framework-upstream` remote.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -171,7 +171,7 @@ The `bin/framework-upstream` script automates these Git commands:
|
||||
|
||||
```bash
|
||||
# Add upstream remote manually
|
||||
git remote add framework-upstream ssh://git@192.168.0.3:3322/brianhansonxyz/rspade-publish.git
|
||||
git remote add framework-upstream ssh://git@git.internal.hanson.xyz/brianhansonxyz/rspade-publish.git
|
||||
|
||||
# Fetch updates
|
||||
git fetch framework-upstream
|
||||
|
||||
Reference in New Issue
Block a user