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
47 changes: 47 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Deploy Documentation

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: cd tko.io && bun install

- name: Build site
run: cd tko.io && bun run build

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: tko.io/_site

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ coverage/
.vscode/extensions.json
!.vscode/launch.json
.vscode/settings.json
.playwright-mcp
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,39 @@ If the issue is an enhancement, consider also including:
1. Explain why it would be **useful**.
2. List **other applications** that have the enhancement.

## Documentation

All TKO documentation lives in `tko.io/src/docs/`.

To contribute documentation:

1. Edit files in `tko.io/src/docs/[category]/` where category is:
- `bindings/` - All binding handlers
- `observables/` - Observable and observable array docs
- `computed/` - Computed observable docs
- `components/` - Component system docs
- `binding-context/` - Binding context and custom bindings
- `advanced/` - Advanced topics (lifecycle, providers, parsers)

2. Each file requires frontmatter:
```yaml
---
layout: base.njk
title: Page Title
---
```

3. Test locally:
```bash
cd tko.io
bun run dev
```
Then visit http://localhost:8080

4. Submit a PR with your changes

The documentation site is automatically deployed when changes are merged to main.

## Pull Requests

A good pull request might include:
Expand Down
5 changes: 0 additions & 5 deletions builds/reference/docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ TKO has a comprehensive suite of tests that ensure its correct functioning and a

## Sponsors

Support Knockout:

<a class='btn btn-primary btn-lg btn-block' href='https://patreon.com/brianmhunt'>
via Patreon to Brian M Hunt
</a>


## First Example
Expand Down
13 changes: 13 additions & 0 deletions builds/reference/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { filters } from '@tko/filter.punches'

import components from '@tko/utils.component'
import { createElement, Fragment } from '@tko/utils.jsx'
import { JsxObserver } from '@tko/utils.jsx'

import { overloadOperator } from '@tko/utils.parser'

Expand Down Expand Up @@ -57,10 +58,22 @@ const builder = new Builder({
})

const version = BUILD_VERSION

export default builder.create({
jsx: {
createElement,
Fragment,
/** Public render function that converts JSX to DOM nodes */
render(jsx: any) {
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The render function parameter uses 'any' type, which bypasses TypeScript's type checking. Consider defining a proper JSX type or using a more specific type to improve type safety and API clarity.

Suggested change
render(jsx: any) {
render(jsx: ReturnType<typeof createElement>) {

Copilot uses AI. Check for mistakes.
const fragment = document.createDocumentFragment()
const observer = new JsxObserver(jsx, fragment)
// Return the first child if single node, or the fragment if multiple
const node = fragment.childNodes.length === 1 ? fragment.firstChild : fragment
return {
node,
dispose: () => observer.dispose()
}
},
},
components,
version,
Expand Down
2 changes: 0 additions & 2 deletions packages/binding.if/docs/_config.yml

This file was deleted.

88 changes: 88 additions & 0 deletions tko.io/.eleventy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import markdownIt from "markdown-it";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Port to TypeScript at some point?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally yes, good point, but I'm not sure how / whether eleventy can import it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


export default function(eleventyConfig) {
// Follow symlinks
eleventyConfig.setServerOptions({
followSymlinks: true
});

// Copy static assets
eleventyConfig.addPassthroughCopy("src/css");
eleventyConfig.addPassthroughCopy("src/js");
eleventyConfig.addPassthroughCopy("src/lib");
eleventyConfig.addPassthroughCopy("src/CNAME");

// Watch for changes
eleventyConfig.addWatchTarget("src/css/");
eleventyConfig.addWatchTarget("src/js/");

// Configure markdown
const md = markdownIt({
html: true,
breaks: false,
linkify: true
});

eleventyConfig.setLibrary("md", md);

// Create collections for each documentation category
eleventyConfig.addCollection("bindingDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/bindings/*.md")
.filter(page => !page.url.endsWith('/bindings/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

eleventyConfig.addCollection("observableDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/observables/*.md")
.filter(page => !page.url.endsWith('/observables/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

eleventyConfig.addCollection("computedDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/computed/*.md")
.filter(page => !page.url.endsWith('/computed/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

eleventyConfig.addCollection("componentDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/components/*.md")
.filter(page => !page.url.endsWith('/components/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

eleventyConfig.addCollection("bindingContextDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/binding-context/*.md")
.filter(page => !page.url.endsWith('/binding-context/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

eleventyConfig.addCollection("advancedDocs", function(collectionApi) {
return collectionApi.getFilteredByGlob("src/docs/advanced/*.md")
.filter(page => !page.url.endsWith('/advanced/'))
.sort((a, b) => a.fileSlug.localeCompare(b.fileSlug));
});

// Add filter to format titles from file slugs
eleventyConfig.addFilter("formatTitle", function(slug) {
return slug
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
});

// Add filter to format binding names (keeps lowercase, removes -binding suffix)
eleventyConfig.addFilter("formatBindingName", function(slug) {
return slug.replace(/-binding$/, '');
});

return {
dir: {
input: "src",
output: "_site",
includes: "_includes",
layouts: "_layouts"
},
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk"
};
}
5 changes: 4 additions & 1 deletion tko.io/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
build
node_modules/
_site/
package-lock.json
src/lib/
64 changes: 64 additions & 0 deletions tko.io/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# TKO Documentation Site

This is the documentation website for TKO, built with 11ty (Eleventy).

## Development

```bash
# Install dependencies
bun install

# Start local development server
bun run dev

# Build for production
bun run build
```

The site will be available at http://localhost:8080/

## Features

- Written in Markdown
- Interactive JSX examples using esbuild-wasm
- Examples are written in code blocks and automatically transformed into editable textareas with live preview
- Styled with the classic TKO theme

## Structure

```
tko.io/
├── src/
│ ├── _layouts/ # Page layouts (Nunjucks)
│ ├── _includes/ # Reusable components
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript (example system)
│ ├── docs/ # Documentation pages (Markdown)
│ └── index.md # Homepage
├── _site/ # Built site (gitignored)
└── .eleventy.js # 11ty configuration
```

## Adding Documentation

1. Create a new `.md` file in `src/docs/`
2. Add front matter with layout and title:
```yaml
---
layout: base.njk
title: Your Page Title
---
```
3. Write content in Markdown
4. Add interactive JSX examples using:
````markdown
```jsx
function Example() {
return <div>Hello World</div>
}
```
````

## Deployment

The site is automatically deployed to GitHub Pages when changes are pushed to the `main` branch.
42 changes: 0 additions & 42 deletions tko.io/app.yaml

This file was deleted.

Loading
Loading