Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/mol/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
mol/
node/
todomvc/-/
.git/
5 changes: 5 additions & 0 deletions examples/mol/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
mol/
node/
**/-/
**/-view.tree/
19 changes: 19 additions & 0 deletions examples/mol/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:20-alpine

RUN apk add --no-cache git

WORKDIR /mam

# Init MAM workspace: clone the root repo so mol/ and node/ are available
RUN git init && git remote add origin https://github.com/hyoo-ru/mam.git && git pull origin master

# Copy our todomvc app into the workspace
RUN mkdir -p /mam/todomvc
COPY todomvc/ /mam/todomvc/

RUN npm exec mam todomvc

EXPOSE 9080

# docker compose up --build
# http://localhost:9080/todomvc/-/test.html
11 changes: 11 additions & 0 deletions examples/mol/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
services:
todomvc-mol:
build: .
ports:
- "9080:9080"
restart: unless-stopped
tty: true
stdin_open: true
command: "npm start"

# http://localhost:9080/todomvc/-/test.html
7 changes: 7 additions & 0 deletions examples/mol/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"private": true,
"scripts": {
"start": "docker compose up --build",
"stop": "docker compose down"
}
}
47 changes: 47 additions & 0 deletions examples/mol/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# TodoMVC: $mol

> $mol is a reactive web framework with zero-config MAM build system, virtual DOM, lazy rendering, and automatic dependency tracking.
See [mol.hyoo.ru](https://mol.hyoo.ru/) for more information.

## Implementation

This implementation uses [$mol](https://github.com/hyoo-ru/mam) — a reactive web framework where UI is declared in `.view.tree` files and logic is written in TypeScript.

### Key components used

- `$mol_scroll` — root scrollable container
- `$mol_list` — virtual list with lazy rendering
- `$mol_string` — text input with submit support
- `$mol_check` — checkbox (toggle all)
- `$mol_link` — routing links with `$mol_state_arg`
- `$mol_button_minor` — clear completed button
- `$mol_state_local` — localStorage persistence

### State management

State is managed reactively via `$mol_mem` (memoized reactive properties). Todo items are persisted in `localStorage` using `$mol_state_local`. Routing (All / Active / Completed) uses `$mol_state_arg` which maps URL hash parameters to reactive state.

### Architecture

The entire app is a single component (`$todomvc`) with a nested `$todomvc_task_row` for each todo item. The `.view.tree` file declares the component structure, `.view.ts` adds reactive logic, and `.css` provides styling matching the TodoMVC spec.

## Building and running

$mol uses [MAM](https://github.com/hyoo-ru/mam) — a zero-config build system that resolves dependencies by filesystem path. Docker provides an isolated MAM workspace out of the box.

```bash
docker compose up --build
# Open http://localhost:9080/todomvc/-/test.html
```

### Local development (MAM workspace)

If you already have a MAM workspace:

```bash
cp -r todomvc/ /path/to/mam/todomvc/
cd /path/to/mam
npm start
# Open http://localhost:9080/todomvc/-/test.html
```
19 changes: 19 additions & 0 deletions examples/mol/todomvc/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!doctype html>
<html style=" height: 100% ">

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">

<style>
body {
height: 100%;
margin : 0;
font: 18px/1.2 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: #f5f5f5;
color: #4d4d4d;
}
</style>

<div mol_view_root="$todomvc">

<script src="web.js" charset="utf-8"></script>
163 changes: 163 additions & 0 deletions examples/mol/todomvc/todomvc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
[todomvc] {
align-self: stretch;
width: 100%;
margin: 0;
transition: none;
}

[todomvc] [mol_string] ,
[todomvc] [mol_string]:focus ,
[todomvc] [mol_string]:hover ,
[todomvc] [mol_button] ,
[todomvc] [mol_button]:focus ,
[todomvc] [mol_button]:hover ,
[todomvc] [mol_link] ,
[todomvc] [mol_link]:focus ,
[todomvc] [mol_link]:hover {
box-shadow: none;
background-color: transparent;
}

[todomvc] [mol_link_current] {
color: black;
}

[todomvc] [mol_link]:not([mol_link_current]):hover ,
[todomvc] [mol_link]:not([mol_link_current]):focus {
text-decoration: underline;
}

[todomvc_head_complete] {
flex: 0 0 4rem;
transform: rotate( 90deg );
align-items: center;
opacity: .5;
padding: 0;
justify-content: center;
}
[todomvc_head_complete]:disabled {
visibility: hidden;
}
[todomvc_head_complete][mol_check_checked] {
opacity: 1;
}

[todomvc_add] {
padding: 0;
flex: 1 1 auto;
}

[todomvc_list] {
font-size: 1.5rem;
transition: none;
}

[todomvc_page] {
flex: 1 1 auto;
transition: none;
}

[todomvc_panel] {
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
width: 38rem;
--mol_theme_back: white;
--mol_theme_text: black;
background-color: var(--mol_theme_back);
display: flex;
flex-direction: column;
margin: 0 auto 2rem;
transition: none;
}

[todomvc_head] {
font-size: 1.5rem;
padding: 0;
display: flex;
height: 4rem;
}

[todomvc_title] {
word-break: normal;
display: block;
padding: .25rem;
font-size: 7rem;
font-weight: 100;
text-align: center;
color: rgba(275, 147, 147, 0.2);
text-rendering: optimizeLegibility;
text-transform: lowercase;
line-height: 1.5;
}

[todomvc_task_row] {
display: flex;
background-color: white;
box-shadow: 0 -1px 0 0 #ededed;
height: 4rem;
}

[todomvc_task_row_complete] {
flex: 0 0 4rem;
padding: 1rem;
background: no-repeat center center url('todomvc/todomvc_active.svg');
}
[todomvc_task_row_complete][mol_check_checked] {
background: no-repeat center center url('todomvc/todomvc_completed.svg');
}

[todomvc_task_row_title] {
padding: 0;
flex: 1 1 auto;
}

[todomvc_task_row_completed] [todomvc_task_row_title] {
color: #d9d9d9;
text-decoration: line-through;
}

[todomvc_task_row_drop] {
font-size: 1.25rem;
flex: 0 04rem;
padding: 1.25rem;
}
[todomvc_task_row]:not(:hover) [todomvc_task_row_drop]:not(:focus) {
opacity: 0;
color: #cc9a9a;
}

[todomvc_foot] {
display: flex;
flex-wrap: wrap;
align-items: baseline;
padding: .5rem 1rem;
color: #777;
font-size: .85rem;
border-top: 1px solid #ededed;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
}

[todomvc_pending] {
padding: .25rem;
flex: 0 1 6rem;
}

[todomvc_filter] {
flex: 1 100 auto;
display: flex;
justify-content: center;
margin: .25rem;
box-shadow: none;
}

[todomvc_sweep] {
flex: 0 1 auto;
padding: .25rem;
}
[todomvc_sweep][disabled] {
visibility: hidden;
}

[todomvc_sweep]:hover ,
[todomvc_sweep]:focus {
text-decoration: underline;
}
2 changes: 2 additions & 0 deletions examples/mol/todomvc/todomvc.meta.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
deploy \/todomvc/todomvc_active.svg
deploy \/todomvc/todomvc_completed.svg
Loading