From 748998408735847c48f6cfc044bc378f8d68c6e7 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 09:20:41 -0500 Subject: [PATCH 01/12] remove old tko.io/ --- tko.io/app.yaml | 42 --- tko.io/make.js | 110 ------- tko.io/package.json | 27 +- tko.io/settings.yaml | 428 -------------------------- tko.io/src/index.pug | 31 -- tko.io/src/tko-io.js | 118 -------- tko.io/src/tko.css | 285 ------------------ tko.io/yarn.lock | 698 ------------------------------------------- 8 files changed, 14 insertions(+), 1725 deletions(-) delete mode 100644 tko.io/app.yaml delete mode 100755 tko.io/make.js delete mode 100644 tko.io/settings.yaml delete mode 100644 tko.io/src/index.pug delete mode 100644 tko.io/src/tko-io.js delete mode 100644 tko.io/src/tko.css delete mode 100644 tko.io/yarn.lock diff --git a/tko.io/app.yaml b/tko.io/app.yaml deleted file mode 100644 index fd142e2ef..000000000 --- a/tko.io/app.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# -# This is the Google App Engine standard config. -# -# Deploy with the `gcloud` tool using: -# -# $ gcloud app deploy --project tko-io . -# -# Test with `dev_appserver.py .` -# -runtime: python27 -api_version: 1 -threadsafe: true - -default_expiration: 30s - -handlers: - -- url: / - secure: always - static_files: build/index.html - upload: build/index.html - # http_headers: ... - -- url: /3to4 - secure: always - static_files: build/3to4.html - upload: build/3to4.html - -- url: /(.*\.js)$ - secure: always - static_files: src/\1 - upload: src/.*$ - -- url: / - secure: always - static_dir: build/ - - -skip_files: - - node_modules - - ".*.yaml" - - ".*.lock" diff --git a/tko.io/make.js b/tko.io/make.js deleted file mode 100755 index 02e0a3a85..000000000 --- a/tko.io/make.js +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs-extra') -const yaml = require('js-yaml') -const pug = require('pug') -const debounce = require('lodash.debounce') -const hljs = require('highlight.js') -const {spawn} = require('child_process') -const {argv} = process - -function highlight (str, lang) { - if (!lang) { return '' } - if (hljs.getLanguage(lang)) { - try { - return hljs.highlight(lang, str).value - } catch (__) {} - } - return '' -} - -const md = require('markdown-it')({ - html: true, - linkify: true, - typographer: true, - highlight -}) - -const ENC = {encoding: 'utf8'} - -function * genHtmlIncludes ({includes}, htmlSettings, config) { - for (const relPath of includes || []) { - const sourcePath = '../packages/' + relPath - console.log(' | ', sourcePath) - const source = fs.readFileSync(sourcePath, ENC) - if (sourcePath.endsWith('.md')) { - yield ` - ${md.render(source)}` - } else if (sourcePath.endsWith('.pug')) { - yield pug.render(source, Object.assign({}, htmlSettings, config)) - } else { - throw new Error(`Bad extension: ${sourcePath} (not .md or .pug)`) - } - } -} - -function * genSections (htmlConfig, config) { - for (const section of htmlConfig.sections) { - console.log(` |- ${section.title}`) - section.html = Array.from(genHtmlIncludes(section, htmlConfig, config)).join('\n') - yield section - } -} - -function makeHtml({htmlSettings, scripts, links, styles}, config) { - /** - * Make build/index.html - */ - console.log(` Making ${htmlSettings.dest}`) - Object.assign(htmlSettings, {scripts, links, styles}) // add links + scripts - const sections = Array.from(genSections(htmlSettings, config)) - const locals = Object.assign(htmlSettings, {sections}) - const html = pug.renderFile('src/index.pug', locals) - fs.writeFileSync(htmlSettings.dest, html) -} - -function make () { - console.log('👲 Starting at ', new Date()) - - /** - * Make build/ - */ - fs.mkdirs('build/') - - const styles = fs.readFileSync('src/tko.css') - const config = yaml.load(fs.readFileSync('./settings.yaml', ENC)) - const {scripts, links} = config - - makeHtml({ htmlSettings: config.index, styles, scripts, links }, config) - makeHtml({ htmlSettings: config['3to4'], styles, scripts, links }, config) - - console.log("🏁 Complete.", new Date()) - /** - * Make Legacy Javascript - */ - // console.log('Making build/tko-io.js') - // fs.copySync('src/tko-io.js', 'build/tko-io.js') -} - -if (argv.includes('-w')) { - const ignored = '' // /(^|[\/\\])(\..|build\/*)/ - require('chokidar') - .watch(['settings.yaml', 'src', '../packages'], {ignored}) - .on('all', debounce(make, 150)) -} else { - console.log(` - Usage: make.js [-w] [-s] - - -w Watch and rebuild on change - -s Start the server with 'dev_appserver.py .' (from Google Cloud) - - Deploy with: - - $ gcloud app deploy --project tko-io . - `) - make() -} - -if (argv.includes('-s')) { - spawn('dev_appserver.py', ['.'], {stdio: 'inherit'}) -} diff --git a/tko.io/package.json b/tko.io/package.json index 244c811e7..e22d45b25 100644 --- a/tko.io/package.json +++ b/tko.io/package.json @@ -1,18 +1,19 @@ { - "name": "tko.io", + "name": "tko-docs", "version": "1.0.0", - "description": "Landing page for tko.io", - "main": "index.js", - "author": "Knockout Team", + "type": "module", + "description": "TKO documentation site", + "scripts": { + "dev": "bunx @11ty/eleventy --serve", + "build": "bunx @11ty/eleventy", + "start": "bunx @11ty/eleventy --serve" + }, + "keywords": ["tko", "knockout", "documentation"], + "author": "", "license": "MIT", - "dependencies": { - "chokidar": "^1.7.0", - "fs-extra": "^4.0.2", - "gray-matter": "^3.1.1", - "highlight.js": "^9.12.0", - "js-yaml": "^3.10.0", - "lodash.debounce": "^4.0.8", - "markdown-it": "^8.4.0", - "pug": "^3.0.3" + "devDependencies": { + "@11ty/eleventy": "^3.0.0", + "esbuild-wasm": "^0.24.0", + "markdown-it": "^14.1.0" } } diff --git a/tko.io/settings.yaml b/tko.io/settings.yaml deleted file mode 100644 index f950678e4..000000000 --- a/tko.io/settings.yaml +++ /dev/null @@ -1,428 +0,0 @@ - -links: -- href: https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css - integrity: sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M - -- href: https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/atom-one-dark.min.css - integrity: sha256-akwTLZec/XAFvgYgVH1T5/369lhA2Efr22xzCNl1nHs= - -- href: https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css - integrity: sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN - -scripts: -- src: https://unpkg.com/tko@4.0.0-alpha4a/dist/tko.js - integrity: sha384-MJ/drYzT5bwJWdFxjHRDTJHTNccRZb70G7rQ9GeBnrSW/ssr3+A0kA1Ul7IHBZ/Y - crossorigin: anonymous - -- src: https://production-assets.codepen.io/assets/embed/ei.js - async: async - -- src: tko-io.js - type: application/javascript - async: async - -3to4: - dest: build/3to4.html - sections: - - title: Knockout 3 to 4 Guide - includes: - - tko/docs/3to4.md - -index: - dest: build/index.html - sections: - - title: Introduction - includes: - - tko/docs/intro.md - - tko/docs/books.pug - # - tko/docs/amd-loading.md - # - tko/docs/animated.md - # - tko/docs/api.md - # - tko/docs/browser-support.md - # - tko/docs/docs.md - # - tko/docs/fn.md - # - tko/docs/installation.md - # - tko/docs/intro.md - # - tko/docs/links.md - # - tko/docs/plugins.md - - # - title: Binding DOM and Javascript - # includes: - # - tko.bind/docs/binding-syntax.md - # - tko.bind/docs/binding-context.md - # - tko.provider.databind/docs/databind-parser.md - # - tko.provider/docs/provider.md - # - tko.bind/docs/binding-preprocessing.md - # - tko.bind/docs/custom-bindings.md - # - tko.bind/docs/custom-bindings-for-virtual-elements.md - # - tko.bind/docs/custom-bindings-controlling-descendant-bindings.md - # - tko.bind/docs/custom-bindings-disposal.md - - # - title: Included bindings - # includes: - # - tko.binding.core/docs/attr-binding.md - # - tko.binding.core/docs/checked-binding.md - # - tko.binding.core/docs/click-binding.md - # - tko.binding.core/docs/css-binding.md - # - tko.binding.core/docs/disable-binding.md - # - tko.binding.core/docs/enable-binding.md - # - tko.binding.core/docs/event-binding.md - # - tko.binding.core/docs/hasfocus-binding.md - # - tko.binding.core/docs/html-binding.md - # - tko.binding.core/docs/options-binding.md - # - tko.binding.core/docs/selectedOptions-binding.md - # - tko.binding.core/docs/style-binding.md - # - tko.binding.core/docs/submit-binding.md - # - tko.binding.core/docs/text-binding.md - # - tko.binding.core/docs/textInput-binding.md - # - tko.binding.core/docs/uniqueName-binding.md - # - tko.binding.core/docs/unobtrusive-event-handling.md - # - tko.binding.core/docs/value-binding.md - # - tko.binding.core/docs/visible-binding.md - # - tko.binding.if/docs/if-binding.md - # - tko.binding.if/docs/ifnot-binding.md - # - tko.binding.if/docs/index.md - # - tko.binding.if/docs/with-binding.md - # - tko.binding.template/docs/foreach-binding.md - # - tko.binding.template/docs/template-binding.md - - # - title: Observables - # includes: - # - tko.observable/docs/arraychange.md - # - tko.observable/docs/observableArrays.md - # - tko.observable/docs/observables.md - # - tko.observable/docs/rateLimit-observable.md - # - tko.observable/docs/extenders.md - # - tko.observable/docs/throttle-extender.md - # - tko/docs/json-data.md - - - # - title: Computed Observables - # includes: - # - tko.computed/docs/computed-dependency-tracking.md - # - tko.computed/docs/computed-pure.md - # - tko.computed/docs/computed-reference.md - # - tko.computed/docs/computed-writable.md - # - tko.computed/docs/computedObservables.md - - # - title: Components & Custom Tags - # includes: - # - tko.utils.component/docs/component-binding.md - # - tko.utils.component/docs/component-custom-elements.md - # - tko.utils.component/docs/component-loaders.md - # - tko.utils.component/docs/component-overview.md - # - tko.utils.component/docs/component-registration.md - - - # - title: Life Cycles - # includes: - # - tko.lifecycle/docs/life-cycle.md - - # - title: Plugins - - # - title: API - - -# The following can be auto-generated from `volumeInfo` via -# https://www.googleapis.com/books/v1/volumes/${id} -books: - - - title: Knockout.js - subtitle: Building Dynamic Client-Side Web Applications - authors: - - Jamie Munro - publisher: Oreilly & Associates Incorporated - publishedDate: '2015-01-03' - description: "

Use Knockout.js to design and build dynamic client-side web applications - that are extremely responsive and easy to maintain. This example-driven book shows - you how to use this lightweight JavaScript framework and its Model-View-ViewModel - (MVVM) pattern. You’ll learn how to build your own data bindings, extend the framework - with reusable functions, and work with a server to enhance your client-side application - with persistence. In the final chapter, you’ll build a shopping cart to see how - everything fits together.

If you’re a web developer with experience in - JavaScript, HTML, and CSS, you’re ready for Knockout.

" - industryIdentifiers: - - type: ISBN_10 - identifier: '1491914319' - - type: ISBN_13 - identifier: '9781491914311' - readingModes: - text: false - image: false - pageCount: 86 - printedPageCount: 86 - dimensions: - height: 23.30 cm - width: 17.80 cm - thickness: 0.50 cm - printType: BOOK - categories: - - Computers / Programming Languages / JavaScript - - Computers / Internet / Application Development - - Computers / Web / Web Programming - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: preview-1.0.0 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=ZPDnoQEACAAJ&printsec=frontcover&img=1&zoom=5&imgtk=AFLRE73xh8xZACPIjOtB8rD5RE06-ElVbzgtH5e0nmkyo0UjgGwBZweLwET2ieAFeO_E6k4vI1gqVVxyRWIhOlXmxrprvBuMm-oQigjlTEfm9M9bABMXNcxwZKRTZvvdUBkP14nJ6MIp&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=ZPDnoQEACAAJ&printsec=frontcover&img=1&zoom=1&imgtk=AFLRE717dECAVjOpJblfNu0EPHVMBQmgOOan5rn21GWN2OQhSbPiehsljCYRsxdJQ1wcejFwt0wBmNnRraYQUHHWPqjrHJW0kRSrXjobRrKukJDD-hhmn5DWwNGA--8nQVITS2vbv8t6&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=ZPDnoQEACAAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=ZPDnoQEACAAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/Knockout_js.html?hl=&id=ZPDnoQEACAAJ - - - title: Asp.net Mvc 5 With Bootstrap and Knockout.js - subtitle: Building Dynamic, Responsive Web Applications - authors: - - Jamie Munro - publisher: O'Reilly Media, Incorporated - publishedDate: '2015-03-25' - description: "

Bring dynamic server-side web content and responsive web design - together to build websites that work and display well on any resolution, desktop - or mobile. With this practical book, you’ll learn how by combining the ASP.NET - MVC server-side language, the Bootstrap front-end framework, and Knockout.js—the - JavaScript implementation of the Model-View-ViewModel pattern.

Author Jamie - Munro introduces these and other related technologies by having you work with - sophisticated web forms. At the end of the book, experienced and aspiring web - developers alike will learn how to build a complete shopping cart that demonstrates - how these technologies interact with each other in a sleek, dynamic, and responsive - web application.

" - industryIdentifiers: - - type: ISBN_10 - identifier: '1491914394' - - type: ISBN_13 - identifier: '9781491914397' - readingModes: - text: false - image: false - pageCount: 278 - printedPageCount: 278 - dimensions: - height: 23.30 cm - width: 17.80 cm - thickness: 1.30 cm - printType: BOOK - categories: - - Computers / Programming Languages / ASP.NET - - Computers / Web / General - - Computers / Web / Web Programming - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: preview-1.0.0 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=try8oQEACAAJ&printsec=frontcover&img=1&zoom=5&imgtk=AFLRE71NgZ3twak4WXm7mMrpPXQwM5LLeI3zeLgpf4CSBir_nKmU0mZJoh6Z5JPwwrMk_WjQE_j04VenoDv3PBQAY0yCp4w_tW5TM4BjdModKb6BhbGfnYR0Qt1Qmn6SmtV6WDNhsqd2&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=try8oQEACAAJ&printsec=frontcover&img=1&zoom=1&imgtk=AFLRE71KQ8fmuM2sSfN0TOooxRRz51cBqbN-oSufwl2TKH6qSzuzhI_Sarc_I5dMWgvbKB1SmWvA3IREW1ByGHSRAnTsybrM8B4LxCKEjavv9Zq2VxVBR3pweSl2UWtLe4Oav0m5_S5G&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=try8oQEACAAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=try8oQEACAAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/Asp_net_Mvc_5_With_Bootstrap_and_Knockou.html?hl=&id=try8oQEACAAJ - - - title: Getting Started with Knockout.js for .NET Developers - authors: - - Andrey Akinshin - publisher: Packt Publishing Ltd - publishedDate: '2015-05-27' - description: This book is intended for .NET developers who want to use the MVVM - design pattern to create powerful client-side JavaScript linked to server-side - C# logic. Basic experience with ASP.NET, Razor, and creating web applications - is needed. - industryIdentifiers: - - type: ISBN_10 - identifier: '1783984015' - - type: ISBN_13 - identifier: '9781783984015' - readingModes: - text: true - image: true - pageCount: 188 - printedPageCount: 188 - printType: BOOK - categories: - - Computers / Programming Languages / JavaScript - - Computers / Internet / Application Development - - Computers / Programming Languages / ASP.NET - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: 0.1.1.0.preview.3 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE700Ur2bUx3-xG1lJ7dhXYNiVxT0TlvFTCLKBDmCgsc2s1pOb_YVZxtI6jUwv53hOOAMnvSH2j3GR2NKExvEev5ALEK68u7oLyrUTxA-4j5iwmH7jEV0USmCSJE8PE6Nke2aUWS2&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE72rh8mTdqUnmJDjLIi1wLaea_frTsNMmKGHxTa2lID6PyuW_qNH_XzvkxqDVn3QEa2LfnTsvAmj6Vq_Wd73Zl4Q9_gIQA0lmIwRfK0Zr17YFHAV9uSx-9q5bTOOepwmRpwG3fbG&source=gbs_api - small: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE71Y9WVxVSRTitqbcivBU-WgoXUCyyPTuNsjEn7uZ9oFTc7vKFCJekAx1vMoo_v4lV59_ehco3Lt8vcJTalPxGaIZj57lo1DPirPBwRhGu_kuQtALcu0khKaKimJdwj4Sp_kEIEj&source=gbs_api - medium: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE71n9MaRenbMt90bPtLsyELGmzZq6U4KEbYvVRl7e_kLBJD0QvZ36lTpLejO3BJ4i3WWHL-hzY10ThMf3Qg5vSGD4q3TFoHU62YmSImkw1obbyph6p04pfMutEekEHYXO6d-Yuc9&source=gbs_api - large: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE73BGDB8DqNQzhG2wE-jBgyb-JdjNtiNXeqazRtGxQUwrTnpjHSg2G5LSXaassUEItKLZbyrt8myz6fLL2dl5M_xvH-tZosq63njrzlZYkJuJqjhj2ZxNItqlkYShzh6C0jMklxu&source=gbs_api - extraLarge: https://books.google.ca/books/content?id=A-iuCQAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE72ntS5Ll1QmMWu-A2hwAdGAqTBeG9EEYgH4qlIvCZH3p5gaoPJO4XxPTCKfOZVpVzPN0gTVAoTclhd6v1Yrmk5dvyfX4032bvw9fEp9HP1AemUNgCh_Cjtgzcwma2HlyVG9n7jh&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=A-iuCQAAQBAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=A-iuCQAAQBAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/Getting_Started_with_Knockout_js_for_NET.html?hl=&id=A-iuCQAAQBAJ - - - title: Mastering KnockoutJS - authors: - - Timothy Moran - publisher: Packt Publishing Ltd - publishedDate: '2014-11-26' - description: If you are an experienced JavaScript developer who is looking for new - tools to build web applications and get an understanding of core elements and - applications, this is the book for you. A basic knowledge of DOM, JavaScript, - and KnockoutJS is assumed. - industryIdentifiers: - - type: ISBN_10 - identifier: '1783981016' - - type: ISBN_13 - identifier: '9781783981014' - readingModes: - text: true - image: true - pageCount: 270 - printedPageCount: 489 - printType: BOOK - categories: - - Computers / Web / Design - - Computers / Programming Languages / JavaScript - - Computers / Internet / Application Development - maturityRating: NOT_MATURE - allowAnonLogging: true - contentVersion: 1.1.1.0.preview.3 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE71OGVtK3R39HAA3YPPexcAfdqkhHpK2qY2aqN2VwnuRbzJ5pWfCdPP_5p0KuPpod7GvQPI8wHYLAO8zssun_5j31mq5DrjNHULX097DHieG55cZxoF3R3nX4po7w9urdhoH83CT&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE70GS42a68O_DBLirScLduiYOUbk80jGntqSuuykg8RU1usaN5xdHy5Gl13pgEb2LdZYZ7b5sYtt4qmedX1FiwDuhbprf-X1ADNLHolZCVletNeHvoUvy0gFKPplc4eopBGHQnPG&source=gbs_api - small: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE72HypPfd5WB7VNPF5ovT8veIkql_616DZvT64hc5QPN8HhYCwP3F_XuPKD5D3euB0qJSau7qlRgyrp7VjnOACjsF10v9PekqqAw7fnOakZVnLGBJ3_tWNTl-GByWWxzTDi1rXrQ&source=gbs_api - medium: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE72bJ_PJ9fydBJC1A4b8n-F1x_VHnyeBSp9uRJTFYxQVcIyEO-hCcU51x-TmUrH4JOblv0J2g2OzpwxQK2oELDtzlE7qzE0a_D-KsqSqoFFPXLlPrL45r8fLLXvTRUubJNJy31No&source=gbs_api - large: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE70OgKfn3_48ic1MFdtFGQyoOBg8JHQwln0q5M1fKTYREAuugBOOtf5Q7m6_yD-DEN_n933Y5labtoiq-KM_BEWWukCgkOeJvT1Xr-XuATJrtXX9N-cq1Z-1qbfO3ncRz0yMo-_c&source=gbs_api - extraLarge: https://books.google.ca/books/content?id=BoqbBQAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE71OClJjx-G3VrcH4lga7AKeiw0CGOSwyAvjxfZ8ns3TKv_o8SLzY_p3_e5Q8kJgiVXA1GWpNT-kWgwNwHa5BYsVHbF5DXlGpqyLGxOT8fOXBlsBr8e_H375DucdcSP-0v7L1jF8&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=BoqbBQAAQBAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=BoqbBQAAQBAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/Mastering_KnockoutJS.html?hl=&id=BoqbBQAAQBAJ - - - title: Web App Testing Using Knockout.JS - authors: - - Roberto Messora - publisher: Packt Publishing Ltd - publishedDate: '2014-11-17' - description: If you are a JavaScript developer, beginner, or an expert who wants - to improve quality standards in terms of solutions design and functional verification, - this book is for you. Basic understanding of web development, HTML, and JavaScript - is required. - industryIdentifiers: - - type: ISBN_10 - identifier: '1783982853' - - type: ISBN_13 - identifier: '9781783982851' - readingModes: - text: true - image: true - pageCount: 154 - printedPageCount: 237 - printType: BOOK - categories: - - Computers / Software Development & Engineering / Quality Assurance & Testing - - Computers / Programming Languages / JavaScript - - Computers / Web / Web Programming - averageRating: 5 - ratingsCount: 2 - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: 1.1.1.0.preview.3 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE70A3vdwml0dXDKn1YCWpz8n0p8IKHaCN6FnhytFUt7ZiwPTttCa3_2-Q6ZRyyeCrTzWd3AvkTSQwvO31hHUZjEbUVKlNij50crBaQerevytbQWF6ZMvTW6qxlFH-DfN-AvUv6e2&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE71zzDoAJ8_68JqPI9sZ3Tlvyhyqxiyi_z0QwpVpjChRp5YC3bKNGiUBlDLThbtmQ3uIdVes5uGumrDrmUPlvGA_3PSqev9ZbCwthmLIjwEisbAwI1z2nJNzqwSHb7b-8uEekpvB&source=gbs_api - small: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE71Bq-gmv0FqD2RAIGkqGsnAMEI2VSAlzOme8QiPTpwA09b16Xx9fwBL7WAHIONiyVnFu6iACEO9SVQqZDGf9l56_gTMChhENAXWnM_ZnV5l-K4ouOHjqhAzx61yzxdj2OgmS7Bl&source=gbs_api - medium: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE72glYr_QFUGY7Xc2cBVMa1kXnEaRIn5W2ULqSu6McZlyOZLXhZ1Aw2EYxWmfIzkpr9Le6d8D0AmMCP2YI8RRAAkmrrtQxTO3ow44Gf_kINC2GIrZtLywg9ul7tGgtJNdqMLyfWi&source=gbs_api - large: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE72x5vBQpMoPigr6fGa246ERfQvakIU7pB6PpTEUbktcoWhXxik5WF8TohPvbPuOGXqG0Lzh1JmSy6N7PIVpqCJXIyHV2UIzOxz1u1SeyfQNatsajdpQ16rGp6uaJxPhT31o0JUy&source=gbs_api - extraLarge: https://books.google.ca/books/content?id=dZN1BQAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE73xjKYwV0fwumEQIryeYX8vFDu7KUe8WgpYN-QvZU8ZMbVU5b5Ua8cYnD_FBBGpDTf22w_tiYOC8G9BpvSOkHaO07HRV-YDM51FaVQWlXywSXL7UVgOWUaI0iG5xbY3M6CJWMOz&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=dZN1BQAAQBAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=dZN1BQAAQBAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/Web_App_Testing_Using_Knockout_JS.html?hl=&id=dZN1BQAAQBAJ - - - title: KnockoutJS Essentials - authors: - - Jorge Ferrando - publisher: Packt Publishing Ltd - publishedDate: '2015-02-27' - description: If you are a JavaScript developer who has been using DOM manipulation - libraries such as Mootools or Scriptaculous, and you want go further in modern - JavaScript development with a simple and well-documented library, then this book - is for you. Learning how to use Knockout will be perfect as your next step towards - building JavaScript applications that respond to user interaction. - industryIdentifiers: - - type: ISBN_10 - identifier: '1784395285' - - type: ISBN_13 - identifier: '9781784395285' - readingModes: - text: true - image: true - pageCount: 232 - printedPageCount: 232 - printType: BOOK - categories: - - Computers / Internet / General - - Computers / Internet / Application Development - - Computers / Programming Languages / Java - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: 2.2.2.0.preview.3 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE73KcIEK76fLL4WMQf9A1CJTvx-SGdI5O2ezmLb54SZbJGzguyU7PjmjzBRqRYw0fFGHmcGBjfwozfYegr6gQ12u-c5vUhXf7szQkTjXEXWV5Y0KQcgDfg_OFQmKi9h-9cm-4zdb&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE72iT2HkL3gawhD9TqH0Ozcj5SZW5bchxnpKvWAjxtYdxh8HcnIKbul_cXzv6zK-EDmZh5QY29qCYf-JQQEGU6sjiaaVSoeGbbezRI32UgWJNVpiPu6kd2aeMSZa5Vamelt0nAda&source=gbs_api - small: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE72qNwfz7Ar9qbXS_7Z_y2lsYAYpWy2pEy37MmEtZSajj0E7AMzXnEHIk2EG6otNwbELtO63zSSVjiAs3jvlerXq_N-uD227lrRMSDt8-N2-rZr0d396FW0F0oukatK3HcBwkBzc&source=gbs_api - medium: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE72m6oGoEwL5xAbLyuU4C_334JN-IgaHkW70a4mFNRW58iirrlcuJaZ8hDEPGcADH_Qd1HeLukT2e_1gxJUEqOVGAqdTJopfflSRdEFSy_d3R3TFxvTCvrWqw2b9RBsbgnWL-vu2&source=gbs_api - large: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE727dgJxfT9K-HpbDFySw0YytRHfJV1Nr8z87ClKXeljh02ZGsv-9KyehOnyFMH11gXgyN7Rks3adufGjMDwsMgxCVvvsFQgX2LHt1_jeB7EoPeoVgRdKllAd6TqnHSvPYG5dePq&source=gbs_api - extraLarge: https://books.google.ca/books/content?id=HEXfBgAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE70WdC_9qZ_FtUczIwDq4_4XBZMUl-HwNXyucuOI0v4OCtyRxQAgvDM3m7jJDeXcrm83b-Bi5-TdzSkjxZu4RbpTzW4NL8mPDRAFUlMGPViNw4xMw-ebSHRkgWYsMriAUYjYp_OZ&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=HEXfBgAAQBAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=HEXfBgAAQBAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/KnockoutJS_Essentials.html?hl=&id=HEXfBgAAQBAJ - - - title: KnockoutJS Blueprints - authors: - - Carlo Russo - publisher: Packt Publishing Ltd - publishedDate: '2015-02-25' - description: If you are a JavaScript developer and already know the basics of KnockoutJS - and you want to get the most out of it, then this book is for you. This book will - help in your transition from a small site to a large web application that is easily - maintainable. - industryIdentifiers: - - type: ISBN_10 - identifier: '1783980850' - - type: ISBN_13 - identifier: '9781783980857' - readingModes: - text: true - image: true - pageCount: 218 - printedPageCount: 218 - printType: BOOK - categories: - - Computers / General - - Computers / Programming Languages / JavaScript - maturityRating: NOT_MATURE - allowAnonLogging: false - contentVersion: 2.2.2.0.preview.3 - imageLinks: - smallThumbnail: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE73zg0-XBzMzQAcMQW_DYHtbhQovG3pLSICrTsQiuPtQjcoh0uUD8mixpJOoQ5Jyhx_Q39QcF8zNZW9vZxYpfOY2H-vNHZ1Q3F-RtZ4urPVrSSKQauMG9YeZOtFE_uyJhwPTkVmA&source=gbs_api - thumbnail: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE709pFQ7H1gOnU-LAndEX0z9bqHUPQAi5rRoYbighr68U9tTN7ONHq8sgiLrkmhFBO5isUK3dKydyNM1aWDgk4FqErfk2DSbjC2T_jd7nfsnQzXHcFCVEcD3rLNxV7n0pegmMoEy&source=gbs_api - small: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE70TIZJ2jb02xZbyrF-pi7rCkKtRbM9QNCKS-7PZfJcNhsqHXwROEZ1W6tasXaC0cOmDdIrUrgAVLgDmP6Yg6HGwxqeV_MyZdZeeJ8zbhL4R9M0jQpKGggoGGLzLzoJ39ID5dCIn&source=gbs_api - medium: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE714ZgU0oxMuFEtbSrhaZJwsYhvkBEr1D_wYVJsJyiy6NE80XQxdDKopHEcJtSOG-WVKnjsrgrvidnWoxo2kvauH97Nr4iE44nAJeTFsjlLOG0Xv8anJAMSd2_aA4KYAK5cXZ0u8&source=gbs_api - large: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE72zC_ZRz28WbKJAHvaFUB6LqLgItZOT3XobgdMZDn_fnYVVWIg8iv1gMPtNek1yrVlTFwWmWa061ZS5xZVFRxlkXRWPmxiroQjmhiAoCEQ7cjp2Gb6Tos7xcoH1rjVU7GCbMYhH&source=gbs_api - extraLarge: https://books.google.ca/books/content?id=5ljTBgAAQBAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE732FDrzmu-zRydUQ7jmyI9LnQxtWx49_kOuxlwgjW2jI95C_GWB_UWIg02hmKbYRHXbU7jtLw_VL38n-_DUntiCj_f9AG5Lswlq1KaXyjlwfCEXWTIsH82VkQeqnC77S3bqLkFV&source=gbs_api - language: en - previewLink: https://books.google.ca/books?id=5ljTBgAAQBAJ&hl=&source=gbs_api - infoLink: https://books.google.ca/books?id=5ljTBgAAQBAJ&hl=&source=gbs_api - canonicalVolumeLink: https://books.google.ca/books/about/KnockoutJS_Blueprints.html?hl=&id=5ljTBgAAQBAJ diff --git a/tko.io/src/index.pug b/tko.io/src/index.pug deleted file mode 100644 index 50cfa52ca..000000000 --- a/tko.io/src/index.pug +++ /dev/null @@ -1,31 +0,0 @@ -doctype html -html(lang="en") - head - title= "TKO" - meta(charset="UTF-8") - - each link in links - link(rel='stylesheet' href=link.href integrity=link.integrity crossorigin='anonymous') - - each script in scripts - script(src=script.src integrity=script.integrity crossorigin=script.crossorigin type=script.type || "application/javascript" async=script.async) - - style(type='text/css') - | #{styles} - - body - nav.top-bar(data-bind='highlightIfCurrent') - .tko TKO - a(href='/') Introduction - a(href='/3to4') Knockout 3 to 4 Guide - a.external(href='https://github.com/knockout/tko') Github - - nav.sidebar - #toc.toc(data-bind='each: contents') - a(href='#\{{ navId }}' class='nav flex-column' data-bind='template: { nodes: nodes }, class: css, click: click') - - main - each section in sections - .section - .section-title #{section.title} - .section-body !{section.html} diff --git a/tko.io/src/tko-io.js b/tko.io/src/tko-io.js deleted file mode 100644 index af7c2601d..000000000 --- a/tko.io/src/tko-io.js +++ /dev/null @@ -1,118 +0,0 @@ -const {ko} = window - -const GITHUB_ROOT = 'https://github.com/knockout/tko/blob/master/packages/' -const TITLE_QS = '.section-title, h1, h2' - -const titleNodeMap = new Map() - -ko.options.deferUpdates = true - -const titleObserver = new IntersectionObserver(entries => { - entries.forEach(e => titleNodeMap.get(e.target)(e.isIntersecting)) -}) - - -class Title { - constructor ({element, navIdNumber}) { - const navId = `toc-${navIdNumber}` - const depth = this.getDepth(element) - Object.assign(this, {element, navId, depth}) - element.setAttribute('id', this.navId) - - const viewportObservables = ko.observableArray([]) - - for (const node of this.generateSiblingNodes()) { - const nodeInViewport = ko.observable(false) - if (titleNodeMap.has(node)) { - viewportObservables.push(titleNodeMap.get(node)) - } else { - titleNodeMap.set(node, nodeInViewport) - titleObserver.observe(node) - } - viewportObservables.push(nodeInViewport) - } - - this.inViewport = ko.computed(() => viewportObservables().some(o => o())) - } - - * generateSiblingNodes () { - let atNode = this.element - yield atNode - while (atNode = atNode.nextSibling) { - if (atNode.nodeType !== atNode.ELEMENT_NODE) { continue } - if (this.getDepth(atNode) <= this.depth) { return } - yield atNode - } - } - - getDepth (node) { - if (node.classList.contains('section-title')) { return 0 } - if (node.tagName === 'H1') { return 1 } - if (node.tagName === 'H2') { return 2 } - return 3 - } - - get css () { - const css = { 'in-viewport': this.inViewport } - switch (this.element.tagName) { - case 'DIV': Object.assign(css, { section: true }); break - case 'H2': Object.assign(css, { subheading: true }); break - case 'H1': Object.assign(css, { heading: true }); break - } - return css - } - - get nodes () { - const node = document.createElement('span') - node.innerHTML = this.element.innerHTML - return [node] - } - - click (vm, evt) { - this.element.scrollIntoView({ behavior: 'smooth', block: 'start' }) - return false - } -} - -function * generateTitles () { - let navIdNumber = 0 - for (const element of document.querySelectorAll(TITLE_QS)) { - navIdNumber++ - yield new Title({element, navIdNumber}) - } -} - - -/** - * Rely on some convention to map the source file to its parts / github origin. - */ -ko.bindingHandlers.set({ - source (element) { - const origin = this.value // e.g. "../packages/tko/docs/intro.md" - const link = GITHUB_ROOT + origin - const [pkg, file] = origin.split('/docs/') - - element.classList.add('source') - element.setAttribute('title', `This part of the documentation comes from ${origin} on GitHub/knockout/tko`) - element.innerHTML = - ` - - ${pkg} - - ${file} - ` - }, - - highlightIfCurrent (element) { - for (const anchor of element.querySelectorAll('a[href]')) { - if (anchor.href === location.href) { - anchor.classList.add('current-page') - } - } - } -}) - -window.addEventListener("load", () => { - const contents = ko.observableArray(Array.from(generateTitles())) - ko.applyBindings({ contents }) -}) diff --git a/tko.io/src/tko.css b/tko.io/src/tko.css deleted file mode 100644 index b9fe13435..000000000 --- a/tko.io/src/tko.css +++ /dev/null @@ -1,285 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Nunito|Pacifico|Source+Code+Pro'); - -html, -body { - /* Prevent scroll on narrow devices */ - overflow-x: hidden; - font-family: 'Nunito', sans-serif; -} - -body { - display: grid; - margin-top: 75px; - height: calc(100vh - 75px); - - grid-template-columns: minmax(200px, max-content) auto; - grid-template-areas: - 'sidebar main'; -} - -@media screen and (max-width: 991px) { - body { - grid-template-columns: auto; - grid-template-areas: - 'sidebar' - 'main'; - } -} - -a { - color: #590c12; - transition: 0.1s ease-in-out; -} - -a:focus, a:hover, a:active { - color: #8e383c; - cursor: pointer; - text-decoration: none; -} - -main a { - border-bottom: 1px dashed #590c12; -} - -td { - padding: 5px 1em; - border-bottom: 1px solid gray; -} - -th { - padding: 5px 1em; - border-bottom: 1px solid black; -} - - -/* Codepen iFrames */ -iframe { - padding: 0px 1em; -} - - -/* - * TOP - BAR - */ -.top-bar { - width: 100%; - display: grid; - grid-template-columns: auto; - grid-auto-flow: column; - justify-content: center; - grid-column-gap: 24px; - - top: 0; - left: 0; - position: fixed; - z-index: 1200; - padding: 12px; - - background-color: #931d0d; - font-family: Pacifico; -} - -.top-bar a { - border-radius: 12px; - text-align: center; - padding: 0.25em 0.4em; - color: white; - font-size: 1.4rem; - text-shadow: 0 0 5px black; - line-height: 2.4rem; - transition: 0.4s background-color ease-in-out; -} - -.top-bar a:hover { - background-color: #00000080; - text-shadow: 0 0 5px #931d0d, 0 0 10px #931d0d; - transition: 0.4s background-color ease-in-out; -} - -.top-bar .tko { - color: white; - font-size: 1.6rem; - padding: 0.25em 0.4em; - text-shadow: 0 0 5px #efff6c, 0 0 10px #efff6c; -} - -.current-page { - background-color: #00000040; -} - -/** - * SIDE-BAR - */ -.sidebar { - grid-area: sidebar; - - height: calc(100vh - 75px); - /* Scrollable contents if viewport is shorter than content */ - overflow-y: auto; - overflow-x: hidden; - left: 0; - bottom: 0; - z-index: 1000; - padding: 0; - background-color: #efebe9; - border-right: 1px solid #efebe9; - - padding: 4px 0.75em; -} - -.toc { - position: sticky; -} - -.sidebar a { - padding: 1ex 0.25rem; - color: #931d0d; - border-radius: 4px; -} - -.sidebar a:hover { - color: black; - background-color: #FFaFaFB0; - transition: 0.4s background-color ease-in-out; -} - -.sidebar .section { - font-size: 1.2rem; -} - -.sidebar .subheading { - padding: 0.6ex 8px; - font-weight: normal; - margin-left: 1rem; -} - - @keyframes popin { - from { transform: translateX(50%); } - to { transform: translateX(0%); } - } - -.sidebar .in-viewport::after { - position: absolute; - right: -22px; - background-color: #8F3F4F90; - width: 20px; - height: 20px; - border-radius: 12px; - content: ' '; - animation: 0.25s ease-in 0s popin; -} - -/** - * MAIN - */ -main { - grid-area: main; - height: calc(100vh - 75px); - background-color: #fcd4d4; - padding: 16px; - overflow: scroll; -} - -h2, h3, h4 { - margin-top: 1.3em; - margin-bottom: 0.6em; -} - -h2 { - font-weight: 800; -} - -h3, h4, h2 time { - font-weight: 400; -} - -h1 { - margin-top: 1.5em; -} - -h1 code { - background-color: transparent; - border: 3px solid #E8D8D8; - font-size: inherit; -} - -/** - * CODE - */ - -pre { - font-family: 'Source Code Pro', monospace; - background-color: #0a0909; - padding: 1ex 1em; - margin-left: 1em; - border-radius: 1px; - border: 1px black inset; -} - -code { - background-color: #080707; - color: #e6c07b; - font-size: 0.9rem; -} - -main code { - font-family: 'Source Code Pro', monospace; -} - -code, td code { - white-space: nowrap; - padding: 2px 8px; -} - -pre code { - color: #e6c07b; - white-space: pre; /* We want newlines to be newlines in code blocks */ -} - -/** - * - * TITLES - * - */ - -.section-title { - font-size: 3rem; - font-weight: bold; -} - - - - - -/** - * Source linking - */ -.source { - display: block; - text-align: right; - font-size: 0.9rem; - position: sticky; - top: 0px; -} - -.source a { - padding: 2px 0.62em; - background-color: #E8D8D8; - border: 1px solid gray; -} - -/** - * - * BOOKS - * - */ -.book-grid { - display: grid; - grid-template-columns: repeat(auto-fit, 250px); - grid-gap: 10px; - justify-content: space-around; -} - -.book-grid .card { - width: 250px; -} diff --git a/tko.io/yarn.lock b/tko.io/yarn.lock deleted file mode 100644 index 999ef5924..000000000 --- a/tko.io/yarn.lock +++ /dev/null @@ -1,698 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/helper-string-parser@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" - -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - -"@babel/parser@^7.6.0", "@babel/parser@^7.9.6": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" - dependencies: - "@babel/types" "^7.26.3" - -"@babel/types@^7.26.3", "@babel/types@^7.6.1", "@babel/types@^7.9.6": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" - dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - -assert-never@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.4.0.tgz#b0d4988628c87f35eb94716cc54422a63927e175" - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -babel-walk@3.0.0-canary-5: - version "3.0.0-canary-5" - resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" - dependencies: - "@babel/types" "^7.9.6" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -binary-extensions@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - dependencies: - file-uri-to-path "1.0.0" - -brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -character-parser@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" - dependencies: - is-regex "^1.0.3" - -chokidar@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -constantinople@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" - dependencies: - "@babel/parser" "^7.6.0" - "@babel/types" "^7.6.1" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -doctypes@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" - -entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - -esprima@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - dependencies: - is-extendable "^0.1.0" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -for-in@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -fs-extra@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fsevents@^1.0.0: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -function-bind@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -gray-matter@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-3.1.1.tgz#101f80d9e69eeca6765cdce437705b18f40876ac" - dependencies: - extend-shallow "^2.0.1" - js-yaml "^3.10.0" - kind-of "^5.0.2" - strip-bom-string "^1.0.0" - -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - dependencies: - function-bind "^1.1.2" - -highlight.js@^9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" - -inherits@^2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-core-module@^2.16.0: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - dependencies: - hasown "^2.0.2" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-expression@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" - dependencies: - acorn "^7.1.1" - object-assign "^4.1.1" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - dependencies: - kind-of "^3.0.2" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-regex@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - dependencies: - has "^1.0.1" - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -js-stringify@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" - -js-yaml@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - optionalDependencies: - graceful-fs "^4.1.6" - -jstransformer@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" - dependencies: - is-promise "^2.0.0" - promise "^7.0.1" - -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - -linkify-it@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" - dependencies: - uc.micro "^1.0.1" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - -markdown-it@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d" - dependencies: - argparse "^1.0.7" - entities "~1.1.1" - linkify-it "^2.0.0" - mdurl "^1.0.1" - uc.micro "^1.0.3" - -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - -micromatch@^2.1.5: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -minimatch@^3.0.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - dependencies: - brace-expansion "^1.1.7" - -nan@^2.12.1: - version "2.22.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" - -normalize-path@^2.0.0, normalize-path@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -promise@^7.0.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - dependencies: - asap "~2.0.3" - -pug-attrs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" - dependencies: - constantinople "^4.0.1" - js-stringify "^1.0.2" - pug-runtime "^3.0.0" - -pug-code-gen@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.3.tgz#58133178cb423fe1716aece1c1da392a75251520" - dependencies: - constantinople "^4.0.1" - doctypes "^1.1.0" - js-stringify "^1.0.2" - pug-attrs "^3.0.0" - pug-error "^2.1.0" - pug-runtime "^3.0.1" - void-elements "^3.1.0" - with "^7.0.0" - -pug-error@^2.0.0, pug-error@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.1.0.tgz#17ea37b587b6443d4b8f148374ec27b54b406e55" - -pug-filters@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" - dependencies: - constantinople "^4.0.1" - jstransformer "1.0.0" - pug-error "^2.0.0" - pug-walk "^2.0.0" - resolve "^1.15.1" - -pug-lexer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" - dependencies: - character-parser "^2.2.0" - is-expression "^4.0.0" - pug-error "^2.0.0" - -pug-linker@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" - dependencies: - pug-error "^2.0.0" - pug-walk "^2.0.0" - -pug-load@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" - dependencies: - object-assign "^4.1.1" - pug-walk "^2.0.0" - -pug-parser@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" - dependencies: - pug-error "^2.0.0" - token-stream "1.0.0" - -pug-runtime@^3.0.0, pug-runtime@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" - -pug-strip-comments@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" - dependencies: - pug-error "^2.0.0" - -pug-walk@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" - -pug@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.3.tgz#e18324a314cd022883b1e0372b8af3a1a99f7597" - dependencies: - pug-code-gen "^3.0.3" - pug-filters "^4.0.0" - pug-lexer "^5.0.1" - pug-linker "^4.0.0" - pug-load "^3.0.0" - pug-parser "^6.0.0" - pug-runtime "^3.0.1" - pug-strip-comments "^2.0.0" - -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -readable-stream@^2.0.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -resolve@^1.15.1: - version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" - dependencies: - is-core-module "^2.16.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - -token-stream@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" - -uc.micro@^1.0.1, uc.micro@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" - -universalify@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -void-elements@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" - -with@^7.0.0: - version "7.0.2" - resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" - dependencies: - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" - assert-never "^1.2.1" - babel-walk "3.0.0-canary-5" From dca618959776f6fd95d910c1350ea88c18e8b645 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 09:22:33 -0500 Subject: [PATCH 02/12] initial variant of new tko.io site --- .github/workflows/deploy-docs.yml | 47 +++++ tko.io/.eleventy.js | 32 +++ tko.io/.gitignore | 4 +- tko.io/README.md | 64 ++++++ tko.io/bun.lock | 286 ++++++++++++++++++++++++++ tko.io/src/CNAME | 1 + tko.io/src/_layouts/base.njk | 29 +++ tko.io/src/css/tko.css | 325 ++++++++++++++++++++++++++++++ tko.io/src/index.md | 16 ++ tko.io/src/js/examples.js | 116 +++++++++++ 10 files changed, 919 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-docs.yml create mode 100644 tko.io/.eleventy.js create mode 100644 tko.io/README.md create mode 100644 tko.io/bun.lock create mode 100644 tko.io/src/CNAME create mode 100644 tko.io/src/_layouts/base.njk create mode 100644 tko.io/src/css/tko.css create mode 100644 tko.io/src/index.md create mode 100644 tko.io/src/js/examples.js diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 000000000..1b2f127d9 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -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 diff --git a/tko.io/.eleventy.js b/tko.io/.eleventy.js new file mode 100644 index 000000000..a6d9650ca --- /dev/null +++ b/tko.io/.eleventy.js @@ -0,0 +1,32 @@ +import markdownIt from "markdown-it"; + +export default function(eleventyConfig) { + // Copy static assets + eleventyConfig.addPassthroughCopy("src/css"); + eleventyConfig.addPassthroughCopy("src/js"); + 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); + + return { + dir: { + input: "src", + output: "_site", + includes: "_includes", + layouts: "_layouts" + }, + markdownTemplateEngine: "njk", + htmlTemplateEngine: "njk" + }; +} diff --git a/tko.io/.gitignore b/tko.io/.gitignore index 378eac25d..7b652d135 100644 --- a/tko.io/.gitignore +++ b/tko.io/.gitignore @@ -1 +1,3 @@ -build +node_modules/ +_site/ +package-lock.json diff --git a/tko.io/README.md b/tko.io/README.md new file mode 100644 index 000000000..7c8f5048c --- /dev/null +++ b/tko.io/README.md @@ -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
Hello World
+ } + ``` + ```` + +## Deployment + +The site is automatically deployed to GitHub Pages when changes are pushed to the `main` branch. diff --git a/tko.io/bun.lock b/tko.io/bun.lock new file mode 100644 index 000000000..34055fbaf --- /dev/null +++ b/tko.io/bun.lock @@ -0,0 +1,286 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "tko-docs", + "devDependencies": { + "@11ty/eleventy": "^3.0.0", + "esbuild-wasm": "^0.24.0", + "markdown-it": "^14.1.0", + }, + }, + }, + "packages": { + "@11ty/dependency-tree": ["@11ty/dependency-tree@4.0.1", "", { "dependencies": { "@11ty/eleventy-utils": "^2.0.1" } }, "sha512-6EPI9ZkGU4BX2KNZpWlf4WdV3vrmIWQpn//nAXicTzdPubI3jZlmFdqEv0Yj5M7oavRUGNzw9GbV9cBxhulZWw=="], + + "@11ty/dependency-tree-esm": ["@11ty/dependency-tree-esm@2.0.4", "", { "dependencies": { "@11ty/eleventy-utils": "^2.0.7", "acorn": "^8.15.0", "dependency-graph": "^1.0.0", "normalize-path": "^3.0.0" } }, "sha512-MYKC0Ac77ILr1HnRJalzKDlb9Z8To3kXQCltx299pUXXUFtJ1RIONtULlknknqW8cLe19DLVgmxVCtjEFm7h0A=="], + + "@11ty/eleventy": ["@11ty/eleventy@3.1.2", "", { "dependencies": { "@11ty/dependency-tree": "^4.0.0", "@11ty/dependency-tree-esm": "^2.0.0", "@11ty/eleventy-dev-server": "^2.0.8", "@11ty/eleventy-plugin-bundle": "^3.0.6", "@11ty/eleventy-utils": "^2.0.7", "@11ty/lodash-custom": "^4.17.21", "@11ty/posthtml-urls": "^1.0.1", "@11ty/recursive-copy": "^4.0.2", "@sindresorhus/slugify": "^2.2.1", "bcp-47-normalize": "^2.3.0", "chokidar": "^3.6.0", "debug": "^4.4.1", "dependency-graph": "^1.0.0", "entities": "^6.0.1", "filesize": "^10.1.6", "gray-matter": "^4.0.3", "iso-639-1": "^3.1.5", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "liquidjs": "^10.21.1", "luxon": "^3.6.1", "markdown-it": "^14.1.0", "minimist": "^1.2.8", "moo": "^0.5.2", "node-retrieve-globals": "^6.0.1", "nunjucks": "^3.2.4", "picomatch": "^4.0.2", "please-upgrade-node": "^3.2.0", "posthtml": "^0.16.6", "posthtml-match-helper": "^2.0.3", "semver": "^7.7.2", "slugify": "^1.6.6", "tinyglobby": "^0.2.14" }, "bin": { "eleventy": "cmd.cjs" } }, "sha512-IcsDlbXnBf8cHzbM1YBv3JcTyLB35EK88QexmVyFdVJVgUU6bh9g687rpxryJirHzo06PuwnYaEEdVZQfIgRGg=="], + + "@11ty/eleventy-dev-server": ["@11ty/eleventy-dev-server@2.0.8", "", { "dependencies": { "@11ty/eleventy-utils": "^2.0.1", "chokidar": "^3.6.0", "debug": "^4.4.0", "finalhandler": "^1.3.1", "mime": "^3.0.0", "minimist": "^1.2.8", "morphdom": "^2.7.4", "please-upgrade-node": "^3.2.0", "send": "^1.1.0", "ssri": "^11.0.0", "urlpattern-polyfill": "^10.0.0", "ws": "^8.18.1" }, "bin": { "eleventy-dev-server": "cmd.js" } }, "sha512-15oC5M1DQlCaOMUq4limKRYmWiGecDaGwryr7fTE/oM9Ix8siqMvWi+I8VjsfrGr+iViDvWcH/TVI6D12d93mA=="], + + "@11ty/eleventy-plugin-bundle": ["@11ty/eleventy-plugin-bundle@3.0.7", "", { "dependencies": { "@11ty/eleventy-utils": "^2.0.2", "debug": "^4.4.0", "posthtml-match-helper": "^2.0.3" } }, "sha512-QK1tRFBhQdZASnYU8GMzpTdsMMFLVAkuU0gVVILqNyp09xJJZb81kAS3AFrNrwBCsgLxTdWHJ8N64+OTTsoKkA=="], + + "@11ty/eleventy-utils": ["@11ty/eleventy-utils@2.0.7", "", {}, "sha512-6QE+duqSQ0GY9rENXYb4iPR4AYGdrFpqnmi59tFp9VrleOl0QSh8VlBr2yd6dlhkdtj7904poZW5PvGr9cMiJQ=="], + + "@11ty/lodash-custom": ["@11ty/lodash-custom@4.17.21", "", {}, "sha512-Mqt6im1xpb1Ykn3nbcCovWXK3ggywRJa+IXIdoz4wIIK+cvozADH63lexcuPpGS/gJ6/m2JxyyXDyupkMr5DHw=="], + + "@11ty/posthtml-urls": ["@11ty/posthtml-urls@1.0.2", "", { "dependencies": { "evaluate-value": "^2.0.0", "http-equiv-refresh": "^2.0.1", "list-to-array": "^1.1.0", "parse-srcset": "^1.0.2" } }, "sha512-0vaV3Wt0surZ+oS1VdKKe0axeeupuM+l7W/Z866WFQwF+dGg2Tc/nmhk/5l74/Y55P8KyImnLN9CdygNw2huHg=="], + + "@11ty/recursive-copy": ["@11ty/recursive-copy@4.0.3", "", { "dependencies": { "errno": "^1.0.0", "junk": "^3.1.0", "maximatch": "^0.1.0", "slash": "^3.0.0" } }, "sha512-SX48BTLEGX8T/OsKWORsHAAeiDsbFl79Oa/0Wg/mv/d27b7trCVZs7fMHvpSgDvZz/fZqx5rDk8+nx5oyT7xBw=="], + + "@sindresorhus/slugify": ["@sindresorhus/slugify@2.2.1", "", { "dependencies": { "@sindresorhus/transliterate": "^1.0.0", "escape-string-regexp": "^5.0.0" } }, "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw=="], + + "@sindresorhus/transliterate": ["@sindresorhus/transliterate@1.6.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0" } }, "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ=="], + + "a-sync-waterfall": ["a-sync-waterfall@1.0.1", "", {}, "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-walk": ["acorn-walk@8.3.4", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-differ": ["array-differ@1.0.0", "", {}, "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ=="], + + "array-union": ["array-union@1.0.2", "", { "dependencies": { "array-uniq": "^1.0.1" } }, "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng=="], + + "array-uniq": ["array-uniq@1.0.3", "", {}, "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q=="], + + "arrify": ["arrify@1.0.1", "", {}, "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="], + + "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="], + + "bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="], + + "bcp-47-normalize": ["bcp-47-normalize@2.3.0", "", { "dependencies": { "bcp-47": "^2.0.0", "bcp-47-match": "^2.0.0" } }, "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + + "commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dependency-graph": ["dependency-graph@1.0.0", "", {}, "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg=="], + + "dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], + + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], + + "domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], + + "domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "errno": ["errno@1.0.0", "", { "dependencies": { "prr": "~1.0.1" }, "bin": { "errno": "cli.js" } }, "sha512-3zV5mFS1E8/1bPxt/B0xxzI1snsg3uSCIh6Zo1qKg6iMw93hzPANk9oBFzSFBFrwuVoQuE3rLoouAUfwOAj1wQ=="], + + "esbuild-wasm": ["esbuild-wasm@0.24.2", "", { "bin": { "esbuild": "bin/esbuild" } }, "sha512-03/7Z1gD+ohDnScFztvI4XddTAbKVmMEzCvvkBpQdWKEXJ+73dTyeNrmdxP1Q0zpDMFjzUJwtK4rLjqwiHbzkw=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "esm-import-transformer": ["esm-import-transformer@3.0.5", "", { "dependencies": { "acorn": "^8.15.0" } }, "sha512-1GKLvfuMnnpI75l8c6sHoz0L3Z872xL5akGuBudgqTDPv4Vy6f2Ec7jEMKTxlqWl/3kSvNbHELeimJtnqgYniw=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "evaluate-value": ["evaluate-value@2.0.0", "", {}, "sha512-VonfiuDJc0z4sOO7W0Pd130VLsXN6vmBWZlrog1mCb/o7o/Nl5Lr25+Kj/nkCCAhG+zqeeGjxhkK9oHpkgTHhQ=="], + + "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], + + "htmlparser2": ["htmlparser2@7.2.0", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.2", "domutils": "^2.8.0", "entities": "^3.0.1" } }, "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog=="], + + "http-equiv-refresh": ["http-equiv-refresh@2.0.1", "", {}, "sha512-XJpDL/MLkV3dKwLzHwr2dY05dYNfBNlyPu4STQ8WvKCFdc6vC5tPXuq28of663+gHVg03C+16pHHs/+FmmDjcw=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-json": ["is-json@2.0.1", "", {}, "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "iso-639-1": ["iso-639-1@3.1.5", "", {}, "sha512-gXkz5+KN7HrG0Q5UGqSMO2qB9AsbEeyLP54kF1YrMsIxmu+g4BdB7rflReZTSTZGpfj8wywu6pfPBCylPIzGQA=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "junk": ["junk@3.1.0", "", {}, "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], + + "liquidjs": ["liquidjs@10.24.0", "", { "dependencies": { "commander": "^10.0.0" }, "bin": { "liquidjs": "bin/liquid.js", "liquid": "bin/liquid.js" } }, "sha512-TAUNAdgwaAXjjcUFuYVJm9kOVH7zc0mTKxsG9t9Lu4qdWjB2BEblyVIYpjWcmJLMGgiYqnGNJjpNMHx0gp/46A=="], + + "list-to-array": ["list-to-array@1.1.0", "", {}, "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g=="], + + "luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="], + + "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="], + + "maximatch": ["maximatch@0.1.0", "", { "dependencies": { "array-differ": "^1.0.0", "array-union": "^1.0.1", "arrify": "^1.0.0", "minimatch": "^3.0.0" } }, "sha512-9ORVtDUFk4u/NFfo0vG/ND/z7UQCVZBL539YW0+U1I7H1BkZwizcPx5foFv7LCPcBnm2U6RjFnQOsIvN4/Vm2A=="], + + "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + + "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "moo": ["moo@0.5.2", "", {}, "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="], + + "morphdom": ["morphdom@2.7.7", "", {}, "sha512-04GmsiBcalrSCNmzfo+UjU8tt3PhZJKzcOy+r1FlGA7/zri8wre3I1WkYN9PT3sIeIKfW9bpyElA+VzOg2E24g=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "node-retrieve-globals": ["node-retrieve-globals@6.0.1", "", { "dependencies": { "acorn": "^8.14.1", "acorn-walk": "^8.3.4", "esm-import-transformer": "^3.0.3" } }, "sha512-j0DeFuZ/Wg3VlklfbxUgZF/mdHMTEiEipBb3q0SpMMbHaV3AVfoUQF8UGxh1s/yjqO0TgRZd4Pi/x2yRqoQ4Eg=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "nunjucks": ["nunjucks@3.2.4", "", { "dependencies": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", "commander": "^5.1.0" }, "peerDependencies": { "chokidar": "^3.3.0" }, "optionalPeers": ["chokidar"], "bin": { "nunjucks-precompile": "bin/precompile" } }, "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "please-upgrade-node": ["please-upgrade-node@3.2.0", "", { "dependencies": { "semver-compare": "^1.0.0" } }, "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg=="], + + "posthtml": ["posthtml@0.16.7", "", { "dependencies": { "posthtml-parser": "^0.11.0", "posthtml-render": "^3.0.0" } }, "sha512-7Hc+IvlQ7hlaIfQFZnxlRl0jnpWq2qwibORBhQYIb0QbNtuicc5ZxvKkVT71HJ4Py1wSZ/3VR1r8LfkCtoCzhw=="], + + "posthtml-match-helper": ["posthtml-match-helper@2.0.3", "", { "peerDependencies": { "posthtml": "^0.16.6" } }, "sha512-p9oJgTdMF2dyd7WE54QI1LvpBIkNkbSiiECKezNnDVYhGhD1AaOnAkw0Uh0y5TW+OHO8iBdSqnd8Wkpb6iUqmw=="], + + "posthtml-parser": ["posthtml-parser@0.11.0", "", { "dependencies": { "htmlparser2": "^7.1.1" } }, "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw=="], + + "posthtml-render": ["posthtml-render@3.0.0", "", { "dependencies": { "is-json": "^2.0.1" } }, "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA=="], + + "prr": ["prr@1.0.1", "", {}, "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="], + + "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "ssri": ["ssri@11.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-aZpUoMN/Jj2MqA4vMCeiKGnc/8SuSyHbGSBdgFbZxP8OJGF/lFkIuElzPxsN0q8TQQ+prw3P4EDfB3TBHHgfXw=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + + "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + + "htmlparser2/entities": ["entities@3.0.1", "", {}, "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="], + + "markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "nunjucks/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], + + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + } +} diff --git a/tko.io/src/CNAME b/tko.io/src/CNAME new file mode 100644 index 000000000..b091eb46d --- /dev/null +++ b/tko.io/src/CNAME @@ -0,0 +1 @@ +tko.io \ No newline at end of file diff --git a/tko.io/src/_layouts/base.njk b/tko.io/src/_layouts/base.njk new file mode 100644 index 000000000..766411cb0 --- /dev/null +++ b/tko.io/src/_layouts/base.njk @@ -0,0 +1,29 @@ + + + + + + {{ title }} - TKO + + + + + + + +
+ {{ content | safe }} +
+ + + + diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css new file mode 100644 index 000000000..5f9799f27 --- /dev/null +++ b/tko.io/src/css/tko.css @@ -0,0 +1,325 @@ +@import url('https://fonts.googleapis.com/css?family=Nunito|Pacifico|Source+Code+Pro'); + +html, +body { + /* Prevent scroll on narrow devices */ + overflow-x: hidden; + font-family: 'Nunito', sans-serif; +} + +body { + display: grid; + margin-top: 75px; + height: calc(100vh - 75px); + + grid-template-columns: minmax(200px, max-content) auto; + grid-template-areas: + 'sidebar main'; +} + +@media screen and (max-width: 991px) { + body { + grid-template-columns: auto; + grid-template-areas: + 'sidebar' + 'main'; + } +} + +a { + color: #590c12; + transition: 0.1s ease-in-out; +} + +a:focus, a:hover, a:active { + color: #8e383c; + cursor: pointer; + text-decoration: none; +} + +main a { + border-bottom: 1px dashed #590c12; +} + +td { + padding: 5px 1em; + border-bottom: 1px solid gray; +} + +th { + padding: 5px 1em; + border-bottom: 1px solid black; +} + + +/* Codepen iFrames */ +iframe { + padding: 0px 1em; +} + + +/* + * TOP - BAR + */ +.top-bar { + width: 100%; + display: grid; + grid-template-columns: auto; + grid-auto-flow: column; + justify-content: center; + grid-column-gap: 24px; + + top: 0; + left: 0; + position: fixed; + z-index: 1200; + padding: 12px; + + background-color: #931d0d; + font-family: Pacifico; +} + +.top-bar a { + border-radius: 12px; + text-align: center; + padding: 0.25em 0.4em; + color: white; + font-size: 1.4rem; + text-shadow: 0 0 5px black; + line-height: 2.4rem; + transition: 0.4s background-color ease-in-out; +} + +.top-bar a:hover { + background-color: #00000080; + text-shadow: 0 0 5px #931d0d, 0 0 10px #931d0d; + transition: 0.4s background-color ease-in-out; +} + +.top-bar .tko { + color: white; + font-size: 1.6rem; + padding: 0.25em 0.4em; + text-shadow: 0 0 5px #efff6c, 0 0 10px #efff6c; +} + +.current-page { + background-color: #00000040; +} + +/** + * SIDE-BAR + */ +.sidebar { + grid-area: sidebar; + + height: calc(100vh - 75px); + /* Scrollable contents if viewport is shorter than content */ + overflow-y: auto; + overflow-x: hidden; + left: 0; + bottom: 0; + z-index: 1000; + padding: 0; + background-color: #efebe9; + border-right: 1px solid #efebe9; + + padding: 4px 0.75em; +} + +.toc { + position: sticky; +} + +.sidebar a { + padding: 1ex 0.25rem; + color: #931d0d; + border-radius: 4px; +} + +.sidebar a:hover { + color: black; + background-color: #FFaFaFB0; + transition: 0.4s background-color ease-in-out; +} + +.sidebar .section { + font-size: 1.2rem; +} + +.sidebar .subheading { + padding: 0.6ex 8px; + font-weight: normal; + margin-left: 1rem; +} + + @keyframes popin { + from { transform: translateX(50%); } + to { transform: translateX(0%); } + } + +.sidebar .in-viewport::after { + position: absolute; + right: -22px; + background-color: #8F3F4F90; + width: 20px; + height: 20px; + border-radius: 12px; + content: ' '; + animation: 0.25s ease-in 0s popin; +} + +/** + * MAIN + */ +main { + grid-area: main; + height: calc(100vh - 75px); + background-color: #fcd4d4; + padding: 16px; + overflow: scroll; +} + +h2, h3, h4 { + margin-top: 1.3em; + margin-bottom: 0.6em; +} + +h2 { + font-weight: 800; +} + +h3, h4, h2 time { + font-weight: 400; +} + +h1 { + margin-top: 1.5em; +} + +h1 code { + background-color: transparent; + border: 3px solid #E8D8D8; + font-size: inherit; +} + +/** + * CODE + */ + +pre { + font-family: 'Source Code Pro', monospace; + background-color: #0a0909; + padding: 1ex 1em; + margin-left: 1em; + border-radius: 1px; + border: 1px black inset; +} + +code { + background-color: #080707; + color: #e6c07b; + font-size: 0.9rem; +} + +main code { + font-family: 'Source Code Pro', monospace; +} + +code, td code { + white-space: nowrap; + padding: 2px 8px; +} + +pre code { + color: #e6c07b; + white-space: pre; /* We want newlines to be newlines in code blocks */ +} + +/** + * + * TITLES + * + */ + +.section-title { + font-size: 3rem; + font-weight: bold; +} + + + + + +/** + * Source linking + */ +.source { + display: block; + text-align: right; + font-size: 0.9rem; + position: sticky; + top: 0px; +} + +.source a { + padding: 2px 0.62em; + background-color: #E8D8D8; + border: 1px solid gray; +} + +/** + * + * BOOKS + * + */ +.book-grid { + display: grid; + grid-template-columns: repeat(auto-fit, 250px); + grid-gap: 10px; + justify-content: space-around; +} + +.book-grid .card { + width: 250px; +} + +/** + * + * INTERACTIVE EXAMPLES + * + */ +.example-container { + margin: 2em 0; + border: 1px solid #931d0d; + border-radius: 4px; + overflow: hidden; +} + +.example-textarea { + width: 100%; + min-height: 200px; + padding: 1em; + font-family: 'Source Code Pro', monospace; + font-size: 0.9rem; + background-color: #0a0909; + color: #e6c07b; + border: none; + border-bottom: 2px solid #931d0d; + resize: vertical; +} + +.example-result { + width: 100%; + min-height: 200px; + border: none; + background-color: white; +} + +.example-error { + padding: 1em; + background-color: #ffebee; + color: #c62828; + font-family: 'Source Code Pro', monospace; + font-size: 0.85rem; +} diff --git a/tko.io/src/index.md b/tko.io/src/index.md new file mode 100644 index 000000000..36d66ec0d --- /dev/null +++ b/tko.io/src/index.md @@ -0,0 +1,16 @@ +--- +layout: base.njk +title: Introduction +--- + +# TKO + +Welcome to the TKO documentation. + +## Example + +```jsx +function Hello() { + return
Hello World
+} +``` diff --git a/tko.io/src/js/examples.js b/tko.io/src/js/examples.js new file mode 100644 index 000000000..9f49339aa --- /dev/null +++ b/tko.io/src/js/examples.js @@ -0,0 +1,116 @@ +import * as esbuild from 'https://cdn.jsdelivr.net/npm/esbuild-wasm@0.24.0/esm/browser.min.js'; + +let esbuildInitialized = false; + +async function initEsbuild() { + if (!esbuildInitialized) { + await esbuild.initialize({ + wasmURL: 'https://cdn.jsdelivr.net/npm/esbuild-wasm@0.24.0/esbuild.wasm' + }); + esbuildInitialized = true; + } +} + +async function transformJSX(code) { + try { + await initEsbuild(); + const result = await esbuild.transform(code, { + loader: 'jsx', + jsx: 'transform' + }); + return { success: true, code: result.code }; + } catch (error) { + return { success: false, error: error.message }; + } +} + +function createExampleContainer(codeBlock) { + const container = document.createElement('div'); + container.className = 'example-container'; + + const textarea = document.createElement('textarea'); + textarea.className = 'example-textarea'; + textarea.value = codeBlock.textContent.trim(); + + const iframe = document.createElement('iframe'); + iframe.className = 'example-result'; + + container.appendChild(textarea); + container.appendChild(iframe); + + // Replace the code block with our interactive container + const pre = codeBlock.parentElement; + pre.parentNode.replaceChild(container, pre); + + // Run the code initially + runExample(textarea, iframe); + + // Re-run on change (debounced) + let timeout; + textarea.addEventListener('input', () => { + clearTimeout(timeout); + timeout = setTimeout(() => runExample(textarea, iframe), 500); + }); +} + +async function runExample(textarea, iframe) { + const code = textarea.value; + const result = await transformJSX(code); + + if (result.success) { + // Create a complete HTML document for the iframe + const html = ` + + + + + + +
+ + + + `; + + // Write to iframe + const doc = iframe.contentDocument || iframe.contentWindow.document; + doc.open(); + doc.write(html); + doc.close(); + + // Remove any error display + const existingError = iframe.nextElementSibling; + if (existingError && existingError.className === 'example-error') { + existingError.remove(); + } + } else { + // Display error + let errorDiv = iframe.nextElementSibling; + if (!errorDiv || errorDiv.className !== 'example-error') { + errorDiv = document.createElement('div'); + errorDiv.className = 'example-error'; + iframe.parentNode.insertBefore(errorDiv, iframe.nextSibling); + } + errorDiv.textContent = `Error: ${result.error}`; + } +} + +// Initialize examples when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initExamples); +} else { + initExamples(); +} + +function initExamples() { + // Find all code blocks with language 'jsx' + const codeBlocks = document.querySelectorAll('pre code.language-jsx'); + codeBlocks.forEach(createExampleContainer); +} From 70c3a32d43e7641adc68f6c1a5276642bd2aaf00 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 11:31:19 -0500 Subject: [PATCH 03/12] docs: consolidate all documentation to tko.io MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove duplicate docs from individual packages - tko.io/src/docs/ is now single source of truth - Update CONTRIBUTING.md with documentation guidelines Deleted docs directories from: - packages/binding.core/docs - packages/binding.if/docs - packages/binding.template/docs - packages/observable/docs - packages/computed/docs - packages/utils.component/docs - packages/bind/docs - packages/lifecycle/docs - packages/provider/docs - packages/provider.databind/docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .gitignore | 1 + CONTRIBUTING.md | 33 + packages/binding.if/docs/_config.yml | 2 - tko.io/.eleventy.js | 50 ++ tko.io/src/3to4.md | 248 +++++++ tko.io/src/_layouts/base.njk | 33 +- tko.io/src/css/tko.css | 603 +++++++++++++----- .../src/docs/advanced}/databind-parser.md | 6 + .../src/docs/advanced}/life-cycle.md | 7 +- .../src/docs/advanced}/provider.md | 8 +- .../docs/binding-context}/binding-context.md | 5 + .../binding-context}/binding-preprocessing.md | 5 + .../docs/binding-context}/binding-syntax.md | 5 + ...indings-controlling-descendant-bindings.md | 5 + .../custom-bindings-disposal.md | 5 + .../custom-bindings-for-virtual-elements.md | 5 + .../docs/binding-context}/custom-bindings.md | 5 + .../src/docs/bindings}/attr-binding.md | 5 + .../src/docs/bindings}/checked-binding.md | 5 + .../src/docs/bindings}/click-binding.md | 5 + .../src/docs/bindings}/css-binding.md | 5 + .../src/docs/bindings}/disable-binding.md | 5 + .../src/docs/bindings}/enable-binding.md | 5 + .../src/docs/bindings}/event-binding.md | 5 + .../src/docs/bindings}/foreach-binding.md | 5 + .../src/docs/bindings}/hasfocus-binding.md | 5 + .../src/docs/bindings}/html-binding.md | 5 + .../src/docs/bindings}/if-binding.md | 5 + .../src/docs/bindings}/ifnot-binding.md | 5 + .../src/docs/bindings}/index.md | 5 + .../src/docs/bindings}/options-binding.md | 5 + .../docs/bindings}/selectedOptions-binding.md | 5 + .../src/docs/bindings}/style-binding.md | 5 + .../src/docs/bindings}/submit-binding.md | 5 + .../src/docs/bindings}/template-binding.md | 5 + .../src/docs/bindings}/text-binding.md | 5 + .../src/docs/bindings}/textInput-binding.md | 5 + .../src/docs/bindings}/uniqueName-binding.md | 5 + .../bindings}/unobtrusive-event-handling.md | 5 + .../src/docs/bindings}/value-binding.md | 5 + .../src/docs/bindings}/visible-binding.md | 5 + .../src/docs/bindings}/with-binding.md | 5 + .../src/docs/components}/component-binding.md | 5 + .../components}/component-custom-elements.md | 7 +- .../src/docs/components}/component-loaders.md | 9 +- .../docs/components}/component-overview.md | 5 + .../components}/component-registration.md | 5 + .../computed}/computed-dependency-tracking.md | 5 + .../src/docs/computed}/computed-pure.md | 7 +- .../src/docs/computed}/computed-reference.md | 5 + .../src/docs/computed}/computed-writable.md | 5 + .../src/docs/computed}/computedObservables.md | 5 + .../src/docs/computed}/proxy.md | 5 + .../src/docs/observables}/arraychange.md | 5 + .../src/docs/observables}/extenders.md | 5 + .../src/docs/observables}/observableArrays.md | 5 + .../src/docs/observables}/observables.md | 5 + .../docs/observables}/rateLimit-observable.md | 5 + .../docs/observables}/throttle-extender.md | 5 + tko.io/src/index.md | 85 ++- 60 files changed, 1147 insertions(+), 182 deletions(-) delete mode 100644 packages/binding.if/docs/_config.yml create mode 100644 tko.io/src/3to4.md rename {packages/provider.databind/docs => tko.io/src/docs/advanced}/databind-parser.md (99%) rename {packages/lifecycle/docs => tko.io/src/docs/advanced}/life-cycle.md (98%) rename {packages/provider/docs => tko.io/src/docs/advanced}/provider.md (83%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/binding-context.md (98%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/binding-preprocessing.md (99%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/binding-syntax.md (98%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/custom-bindings-controlling-descendant-bindings.md (98%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/custom-bindings-disposal.md (97%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/custom-bindings-for-virtual-elements.md (98%) rename {packages/bind/docs => tko.io/src/docs/binding-context}/custom-bindings.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/attr-binding.md (97%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/checked-binding.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/click-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/css-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/disable-binding.md (90%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/enable-binding.md (96%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/event-binding.md (99%) rename {packages/binding.template/docs => tko.io/src/docs/bindings}/foreach-binding.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/hasfocus-binding.md (97%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/html-binding.md (97%) rename {packages/binding.if/docs => tko.io/src/docs/bindings}/if-binding.md (98%) rename {packages/binding.if/docs => tko.io/src/docs/bindings}/ifnot-binding.md (94%) rename {packages/binding.if/docs => tko.io/src/docs/bindings}/index.md (68%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/options-binding.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/selectedOptions-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/style-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/submit-binding.md (98%) rename {packages/binding.template/docs => tko.io/src/docs/bindings}/template-binding.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/text-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/textInput-binding.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/uniqueName-binding.md (96%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/unobtrusive-event-handling.md (98%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/value-binding.md (99%) rename {packages/binding.core/docs => tko.io/src/docs/bindings}/visible-binding.md (97%) rename {packages/binding.if/docs => tko.io/src/docs/bindings}/with-binding.md (98%) rename {packages/utils.component/docs => tko.io/src/docs/components}/component-binding.md (99%) rename {packages/utils.component/docs => tko.io/src/docs/components}/component-custom-elements.md (99%) rename {packages/utils.component/docs => tko.io/src/docs/components}/component-loaders.md (98%) rename {packages/utils.component/docs => tko.io/src/docs/components}/component-overview.md (98%) rename {packages/utils.component/docs => tko.io/src/docs/components}/component-registration.md (99%) rename {packages/computed/docs => tko.io/src/docs/computed}/computed-dependency-tracking.md (98%) rename {packages/computed/docs => tko.io/src/docs/computed}/computed-pure.md (97%) rename {packages/computed/docs => tko.io/src/docs/computed}/computed-reference.md (99%) rename {packages/computed/docs => tko.io/src/docs/computed}/computed-writable.md (98%) rename {packages/computed/docs => tko.io/src/docs/computed}/computedObservables.md (99%) rename {packages/computed/docs => tko.io/src/docs/computed}/proxy.md (96%) rename {packages/observable/docs => tko.io/src/docs/observables}/arraychange.md (96%) rename {packages/observable/docs => tko.io/src/docs/observables}/extenders.md (98%) rename {packages/observable/docs => tko.io/src/docs/observables}/observableArrays.md (99%) rename {packages/observable/docs => tko.io/src/docs/observables}/observables.md (99%) rename {packages/observable/docs => tko.io/src/docs/observables}/rateLimit-observable.md (99%) rename {packages/observable/docs => tko.io/src/docs/observables}/throttle-extender.md (98%) diff --git a/.gitignore b/.gitignore index 1163adbb0..0f9690c97 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ coverage/ .vscode/extensions.json !.vscode/launch.json .vscode/settings.json +.playwright-mcp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f36b6d96..24b324839 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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: diff --git a/packages/binding.if/docs/_config.yml b/packages/binding.if/docs/_config.yml deleted file mode 100644 index 99273f014..000000000 --- a/packages/binding.if/docs/_config.yml +++ /dev/null @@ -1,2 +0,0 @@ -# theme: jekyll-theme-slate - diff --git a/tko.io/.eleventy.js b/tko.io/.eleventy.js index a6d9650ca..61b29d989 100644 --- a/tko.io/.eleventy.js +++ b/tko.io/.eleventy.js @@ -1,6 +1,11 @@ import markdownIt from "markdown-it"; export default function(eleventyConfig) { + // Follow symlinks + eleventyConfig.setServerOptions({ + followSymlinks: true + }); + // Copy static assets eleventyConfig.addPassthroughCopy("src/css"); eleventyConfig.addPassthroughCopy("src/js"); @@ -19,6 +24,51 @@ export default function(eleventyConfig) { 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(' '); + }); + return { dir: { input: "src", diff --git a/tko.io/src/3to4.md b/tko.io/src/3to4.md new file mode 100644 index 000000000..a1fff9fc6 --- /dev/null +++ b/tko.io/src/3to4.md @@ -0,0 +1,248 @@ +--- +layout: base.njk +title: Knockout 3 to 4 Guide +--- + +# Overview of the Differences + +TKO is a fundamental change in the foundation of the code behind Knockout, representing the reverse engineering, compartmentalization, and reintegration of the original code that dates back to 2010. + +With this reengineering of Knockout into discrete pieces, it will be easier to update, build, and test the features of Knockout, as well as put the individual packages that make up Knockout to use in situations outside Knockout itself. + +To see the differences, have a look at the [CHANGELOG.md](https://github.com/knockout/tko/blob/master/CHANGELOG.md). + + +# Version Comparison + +| Version | tko.js | Knockout 4 | Knockout 3.x | +| --- | --- | --- | --- | +| Browser compatibility | Modern browsers (i.e. [`Proxy`](https://caniuse.com/#feat=proxy) support) | Internet Explorer 9+ | Internet Explorer 6+ | +| `ko.BindingHandler` | Yes | Yes | No | +| `observableArray.length` | Yes | Browsers with [Array subclass](https://kangax.github.io/compat-table/es6/#test-Array_is_subclassable_length_property_(accessing)) support [More info](http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/#wrappers_prototype_chain_injection) | No | +| iterable `observableArray` | Yes | Browsers with [Symbol support](https://kangax.github.io/compat-table/es6/#test-Symbol) | No | +| event `throttle` / `debounce` | Yes | Yes | No | +| `ko.Component` | Yes | Yes | No | +| `applyBindings` returns `Promise` | Yes | Yes | No | +| Polyfills included | None | Up to ES6 | `Object.prototype.bind` | +| `ko.proxy` | Yes | No | No | +| `ko.onError` | Does not automatically throw (you need to add `throw error` to `ko.options.onError`) | Does not rethrow automatically | Always re-throws error | +| `foreach` binding | FastForEach | Template foreach | Template foreach | +| `each` binding | FastForEach | FastForEach | Template foreach + + +# Build System + +TKO has a complete build system rewrite and a conversion of the code from a concatenation of source files to discrete ES6 modules with `import` and `export` statements. This process has been complex and taken years, with the result being a Knockout with a strong foundation of discrete reusable components. + +Knockout 4 and TKO will be maintained at the new monorepo [knockout/tko](https://github.com/knockout/tko) on GitHub. Knockout 3.x will continue to be maintained at [knockout/knockout](https://github.com/knockout/knockout). + +TKO now uses standard tools like `lerna` and `yarn`, and employs [StandardJS](https://standardjs.com/) for code style — though all the old code is not yet updated to StandardJS yet. + +# Test System + +Knockout 3 used Jasmine 1 to perform tests. Some packages in TKO continue to use Jasmine 1, while others now use Mocha. + +## Backwards compatibility and 💥 breaking changes + +We've strived to have backwards compatibility, and the numerous test cases we've run it through have been successful. A key value of Knockout has been and remains the comprehensive test cases that enforce performance to a predictable standard, and TKO meets all the tests that were enforced on Knockout 3.x, notwithstanding a few issues: + +- The `function` no longer works in `data-bind`, having been replaced by lambdas, so `data-bind='click: function () { x++ }'` will have to be replaced by e.g. `data-bind='click: => x++'` +- The `data-bind` is now interpreted by a LL compiler (based on [knockout-secure-binding](https://github.com/brianmhunt/knockout-secure-binding)), so some edge case parsing issues may crop up + +## Browser support + +Knockout 4 and TKO and the underlying packages will start support from IE9 forward. We aim to test Knockout across all browsers from this point forward. + +If you need IE6, 7, and 8 support you may prefer to keep using the Knockout 3.x line, or be prepared for workarounds or missing functionality. + +# Options + +* (options) Allow importing `ko` in node +* various new [`options`](https://github.com/knockout/tko.utils/blob/master/src/options.js) + +# Utilities + +* (utils) Several array utilities use native functions now (`arrayPushAll`, `arrayFilter`, `arrayGetDistinctValues`, `arrayFirst`, `arrayIndexOf`) +* The `throttle` and `debounce` utilities now pass arguments to the target functions +* utils + * utils.domNodeDisposal is now exposed as domNodeDisposal + * arguments to setHtml that are functions are called (not just observables) + * cleanExternalData now exposed in domNodeDisposal.otherNodeCleanerFunctions + +* error handling + * onError no longer throws if overloaded; default function is to re-throw. + * error is thrown when an extender is not found + +# Life Cycles + +* (internal) Add the ES6 LifeCycle class (see tko.lifecycle) + +# Observables + +* (observable) When supported, `observable.length` will now be undefined (was `0` before), and `observableArray.length` will now be the length of the wrapped array +* (observableArray) `observableArray` is now iterable (has a `Symbol.iterator` property) +* (subscribable) Add the `once`, `then`, `when`, `yet`, and `next` functions + +# Components + +* (components) Warn with custom-element names that cannot be used with custom elements re. #43 & knockout/knockout#1603 +* (components) Add `ko.Component`, an abstract base class that simplifies the Component creation and registration API (see `tko.utils.component/src/ComponentABC.js`) +* (components) Add `getBindingHandler(key)` to use binding handlers local to a component + +Components can control their binding handlers. + +## ko.Component example + +

See the Pen Commented ko.Component by Knockout (@Knockout) on CodePen.

+ +# Bindings + +* (binding) `ko.applyBindings` now returns a Promise that resolves when bindings are completed + +* (binding handlers) Add new-style ES6 Binding Handler class (see custom-bindings documentation and tko.bind/src/BindingHandler.js), descended from the LifeCycle class +* (bind) String errors on binding are now propagated + +## Binding Handlers and ko.BindingHandler + +In TKO, binding handlers can be either an `object`, a `function` or a descendant of `ko.BindingHandler`. The following are all valid binding handlers: + +```javascript +class NewHandler extends ko.BindingHandler { + constructor ({$element, valueAccessor, allBindings, $context, onError}) { + /* + this.value is valueAccessor() + this.value(x) is valueAccessor(x); + if valueAccessor() is observable or a property, it will be set properly. + */ + } + + get controlsDescendants () { return true|false } + + get bindingComplete () { + /* overload where binding is asynchronous. See e.g. Component binding*/ + } + static get allowVirtualElements () { return true|false } +} + +handler_fn = (element, valueAccessor, allBindings, viewModel, bindingContext) => { /* ... */ } + +the_knockout_3_way = { + init (element, valueAccessor, allBindings, viewModel, bindingContext) { + /* ... */ + }, + + update (element, valueAccessor, allBindings, viewModel, bindingContext) { + /* ... */ + }, + + allowVirtualElements: true|false +} + +// Virtual Elements may be permitted for a binding explicitly. +virtualElements.allowedBindings[handler_name] = true|false +``` + +Binding handlers can be registered in the following ways: + +```javascript +// Globally, where NewHandler is a BindingHandler descendant. +NewHandler.registerAs('handler') + +// Globally, for any handler type. +ko.bindingHandlers.set(handler_name: handler) + +// Knockout 3.x style +ko.bindingHandlers[handler_name] = handler + +// For a component +class Component { + getBindingHandler(bindingKey) { + if (bindingKey === handler_name) { return handler } + // Alternatively: + // return { bindingKey[bindingKey]: handler }[bindingKey] + } +} +``` + +## Parsing + +* (parser) Fix interpretation of unicode characters as identifiers / variables +* `==` and `===` use `===` for comparison (same for `!=` and `!==`); fuzzy equality ~== / ~!= for the evil twins +* Knockout 4 will work with a Content Security Policy that prohibits unsafe-eval. + * add "naked" `=>` lambdas (even in legacy browsers e.g. `data-bind='click: => was_clicked(true)'` + * inline functions are no longer supported (e.g. `data-bind='click: function (){...}'` will fail) + * No longer uses `with` statements + * No longer uses `eval`/`new Function` + +## Namespacing + + * incorporate punches namespacing i.e. `data-bind='event.click: => thing(true)'` is equivalent to `data-bind='event: {click: => thing(true)}'` + +## Attribute Interpolation + + +## Text Interpolation + +* incorporate punches {% raw %}`{{ }}`{% endraw %} and {% raw %}`{{{}}}` {% endraw %}text and attribute interpolation + + +## Event +* (`event` binding) Add object-based event handler e.g. `event.click: { handler: fn, once: true, capture: true, bubble: false, passive: false}`. Also, bubbling can be prevented with `event.click: {bubble: false }` re #32 +* (`event` binding) Add `throttle` and `debounce` parameters + +## With +* (with binding) Fix dependency count for function-arguments [knockout/knockout#2285] + +## Attr +* (attr) Support namespaced attributes with `attr` binding #27 + +## Foreach + +* Use tko.binding.foreach for the `foreach` binding (based on brianmhunt/knockout-fast-foreach) +* Add `each` as an alias of `foreach` +* Rewritten as O(1) +* (foreach binding) When using the `as` parameter, the `$data` remains unchanged (i.e. the context inside a `foreach` is no longer a "child" context, but an extension of the current context); this deprecates the `noContext` parameter +* (foreach binding) Expose the `conditional` on the `domData` for use by the `else` binding (when the array is empty, the `else` binding will be rendered) +* (foreach binding) Expose `$list` inside the foreach +* (foreach binding) Allow `noIndex` as a peer binding parameter (e.g. `foreach: items, noIndex: true`) +* (foreach) Preserve focus when items are deleted and re-added (i.e. moved) in the same animation frame. + +## Template + +* Make the `template` binding expose a conditional for else-binding +* support template literals (\`\`) in bindings (even in legacy browsers) +* add the `@` prefix operator that calls/unwrap functions (i.e. `obs()()` is the same as `@obs`) + +## If / Else + +* add `` inside the `if` binding, and add an `else` binding (following the brianmhunt/knockout-else plugin) +* Add `else` binding +* Add `unless` binding + +## Using + +## Hidden + + * add `hidden` binding (knockout/knockut#2103) + +## HTML + +The HTML binding now works in virtual elements. Based on ko.punches. + +## Filtering + +Filters can be applied to modify values in bindings. Based on ko.punches. + + +# Deprecated + + - Template binding options are deprecated + - expressionWriting (twoWayBinding) + - ‘.’ in binding handler names + - jsonExpressionRewriting (expressionRewriting) + - form parsing + - `bind` shim + - ko.utils.parseJson + - getFormFields + - fieldsIncludedWithJsonPost + - postJson diff --git a/tko.io/src/_layouts/base.njk b/tko.io/src/_layouts/base.njk index 766411cb0..28d32ac3a 100644 --- a/tko.io/src/_layouts/base.njk +++ b/tko.io/src/_layouts/base.njk @@ -16,7 +16,38 @@ diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css index 5f9799f27..8927959d9 100644 --- a/tko.io/src/css/tko.css +++ b/tko.io/src/css/tko.css @@ -1,325 +1,596 @@ -@import url('https://fonts.googleapis.com/css?family=Nunito|Pacifico|Source+Code+Pro'); +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap'); + +/* CSS Reset and Base Styles */ +* { + box-sizing: border-box; +} + +html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} html, body { - /* Prevent scroll on narrow devices */ + margin: 0; + padding: 0; overflow-x: hidden; - font-family: 'Nunito', sans-serif; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; + font-size: 16px; + line-height: 1.6; + color: #1a1a1a; + background-color: #ffffff; } body { display: grid; - margin-top: 75px; - height: calc(100vh - 75px); - - grid-template-columns: minmax(200px, max-content) auto; - grid-template-areas: - 'sidebar main'; + margin-top: 64px; + height: calc(100vh - 64px); + grid-template-columns: 280px 1fr; + grid-template-areas: 'sidebar main'; } @media screen and (max-width: 991px) { body { - grid-template-columns: auto; + grid-template-columns: 1fr; grid-template-areas: 'sidebar' 'main'; + height: auto; } } -a { - color: #590c12; - transition: 0.1s ease-in-out; +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.25; + margin-top: 1.5em; + margin-bottom: 0.5em; + color: #111827; } -a:focus, a:hover, a:active { - color: #8e383c; - cursor: pointer; +h1 { + font-size: 2.25rem; + font-weight: 700; + margin-top: 0; + margin-bottom: 1rem; + letter-spacing: -0.025em; +} + +h2 { + font-size: 1.875rem; + margin-top: 2em; + padding-bottom: 0.3em; + border-bottom: 1px solid #e5e7eb; +} + +h3 { + font-size: 1.5rem; + margin-top: 1.6em; +} + +h4 { + font-size: 1.25rem; +} + +p { + margin: 1em 0; + line-height: 1.7; +} + +/* Links */ +a { + color: #2563eb; text-decoration: none; + transition: color 0.15s ease; +} + +a:hover { + color: #1d4ed8; +} + +a:focus { + outline: 2px solid #3b82f6; + outline-offset: 2px; + border-radius: 2px; } main a { - border-bottom: 1px dashed #590c12; + text-decoration: underline; + text-decoration-color: #93c5fd; + text-underline-offset: 2px; } -td { - padding: 5px 1em; - border-bottom: 1px solid gray; +main a:hover { + text-decoration-color: #2563eb; +} + +/* Tables */ +table { + width: 100%; + border-collapse: collapse; + margin: 1.5em 0; + font-size: 0.9375rem; } th { - padding: 5px 1em; - border-bottom: 1px solid black; + padding: 0.75rem 1rem; + text-align: left; + font-weight: 600; + background-color: #f9fafb; + border-bottom: 2px solid #e5e7eb; + color: #374151; } +td { + padding: 0.75rem 1rem; + border-bottom: 1px solid #e5e7eb; +} -/* Codepen iFrames */ -iframe { - padding: 0px 1em; +tr:hover { + background-color: #f9fafb; } +/* Lists */ +ul, ol { + margin: 1em 0; + padding-left: 1.5em; +} + +li { + margin: 0.5em 0; + line-height: 1.7; +} + +/* Blockquotes */ +blockquote { + margin: 1.5em 0; + padding: 1em 1.5em; + border-left: 4px solid #3b82f6; + background-color: #eff6ff; + color: #1e40af; +} /* - * TOP - BAR + * TOP BAR */ .top-bar { width: 100%; - display: grid; - grid-template-columns: auto; - grid-auto-flow: column; - justify-content: center; - grid-column-gap: 24px; - + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + padding: 0 2rem; top: 0; left: 0; position: fixed; z-index: 1200; - padding: 12px; + height: 64px; + background-color: #ffffff; + border-bottom: 1px solid #e5e7eb; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); +} - background-color: #931d0d; - font-family: Pacifico; +.top-bar .tko { + font-size: 1.5rem; + font-weight: 700; + color: #2563eb; + letter-spacing: -0.025em; } .top-bar a { - border-radius: 12px; - text-align: center; - padding: 0.25em 0.4em; - color: white; - font-size: 1.4rem; - text-shadow: 0 0 5px black; - line-height: 2.4rem; - transition: 0.4s background-color ease-in-out; + padding: 0.5rem 1rem; + color: #4b5563; + font-size: 0.9375rem; + font-weight: 500; + border-radius: 6px; + transition: all 0.15s ease; } .top-bar a:hover { - background-color: #00000080; - text-shadow: 0 0 5px #931d0d, 0 0 10px #931d0d; - transition: 0.4s background-color ease-in-out; + color: #2563eb; + background-color: #eff6ff; } -.top-bar .tko { - color: white; - font-size: 1.6rem; - padding: 0.25em 0.4em; - text-shadow: 0 0 5px #efff6c, 0 0 10px #efff6c; +.top-bar a.external::after { + content: '↗'; + margin-left: 0.25rem; + font-size: 0.875rem; } .current-page { - background-color: #00000040; + color: #2563eb !important; + background-color: #eff6ff; +} + +@media screen and (max-width: 768px) { + .top-bar { + padding: 0 1rem; + gap: 0.5rem; + } + + .top-bar a { + padding: 0.5rem; + font-size: 0.875rem; + } } /** - * SIDE-BAR + * SIDEBAR */ .sidebar { grid-area: sidebar; - - height: calc(100vh - 75px); - /* Scrollable contents if viewport is shorter than content */ + height: calc(100vh - 64px); overflow-y: auto; overflow-x: hidden; - left: 0; - bottom: 0; - z-index: 1000; - padding: 0; - background-color: #efebe9; - border-right: 1px solid #efebe9; + background-color: #f9fafb; + border-right: 1px solid #e5e7eb; + padding: 2rem 0; +} + +/* Custom scrollbar for sidebar */ +.sidebar::-webkit-scrollbar { + width: 6px; +} + +.sidebar::-webkit-scrollbar-track { + background: transparent; +} + +.sidebar::-webkit-scrollbar-thumb { + background-color: #d1d5db; + border-radius: 3px; +} - padding: 4px 0.75em; +.sidebar::-webkit-scrollbar-thumb:hover { + background-color: #9ca3af; } .toc { - position: sticky; + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0 1rem; } .sidebar a { - padding: 1ex 0.25rem; - color: #931d0d; - border-radius: 4px; + display: block; + padding: 0.5rem 0.75rem; + color: #4b5563; + font-size: 0.875rem; + border-radius: 6px; + transition: all 0.15s ease; + text-decoration: none; } .sidebar a:hover { - color: black; - background-color: #FFaFaFB0; - transition: 0.4s background-color ease-in-out; + color: #111827; + background-color: #e5e7eb; } .sidebar .section { - font-size: 1.2rem; + font-size: 0.875rem; + font-weight: 600; + color: #111827; + padding: 0.75rem 0.75rem 0.5rem; + margin-top: 1.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; + font-size: 0.75rem; } -.sidebar .subheading { - padding: 0.6ex 8px; - font-weight: normal; - margin-left: 1rem; +.sidebar .section:first-child { + margin-top: 0; } - @keyframes popin { - from { transform: translateX(50%); } - to { transform: translateX(0%); } - } +.sidebar .subheading { + padding-left: 1.5rem; + font-weight: 400; + position: relative; +} -.sidebar .in-viewport::after { +.sidebar .subheading::before { + content: ''; position: absolute; - right: -22px; - background-color: #8F3F4F90; - width: 20px; - height: 20px; - border-radius: 12px; - content: ' '; - animation: 0.25s ease-in 0s popin; + left: 0.75rem; + top: 50%; + transform: translateY(-50%); + width: 2px; + height: 60%; + background-color: transparent; + transition: background-color 0.15s ease; } -/** - * MAIN - */ -main { - grid-area: main; - height: calc(100vh - 75px); - background-color: #fcd4d4; - padding: 16px; - overflow: scroll; +.sidebar .subheading:hover::before { + background-color: #3b82f6; } -h2, h3, h4 { - margin-top: 1.3em; - margin-bottom: 0.6em; +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(-4px); + } + to { + opacity: 1; + transform: translateX(0); + } } -h2 { - font-weight: 800; +.sidebar .in-viewport { + color: #2563eb; + background-color: #eff6ff; + font-weight: 500; } -h3, h4, h2 time { - font-weight: 400; +.sidebar .in-viewport::before { + background-color: #2563eb; } -h1 { - margin-top: 1.5em; +@media screen and (max-width: 991px) { + .sidebar { + height: auto; + max-height: 400px; + border-right: none; + border-bottom: 1px solid #e5e7eb; + } +} + +/** + * MAIN CONTENT + */ +main { + grid-area: main; + height: calc(100vh - 64px); + background-color: #ffffff; + padding: 3rem; + overflow-y: auto; + overflow-x: hidden; +} + +main > *:first-child { + margin-top: 0; +} + +/* Improved readability for main content */ +main { + max-width: 1000px; + margin-left: auto; + margin-right: auto; } h1 code { - background-color: transparent; - border: 3px solid #E8D8D8; + background-color: #eff6ff; + border: 2px solid #3b82f6; font-size: inherit; + padding: 0.1em 0.3em; + border-radius: 6px; + font-weight: 700; +} + +@media screen and (max-width: 991px) { + main { + padding: 2rem 1.5rem; + height: auto; + } } /** - * CODE + * CODE BLOCKS */ +code { + font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace; + font-size: 0.875em; + font-weight: 400; +} + +/* Inline code */ +:not(pre) > code { + background-color: #f3f4f6; + color: #dc2626; + padding: 0.2em 0.4em; + border-radius: 4px; + font-size: 0.875em; + white-space: nowrap; +} + +/* Code blocks */ pre { - font-family: 'Source Code Pro', monospace; - background-color: #0a0909; - padding: 1ex 1em; - margin-left: 1em; - border-radius: 1px; - border: 1px black inset; + background-color: #1e293b; + color: #e2e8f0; + padding: 1.25rem 1.5rem; + margin: 1.5em 0; + border-radius: 8px; + overflow-x: auto; + line-height: 1.6; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); } -code { - background-color: #080707; - color: #e6c07b; - font-size: 0.9rem; +pre code { + background-color: transparent; + color: #e2e8f0; + padding: 0; + border-radius: 0; + white-space: pre; + font-size: 0.875rem; + line-height: 1.6; } -main code { - font-family: 'Source Code Pro', monospace; +/* Syntax highlighting hints */ +pre code .keyword { + color: #c792ea; } -code, td code { - white-space: nowrap; - padding: 2px 8px; +pre code .string { + color: #c3e88d; } -pre code { - color: #e6c07b; - white-space: pre; /* We want newlines to be newlines in code blocks */ +pre code .comment { + color: #636e7b; + font-style: italic; } -/** - * - * TITLES - * - */ +/* Custom scrollbar for code blocks */ +pre::-webkit-scrollbar { + height: 8px; +} -.section-title { - font-size: 3rem; - font-weight: bold; +pre::-webkit-scrollbar-track { + background: #0f172a; + border-radius: 4px; } +pre::-webkit-scrollbar-thumb { + background-color: #475569; + border-radius: 4px; +} +pre::-webkit-scrollbar-thumb:hover { + background-color: #64748b; +} +/** + * UTILITY CLASSES + */ +.section-title { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 1rem; + color: #111827; +} /** - * Source linking + * Source Linking */ .source { display: block; text-align: right; - font-size: 0.9rem; + font-size: 0.875rem; position: sticky; - top: 0px; + top: 0; + margin-bottom: 1rem; } .source a { - padding: 2px 0.62em; - background-color: #E8D8D8; - border: 1px solid gray; + display: inline-block; + padding: 0.5rem 1rem; + background-color: #f3f4f6; + color: #4b5563; + border: 1px solid #d1d5db; + border-radius: 6px; + transition: all 0.15s ease; +} + +.source a:hover { + background-color: #e5e7eb; + border-color: #9ca3af; } /** - * - * BOOKS - * + * BOOK GRID */ .book-grid { display: grid; - grid-template-columns: repeat(auto-fit, 250px); - grid-gap: 10px; - justify-content: space-around; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; + margin: 2rem 0; } .book-grid .card { - width: 250px; + background: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 8px; + padding: 1.5rem; + transition: all 0.2s ease; +} + +.book-grid .card:hover { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + transform: translateY(-2px); } /** - * * INTERACTIVE EXAMPLES - * */ .example-container { - margin: 2em 0; - border: 1px solid #931d0d; - border-radius: 4px; + margin: 2rem 0; + border: 1px solid #e5e7eb; + border-radius: 8px; overflow: hidden; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); } .example-textarea { width: 100%; - min-height: 200px; - padding: 1em; - font-family: 'Source Code Pro', monospace; - font-size: 0.9rem; - background-color: #0a0909; - color: #e6c07b; + min-height: 250px; + padding: 1.25rem 1.5rem; + font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', 'Inconsolata', monospace; + font-size: 0.875rem; + line-height: 1.6; + background-color: #1e293b; + color: #e2e8f0; border: none; - border-bottom: 2px solid #931d0d; + border-bottom: 2px solid #3b82f6; resize: vertical; + outline: none; +} + +.example-textarea:focus { + border-bottom-color: #2563eb; } .example-result { width: 100%; - min-height: 200px; + min-height: 250px; border: none; - background-color: white; + background-color: #ffffff; } .example-error { - padding: 1em; - background-color: #ffebee; - color: #c62828; - font-family: 'Source Code Pro', monospace; - font-size: 0.85rem; + padding: 1rem 1.5rem; + background-color: #fef2f2; + color: #991b1b; + border-top: 2px solid #dc2626; + font-family: 'JetBrains Mono', monospace; + font-size: 0.875rem; + line-height: 1.5; +} + +/** + * ACCESSIBILITY & FOCUS STATES + */ +*:focus-visible { + outline: 2px solid #3b82f6; + outline-offset: 2px; +} + +/** + * PRINT STYLES + */ +@media print { + .top-bar, + .sidebar { + display: none; + } + + main { + max-width: 100%; + padding: 1rem; + } + + body { + grid-template-columns: 1fr; + margin-top: 0; + } + + a { + text-decoration: underline; + } + + a[href^="http"]::after { + content: " (" attr(href) ")"; + font-size: 0.875em; + color: #6b7280; + } } diff --git a/packages/provider.databind/docs/databind-parser.md b/tko.io/src/docs/advanced/databind-parser.md similarity index 99% rename from packages/provider.databind/docs/databind-parser.md rename to tko.io/src/docs/advanced/databind-parser.md index db9df61cd..17eb88a27 100644 --- a/packages/provider.databind/docs/databind-parser.md +++ b/tko.io/src/docs/advanced/databind-parser.md @@ -1,3 +1,9 @@ +--- +layout: base.njk +title: Databind Parser +--- + +# Databind Parser tko.provider is a binding provider for [Knockout](http://knockoutjs.com), namely it parses HTML attributes and converts them to handlers of bidirectional updates. diff --git a/packages/lifecycle/docs/life-cycle.md b/tko.io/src/docs/advanced/life-cycle.md similarity index 98% rename from packages/lifecycle/docs/life-cycle.md rename to tko.io/src/docs/advanced/life-cycle.md index 565e95a2e..7cdecab87 100644 --- a/packages/lifecycle/docs/life-cycle.md +++ b/tko.io/src/docs/advanced/life-cycle.md @@ -1,4 +1,9 @@ -# tko.lifecycle +--- +layout: base.njk +title: Lifecycle +--- + +# Lifecycle The `tko.lifecycle` package exports one class, `LifeCycle`, which is intended to make tracking and disposing of temporary computations, subscriptions, and DOM event handlers simpler. diff --git a/packages/provider/docs/provider.md b/tko.io/src/docs/advanced/provider.md similarity index 83% rename from packages/provider/docs/provider.md rename to tko.io/src/docs/advanced/provider.md index adcb20017..4f5e8591e 100644 --- a/packages/provider/docs/provider.md +++ b/tko.io/src/docs/advanced/provider.md @@ -1,3 +1,7 @@ +--- +layout: base.njk +title: Provider +--- # Provider @@ -9,8 +13,8 @@ The `Provider` is a class that is responsible for linking nodes to binding handl | `VirtualProvider` | `` | `ComponentProvider` | `` | `AttributeProvider` | `` -| `TextMustacheProvider` | `{{ x_text }}`
`{{{ x_html }}}`
`{{# text: thing /}}` -| `AttributeMustacheProvider` | `` +| `TextMustacheProvider` | {% raw %}`{{ x_text }}`{% endraw %}
{% raw %}`{{{ x_html }}}`{% endraw %}
{% raw %}`{{# text: thing /}}`{% endraw %} +| `AttributeMustacheProvider` | {% raw %}``{% endraw %} | `MultiProvider` | Combines more than one provider together. When building custom versions of tko, one can choose to use one or more binding providers, or to create a custom binding provider. diff --git a/packages/bind/docs/binding-context.md b/tko.io/src/docs/binding-context/binding-context.md similarity index 98% rename from packages/bind/docs/binding-context.md rename to tko.io/src/docs/binding-context/binding-context.md index 14c342c05..d45055ab7 100644 --- a/packages/bind/docs/binding-context.md +++ b/tko.io/src/docs/binding-context/binding-context.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Binding Context +--- + # Binding Context diff --git a/packages/bind/docs/binding-preprocessing.md b/tko.io/src/docs/binding-context/binding-preprocessing.md similarity index 99% rename from packages/bind/docs/binding-preprocessing.md rename to tko.io/src/docs/binding-context/binding-preprocessing.md index bf6392858..f85f7f60f 100644 --- a/packages/bind/docs/binding-preprocessing.md +++ b/tko.io/src/docs/binding-context/binding-preprocessing.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Binding Preprocessing +--- + # Binding Preprocessing diff --git a/packages/bind/docs/binding-syntax.md b/tko.io/src/docs/binding-context/binding-syntax.md similarity index 98% rename from packages/bind/docs/binding-syntax.md rename to tko.io/src/docs/binding-context/binding-syntax.md index 192c19360..e0e3df8ff 100644 --- a/packages/bind/docs/binding-syntax.md +++ b/tko.io/src/docs/binding-context/binding-syntax.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Binding Syntax +--- + # The `data-bind` Syntax diff --git a/packages/bind/docs/custom-bindings-controlling-descendant-bindings.md b/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md similarity index 98% rename from packages/bind/docs/custom-bindings-controlling-descendant-bindings.md rename to tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md index c1187ae7f..bc6ff5ca9 100644 --- a/packages/bind/docs/custom-bindings-controlling-descendant-bindings.md +++ b/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Custom Bindings Controlling Descendant Bindings +--- + # Controlling Descendant Bindnings *Note: Creating custom bindings that control descendant binding is an advanced technique, typically used only when creating libraries of reusable bindings. It's not something you'll normally need to do when building applications with Knockout.* diff --git a/packages/bind/docs/custom-bindings-disposal.md b/tko.io/src/docs/binding-context/custom-bindings-disposal.md similarity index 97% rename from packages/bind/docs/custom-bindings-disposal.md rename to tko.io/src/docs/binding-context/custom-bindings-disposal.md index fdcc7e607..300b9aac3 100644 --- a/packages/bind/docs/custom-bindings-disposal.md +++ b/tko.io/src/docs/binding-context/custom-bindings-disposal.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Custom Bindings Disposal +--- + # Custom Disposal Logic In a typical Knockout application, DOM elements are dynamically added and removed, for example using the [`template`](template-binding.html) binding or via control-flow bindings ([`if`](if-binding.html), [`ifnot`](ifnot-binding.html), [`with`](with-binding.html), and [`foreach`](foreach-binding.html)). When creating a custom binding, it is often desirable to add clean-up logic that runs when an element associated with your custom binding is removed by Knockout. diff --git a/packages/bind/docs/custom-bindings-for-virtual-elements.md b/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md similarity index 98% rename from packages/bind/docs/custom-bindings-for-virtual-elements.md rename to tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md index 0a6fbdf26..105e0dab8 100644 --- a/packages/bind/docs/custom-bindings-for-virtual-elements.md +++ b/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Custom Bindings For Virtual Elements +--- + # Custom Bindings and Virtual Elements diff --git a/packages/bind/docs/custom-bindings.md b/tko.io/src/docs/binding-context/custom-bindings.md similarity index 99% rename from packages/bind/docs/custom-bindings.md rename to tko.io/src/docs/binding-context/custom-bindings.md index a9d78c7d4..8de3fcb04 100644 --- a/packages/bind/docs/custom-bindings.md +++ b/tko.io/src/docs/binding-context/custom-bindings.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Custom Bindings +--- + # Creating Custom Bindings diff --git a/packages/binding.core/docs/attr-binding.md b/tko.io/src/docs/bindings/attr-binding.md similarity index 97% rename from packages/binding.core/docs/attr-binding.md rename to tko.io/src/docs/bindings/attr-binding.md index 4254ddbdd..ff391b3f2 100644 --- a/packages/binding.core/docs/attr-binding.md +++ b/tko.io/src/docs/bindings/attr-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Attr Binding +--- + # `attr` ### Purpose diff --git a/packages/binding.core/docs/checked-binding.md b/tko.io/src/docs/bindings/checked-binding.md similarity index 99% rename from packages/binding.core/docs/checked-binding.md rename to tko.io/src/docs/bindings/checked-binding.md index fb4b32228..329c7eddb 100644 --- a/packages/binding.core/docs/checked-binding.md +++ b/tko.io/src/docs/bindings/checked-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Checked Binding +--- + # `checked` diff --git a/packages/binding.core/docs/click-binding.md b/tko.io/src/docs/bindings/click-binding.md similarity index 98% rename from packages/binding.core/docs/click-binding.md rename to tko.io/src/docs/bindings/click-binding.md index d7159a2b2..30bae7bd0 100644 --- a/packages/binding.core/docs/click-binding.md +++ b/tko.io/src/docs/bindings/click-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Click Binding +--- + # `click` ### Purpose diff --git a/packages/binding.core/docs/css-binding.md b/tko.io/src/docs/bindings/css-binding.md similarity index 98% rename from packages/binding.core/docs/css-binding.md rename to tko.io/src/docs/bindings/css-binding.md index 38c945376..fbd809b50 100644 --- a/packages/binding.core/docs/css-binding.md +++ b/tko.io/src/docs/bindings/css-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Css Binding +--- + # `css` ### Purpose diff --git a/packages/binding.core/docs/disable-binding.md b/tko.io/src/docs/bindings/disable-binding.md similarity index 90% rename from packages/binding.core/docs/disable-binding.md rename to tko.io/src/docs/bindings/disable-binding.md index 791cf2539..a1d48dc19 100644 --- a/packages/binding.core/docs/disable-binding.md +++ b/tko.io/src/docs/bindings/disable-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Disable Binding +--- + # `disable` ### Purpose diff --git a/packages/binding.core/docs/enable-binding.md b/tko.io/src/docs/bindings/enable-binding.md similarity index 96% rename from packages/binding.core/docs/enable-binding.md rename to tko.io/src/docs/bindings/enable-binding.md index 4c0ca9c03..126ae3ed7 100644 --- a/packages/binding.core/docs/enable-binding.md +++ b/tko.io/src/docs/bindings/enable-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Enable Binding +--- + # `enable` diff --git a/packages/binding.core/docs/event-binding.md b/tko.io/src/docs/bindings/event-binding.md similarity index 99% rename from packages/binding.core/docs/event-binding.md rename to tko.io/src/docs/bindings/event-binding.md index 99634db65..5aa221a0c 100644 --- a/packages/binding.core/docs/event-binding.md +++ b/tko.io/src/docs/bindings/event-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Event Binding +--- + # `event` ### Purpose diff --git a/packages/binding.template/docs/foreach-binding.md b/tko.io/src/docs/bindings/foreach-binding.md similarity index 99% rename from packages/binding.template/docs/foreach-binding.md rename to tko.io/src/docs/bindings/foreach-binding.md index db773c014..60df74b4c 100644 --- a/packages/binding.template/docs/foreach-binding.md +++ b/tko.io/src/docs/bindings/foreach-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Foreach Binding +--- + # `foreach` Binding diff --git a/packages/binding.core/docs/hasfocus-binding.md b/tko.io/src/docs/bindings/hasfocus-binding.md similarity index 97% rename from packages/binding.core/docs/hasfocus-binding.md rename to tko.io/src/docs/bindings/hasfocus-binding.md index 995aafabe..ddacd2e29 100644 --- a/packages/binding.core/docs/hasfocus-binding.md +++ b/tko.io/src/docs/bindings/hasfocus-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Hasfocus Binding +--- + # `hasFocus` ### Purpose diff --git a/packages/binding.core/docs/html-binding.md b/tko.io/src/docs/bindings/html-binding.md similarity index 97% rename from packages/binding.core/docs/html-binding.md rename to tko.io/src/docs/bindings/html-binding.md index 08438f8c8..85506ca84 100644 --- a/packages/binding.core/docs/html-binding.md +++ b/tko.io/src/docs/bindings/html-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Html Binding +--- + # `html` diff --git a/packages/binding.if/docs/if-binding.md b/tko.io/src/docs/bindings/if-binding.md similarity index 98% rename from packages/binding.if/docs/if-binding.md rename to tko.io/src/docs/bindings/if-binding.md index 648a0d392..48eb3aaf5 100644 --- a/packages/binding.if/docs/if-binding.md +++ b/tko.io/src/docs/bindings/if-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: If Binding +--- + # `if` Binding diff --git a/packages/binding.if/docs/ifnot-binding.md b/tko.io/src/docs/bindings/ifnot-binding.md similarity index 94% rename from packages/binding.if/docs/ifnot-binding.md rename to tko.io/src/docs/bindings/ifnot-binding.md index 49bdfe4e5..6a5c157b8 100644 --- a/packages/binding.if/docs/ifnot-binding.md +++ b/tko.io/src/docs/bindings/ifnot-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Ifnot Binding +--- + # `ifnot` diff --git a/packages/binding.if/docs/index.md b/tko.io/src/docs/bindings/index.md similarity index 68% rename from packages/binding.if/docs/index.md rename to tko.io/src/docs/bindings/index.md index 6d65ed55f..e955e84ca 100644 --- a/packages/binding.if/docs/index.md +++ b/tko.io/src/docs/bindings/index.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Index Binding +--- + # tko.binding.if Bindings for TKO: diff --git a/packages/binding.core/docs/options-binding.md b/tko.io/src/docs/bindings/options-binding.md similarity index 99% rename from packages/binding.core/docs/options-binding.md rename to tko.io/src/docs/bindings/options-binding.md index 5c556dbb2..786faa2a2 100644 --- a/packages/binding.core/docs/options-binding.md +++ b/tko.io/src/docs/bindings/options-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Options Binding +--- + # `options` ### Purpose diff --git a/packages/binding.core/docs/selectedOptions-binding.md b/tko.io/src/docs/bindings/selectedOptions-binding.md similarity index 98% rename from packages/binding.core/docs/selectedOptions-binding.md rename to tko.io/src/docs/bindings/selectedOptions-binding.md index aa176d389..7a50b60da 100644 --- a/packages/binding.core/docs/selectedOptions-binding.md +++ b/tko.io/src/docs/bindings/selectedOptions-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: SelectedOptions Binding +--- + # `selectedOptions` diff --git a/packages/binding.core/docs/style-binding.md b/tko.io/src/docs/bindings/style-binding.md similarity index 98% rename from packages/binding.core/docs/style-binding.md rename to tko.io/src/docs/bindings/style-binding.md index 51c4fd48d..9e1ae0eb4 100644 --- a/packages/binding.core/docs/style-binding.md +++ b/tko.io/src/docs/bindings/style-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Style Binding +--- + # `style` ### Purpose diff --git a/packages/binding.core/docs/submit-binding.md b/tko.io/src/docs/bindings/submit-binding.md similarity index 98% rename from packages/binding.core/docs/submit-binding.md rename to tko.io/src/docs/bindings/submit-binding.md index e6a5769f4..290ff0586 100644 --- a/packages/binding.core/docs/submit-binding.md +++ b/tko.io/src/docs/bindings/submit-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Submit Binding +--- + # `submit` diff --git a/packages/binding.template/docs/template-binding.md b/tko.io/src/docs/bindings/template-binding.md similarity index 99% rename from packages/binding.template/docs/template-binding.md rename to tko.io/src/docs/bindings/template-binding.md index 10bcdbafc..85e7fb058 100644 --- a/packages/binding.template/docs/template-binding.md +++ b/tko.io/src/docs/bindings/template-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Template Binding +--- + # `template` Binding diff --git a/packages/binding.core/docs/text-binding.md b/tko.io/src/docs/bindings/text-binding.md similarity index 98% rename from packages/binding.core/docs/text-binding.md rename to tko.io/src/docs/bindings/text-binding.md index f4a69fbde..f852306b7 100644 --- a/packages/binding.core/docs/text-binding.md +++ b/tko.io/src/docs/bindings/text-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Text Binding +--- + # `text` diff --git a/packages/binding.core/docs/textInput-binding.md b/tko.io/src/docs/bindings/textInput-binding.md similarity index 98% rename from packages/binding.core/docs/textInput-binding.md rename to tko.io/src/docs/bindings/textInput-binding.md index 2950148a3..31c879bfe 100644 --- a/packages/binding.core/docs/textInput-binding.md +++ b/tko.io/src/docs/bindings/textInput-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: TextInput Binding +--- + # `textInput` diff --git a/packages/binding.core/docs/uniqueName-binding.md b/tko.io/src/docs/bindings/uniqueName-binding.md similarity index 96% rename from packages/binding.core/docs/uniqueName-binding.md rename to tko.io/src/docs/bindings/uniqueName-binding.md index 0765425ca..d8c0d5023 100644 --- a/packages/binding.core/docs/uniqueName-binding.md +++ b/tko.io/src/docs/bindings/uniqueName-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: UniqueName Binding +--- + # `uniqueName` diff --git a/packages/binding.core/docs/unobtrusive-event-handling.md b/tko.io/src/docs/bindings/unobtrusive-event-handling.md similarity index 98% rename from packages/binding.core/docs/unobtrusive-event-handling.md rename to tko.io/src/docs/bindings/unobtrusive-event-handling.md index ac96d2ba1..6d542ee0b 100644 --- a/packages/binding.core/docs/unobtrusive-event-handling.md +++ b/tko.io/src/docs/bindings/unobtrusive-event-handling.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Unobtrusive Event Handling Binding +--- + # Unobtrusive Event Handlers In most cases, data-bind attributes provide a clean and succinct way to bind to a view model. However, event handling is one area that can often result in verbose data-bind attributes, as anonymous functions were typically the recommended techinique to pass arguments. For example: diff --git a/packages/binding.core/docs/value-binding.md b/tko.io/src/docs/bindings/value-binding.md similarity index 99% rename from packages/binding.core/docs/value-binding.md rename to tko.io/src/docs/bindings/value-binding.md index 23f1627e6..1a5083fd1 100644 --- a/packages/binding.core/docs/value-binding.md +++ b/tko.io/src/docs/bindings/value-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Value Binding +--- + # `value` diff --git a/packages/binding.core/docs/visible-binding.md b/tko.io/src/docs/bindings/visible-binding.md similarity index 97% rename from packages/binding.core/docs/visible-binding.md rename to tko.io/src/docs/bindings/visible-binding.md index 85388b11b..ce8db7ac7 100644 --- a/packages/binding.core/docs/visible-binding.md +++ b/tko.io/src/docs/bindings/visible-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Visible Binding +--- + # `visible` diff --git a/packages/binding.if/docs/with-binding.md b/tko.io/src/docs/bindings/with-binding.md similarity index 98% rename from packages/binding.if/docs/with-binding.md rename to tko.io/src/docs/bindings/with-binding.md index 7aac52bae..641dbd004 100644 --- a/packages/binding.if/docs/with-binding.md +++ b/tko.io/src/docs/bindings/with-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: With Binding +--- + # `with` Binding diff --git a/packages/utils.component/docs/component-binding.md b/tko.io/src/docs/components/component-binding.md similarity index 99% rename from packages/utils.component/docs/component-binding.md rename to tko.io/src/docs/components/component-binding.md index f153c2f9b..c264bf883 100644 --- a/packages/utils.component/docs/component-binding.md +++ b/tko.io/src/docs/components/component-binding.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Component Binding +--- + # `component` diff --git a/packages/utils.component/docs/component-custom-elements.md b/tko.io/src/docs/components/component-custom-elements.md similarity index 99% rename from packages/utils.component/docs/component-custom-elements.md rename to tko.io/src/docs/components/component-custom-elements.md index 7e23fb93c..5f4c565ce 100644 --- a/packages/utils.component/docs/component-custom-elements.md +++ b/tko.io/src/docs/components/component-custom-elements.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Component Custom Elements +--- + # Custom Component Elements @@ -148,7 +153,7 @@ ko.components.getComponentNameForNode = function(node) { You can use this technique if, for example, you want to control which subset of registered components may be used as custom elements. -### Registering custom elements {#registering-custom-elements} +### Registering custom elements {% raw %}{#registering-custom-elements}{% endraw %} If you are using the default component loader, and hence are registering your components using `ko.components.register`, then there is nothing extra you need to do. Components registered this way are immediately available for use as custom elements. diff --git a/packages/utils.component/docs/component-loaders.md b/tko.io/src/docs/components/component-loaders.md similarity index 98% rename from packages/utils.component/docs/component-loaders.md rename to tko.io/src/docs/components/component-loaders.md index 06993b6d1..39f5d371d 100644 --- a/packages/utils.component/docs/component-loaders.md +++ b/tko.io/src/docs/components/component-loaders.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Component Loaders +--- + # Component Loaders @@ -6,7 +11,7 @@ Whenever you inject a [component](component-overview.html) using the [`component * [Table of contents injected here] {:toc} -# The default component loader {#default-component-loader} +# The default component loader {% raw %}{#default-component-loader}{% endraw %} The built-in default component loader, `ko.components.defaultLoader`, is based around a central "registry" of component definitions. It relies on you explicitly registering a configuration for each component before you can use that component. @@ -42,7 +47,7 @@ Also, since `ko.components.defaultLoader` is a component loader, it implements t For documentation on these standard component loader functions, see [implementing a custom component loader](#custom-component-loader). -# Implementing a custom component loader {#custom-component-loader} +# Implementing a custom component loader {% raw %}{#custom-component-loader}{% endraw %} You might want to implement a custom component loader if you want to use naming conventions, rather than explicit registration, to load components. Or, if you want to use a third-party "loader" library to fetch component viewmodels or templates from external locations. diff --git a/packages/utils.component/docs/component-overview.md b/tko.io/src/docs/components/component-overview.md similarity index 98% rename from packages/utils.component/docs/component-overview.md rename to tko.io/src/docs/components/component-overview.md index 76b6707da..377c13800 100644 --- a/packages/utils.component/docs/component-overview.md +++ b/tko.io/src/docs/components/component-overview.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Component Overview +--- + # Component Overview diff --git a/packages/utils.component/docs/component-registration.md b/tko.io/src/docs/components/component-registration.md similarity index 99% rename from packages/utils.component/docs/component-registration.md rename to tko.io/src/docs/components/component-registration.md index 02cdb3e46..af2eb2472 100644 --- a/packages/utils.component/docs/component-registration.md +++ b/tko.io/src/docs/components/component-registration.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Component Registration +--- + # Component Registration diff --git a/packages/computed/docs/computed-dependency-tracking.md b/tko.io/src/docs/computed/computed-dependency-tracking.md similarity index 98% rename from packages/computed/docs/computed-dependency-tracking.md rename to tko.io/src/docs/computed/computed-dependency-tracking.md index ab730e79f..727286261 100644 --- a/packages/computed/docs/computed-dependency-tracking.md +++ b/tko.io/src/docs/computed/computed-dependency-tracking.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Computed Dependency Tracking +--- + # Dependency Tracking diff --git a/packages/computed/docs/computed-pure.md b/tko.io/src/docs/computed/computed-pure.md similarity index 97% rename from packages/computed/docs/computed-pure.md rename to tko.io/src/docs/computed/computed-pure.md index 6f4eab938..33d8e56a7 100644 --- a/packages/computed/docs/computed-pure.md +++ b/tko.io/src/docs/computed/computed-pure.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Computed Pure +--- + # Pure Computed Observables @@ -12,7 +17,7 @@ A *pure* computed observable automatically switches between two states based on 2. Whenever it has *any* `change` subscribers, it is awake and ***listening***. When entering the *listening* state, it immediately subscribes to any dependencies. In this state, it operates just like a regular computed observable, as described in [how dependency tracking works](computed-dependency-tracking.md). -#### Why "pure"? {#pure-computed-function-defined} +#### Why "pure"? {% raw %}{#pure-computed-function-defined}{% endraw %} We've borrowed the term from [pure functions](http://en.wikipedia.org/wiki/Pure_function) because this feature is generally only applicable for computed observables whose evaluator is a *pure function* as follows: diff --git a/packages/computed/docs/computed-reference.md b/tko.io/src/docs/computed/computed-reference.md similarity index 99% rename from packages/computed/docs/computed-reference.md rename to tko.io/src/docs/computed/computed-reference.md index 5bb7ea860..574ed5231 100644 --- a/packages/computed/docs/computed-reference.md +++ b/tko.io/src/docs/computed/computed-reference.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Computed Reference +--- + # Computed Observable Reference diff --git a/packages/computed/docs/computed-writable.md b/tko.io/src/docs/computed/computed-writable.md similarity index 98% rename from packages/computed/docs/computed-writable.md rename to tko.io/src/docs/computed/computed-writable.md index 1aadb28ad..3df6e1fa3 100644 --- a/packages/computed/docs/computed-writable.md +++ b/tko.io/src/docs/computed/computed-writable.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Computed Writable +--- + # Writeable Computeds diff --git a/packages/computed/docs/computedObservables.md b/tko.io/src/docs/computed/computedObservables.md similarity index 99% rename from packages/computed/docs/computedObservables.md rename to tko.io/src/docs/computed/computedObservables.md index 26edb9354..279ec653d 100644 --- a/packages/computed/docs/computedObservables.md +++ b/tko.io/src/docs/computed/computedObservables.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: ComputedObservables +--- + # Computed Observables Overview What if you've got an [observable](observables.html) for `firstName`, and another for `lastName`, and you want to display the full name? That's where *computed observables* come in - these are functions that are dependent on one or more other observables, and will automatically update whenever any of these dependencies change. diff --git a/packages/computed/docs/proxy.md b/tko.io/src/docs/computed/proxy.md similarity index 96% rename from packages/computed/docs/proxy.md rename to tko.io/src/docs/computed/proxy.md index 3aa38c5c7..7806c1303 100644 --- a/packages/computed/docs/proxy.md +++ b/tko.io/src/docs/computed/proxy.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Proxy +--- + # Proxy Where the [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object is [supported](https://caniuse.com/#search=Proxy), TKO can wrap objects to make them observable. diff --git a/packages/observable/docs/arraychange.md b/tko.io/src/docs/observables/arraychange.md similarity index 96% rename from packages/observable/docs/arraychange.md rename to tko.io/src/docs/observables/arraychange.md index eaba1b3fe..6841b2d29 100644 --- a/packages/observable/docs/arraychange.md +++ b/tko.io/src/docs/observables/arraychange.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Arraychange +--- + # Tracking Array Changes diff --git a/packages/observable/docs/extenders.md b/tko.io/src/docs/observables/extenders.md similarity index 98% rename from packages/observable/docs/extenders.md rename to tko.io/src/docs/observables/extenders.md index 9e0935cc5..979f96f28 100644 --- a/packages/observable/docs/extenders.md +++ b/tko.io/src/docs/observables/extenders.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Extenders +--- + # Observable Extenders diff --git a/packages/observable/docs/observableArrays.md b/tko.io/src/docs/observables/observableArrays.md similarity index 99% rename from packages/observable/docs/observableArrays.md rename to tko.io/src/docs/observables/observableArrays.md index 1c793a552..99466eba6 100644 --- a/packages/observable/docs/observableArrays.md +++ b/tko.io/src/docs/observables/observableArrays.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: ObservableArrays +--- + # Observable Arrays diff --git a/packages/observable/docs/observables.md b/tko.io/src/docs/observables/observables.md similarity index 99% rename from packages/observable/docs/observables.md rename to tko.io/src/docs/observables/observables.md index 0b2359d1b..0dd431a68 100644 --- a/packages/observable/docs/observables.md +++ b/tko.io/src/docs/observables/observables.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Observables +--- + # Observables Overview diff --git a/packages/observable/docs/rateLimit-observable.md b/tko.io/src/docs/observables/rateLimit-observable.md similarity index 99% rename from packages/observable/docs/rateLimit-observable.md rename to tko.io/src/docs/observables/rateLimit-observable.md index 16c2ec088..f3725136e 100644 --- a/packages/observable/docs/rateLimit-observable.md +++ b/tko.io/src/docs/observables/rateLimit-observable.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: RateLimit Observable +--- + # Extender: `rateLimit` diff --git a/packages/observable/docs/throttle-extender.md b/tko.io/src/docs/observables/throttle-extender.md similarity index 98% rename from packages/observable/docs/throttle-extender.md rename to tko.io/src/docs/observables/throttle-extender.md index 562631ee5..ed63362d0 100644 --- a/packages/observable/docs/throttle-extender.md +++ b/tko.io/src/docs/observables/throttle-extender.md @@ -1,3 +1,8 @@ +--- +layout: base.njk +title: Throttle Extender +--- + # Throttle extender *Note: This throttle API is deprecated as of Knockout 3.1.0. Please use the [`rateLimit` extender](#rateLimit-observable) for similar functionality.* diff --git a/tko.io/src/index.md b/tko.io/src/index.md index 36d66ec0d..084d505f3 100644 --- a/tko.io/src/index.md +++ b/tko.io/src/index.md @@ -3,14 +3,87 @@ layout: base.njk title: Introduction --- -# TKO +# What is TKO? -Welcome to the TKO documentation. +TKO is a Javascript web framework, and the foundation for Knockout 4. -## Example +Knockout helps you create rich, responsive, maintainable applications built on a clean underlying data model. + +- **Simple data-html bindings** + Easily associate DOM elements with model data using a concise, readable syntax, like this: `` +- **Two-way observables** + Data model and DOM stay in sync, updating the UI whenever the data changes. +- **Computed dependencies** + Create chains of calculated variables dependencies. +- **Templating** + Create reusable components and sophisticated web applications. +- **Extensible** + Implement custom behaviors and compartmentalized code. + +TKO has a comprehensive suite of tests that ensure its correct functioning and allow easy verification on different Javascript browsers and platforms. + +## Sponsors + +Support Knockout [via Patreon to Brian M Hunt](https://patreon.com/brianmhunt) + +## First Example ```jsx -function Hello() { - return
Hello World
-} +// Simple observable example +const viewModel = { + firstName: ko.observable('John'), + lastName: ko.observable('Doe') +}; + +viewModel.fullName = ko.computed(() => { + return viewModel.firstName() + ' ' + viewModel.lastName(); +}); + +ko.applyBindings(viewModel); +``` + +```html +
+

First name:

+

Last name:

+

Hello, !

+
+``` + +## Supported Platforms + +TKO & Knockout should work on all modern browsers, as well as Javascript engines such as Node.js. + +## Getting started + +Include the latest version with this ` +``` + +or install it locally with: + +```bash +npm install tko +# or +yarn add tko +# or +bun add tko ``` + +Clone the code with: + +```bash +git clone git@github.com:knockout/tko +``` + +## Community + +Find Knockout online at: + +- [Gitter knockout/tko](https://gitter.im/knockout/tko) +- [Gitter knockout/knockout](https://gitter.im/knockout/knockout) +- [Reddit /r/knockoutjs](https://www.reddit.com/r/knockoutjs/) +- [Google Groups](https://groups.google.com/forum/#!forum/knockoutjs) +- [StackOverflow [knockout.js]](http://stackoverflow.com/tags/knockout.js/info) From a4ac0cbb682dfcd2ea1536dceba82e56dc36666f Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 11:37:12 -0500 Subject: [PATCH 04/12] reduce left-panel white-space --- tko.io/src/css/tko.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css index 8927959d9..b175773c7 100644 --- a/tko.io/src/css/tko.css +++ b/tko.io/src/css/tko.css @@ -225,7 +225,7 @@ blockquote { overflow-x: hidden; background-color: #f9fafb; border-right: 1px solid #e5e7eb; - padding: 2rem 0; + padding: 1rem 0; } /* Custom scrollbar for sidebar */ @@ -249,13 +249,13 @@ blockquote { .toc { display: flex; flex-direction: column; - gap: 0.25rem; + gap: 0.125rem; padding: 0 1rem; } .sidebar a { display: block; - padding: 0.5rem 0.75rem; + padding: 0.375rem 0.75rem; color: #4b5563; font-size: 0.875rem; border-radius: 6px; @@ -272,8 +272,8 @@ blockquote { font-size: 0.875rem; font-weight: 600; color: #111827; - padding: 0.75rem 0.75rem 0.5rem; - margin-top: 1.5rem; + padding: 0.5rem 0.75rem 0.25rem; + margin-top: 1rem; text-transform: uppercase; letter-spacing: 0.05em; font-size: 0.75rem; From 4f00b059d02dde143f8f46b47c52dcdf5312f15f Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 11:44:40 -0500 Subject: [PATCH 05/12] tko.io) fix broken links --- tko.io/src/docs/binding-context/binding-context.md | 2 +- .../src/docs/binding-context/binding-preprocessing.md | 4 ++-- tko.io/src/docs/binding-context/binding-syntax.md | 2 +- .../custom-bindings-controlling-descendant-bindings.md | 2 +- .../docs/binding-context/custom-bindings-disposal.md | 2 +- .../custom-bindings-for-virtual-elements.md | 2 +- tko.io/src/docs/bindings/checked-binding.md | 2 +- tko.io/src/docs/bindings/click-binding.md | 2 +- tko.io/src/docs/bindings/css-binding.md | 2 +- tko.io/src/docs/bindings/disable-binding.md | 2 +- tko.io/src/docs/bindings/event-binding.md | 4 ++-- tko.io/src/docs/bindings/foreach-binding.md | 10 +++++----- tko.io/src/docs/bindings/html-binding.md | 2 +- tko.io/src/docs/bindings/if-binding.md | 2 +- tko.io/src/docs/bindings/options-binding.md | 4 ++-- tko.io/src/docs/bindings/selectedOptions-binding.md | 6 +++--- tko.io/src/docs/bindings/style-binding.md | 2 +- tko.io/src/docs/bindings/template-binding.md | 10 +++++----- tko.io/src/docs/bindings/text-binding.md | 4 ++-- tko.io/src/docs/bindings/unobtrusive-event-handling.md | 2 +- tko.io/src/docs/bindings/value-binding.md | 4 ++-- tko.io/src/docs/bindings/with-binding.md | 8 ++++---- .../src/docs/components/component-custom-elements.md | 6 +++--- tko.io/src/docs/components/component-registration.md | 4 ++-- .../src/docs/computed/computed-dependency-tracking.md | 4 ++-- tko.io/src/docs/computed/computed-pure.md | 2 +- tko.io/src/docs/computed/computed-reference.md | 6 +++--- tko.io/src/docs/computed/computed-writable.md | 2 +- tko.io/src/docs/computed/computedObservables.md | 10 +++++----- tko.io/src/docs/observables/observables.md | 4 ++-- tko.io/src/docs/observables/rateLimit-observable.md | 4 ++-- 31 files changed, 61 insertions(+), 61 deletions(-) diff --git a/tko.io/src/docs/binding-context/binding-context.md b/tko.io/src/docs/binding-context/binding-context.md index d45055ab7..c453d7249 100644 --- a/tko.io/src/docs/binding-context/binding-context.md +++ b/tko.io/src/docs/binding-context/binding-context.md @@ -6,7 +6,7 @@ title: Binding Context # Binding Context -A *binding context* is an object that holds data that you can reference from your bindings. While applying bindings, Knockout automatically creates and manages a hierarchy of binding contexts. The root level of the hierarchy refers to the `viewModel` parameter you supplied to `ko.applyBindings(viewModel)`. Then, each time you use a control flow binding such as [`with`](with-binding.html) or [`foreach`](foreach-binding.html), that creates a child binding context that refers to the nested view model data. +A *binding context* is an object that holds data that you can reference from your bindings. While applying bindings, Knockout automatically creates and manages a hierarchy of binding contexts. The root level of the hierarchy refers to the `viewModel` parameter you supplied to `ko.applyBindings(viewModel)`. Then, each time you use a control flow binding such as [`with`](../with-binding/) or [`foreach`](../foreach-binding/), that creates a child binding context that refers to the nested view model data. Bindings contexts offer the following special properties that you can reference in any binding: diff --git a/tko.io/src/docs/binding-context/binding-preprocessing.md b/tko.io/src/docs/binding-context/binding-preprocessing.md index f85f7f60f..2b0b76851 100644 --- a/tko.io/src/docs/binding-context/binding-preprocessing.md +++ b/tko.io/src/docs/binding-context/binding-preprocessing.md @@ -12,7 +12,7 @@ Starting with Knockout 3.0, developers can define custom syntaxes by providing c ## Preprocessing binding strings -You can hook into Knockout's logic for interpreting `data-bind` attributes by providing a *binding preprocessor* for a specific binding handler (such as `click`, `visible`, or any [custom binding handler](custom-bindings.html)). +You can hook into Knockout's logic for interpreting `data-bind` attributes by providing a *binding preprocessor* for a specific binding handler (such as `click`, `visible`, or any [custom binding handler](../../binding-context/custom-bindings/)). To do this, attach a `preprocess` function to the binding handler: @@ -82,7 +82,7 @@ Now you can bind `click` like this: ## Preprocessing DOM nodes -You can hook into Knockout's logic for traversing the DOM by providing a *node preprocessor*. This is a function that Knockout will call once for each DOM node that it walks over, both when the UI is first bound, and later when any new DOM subtrees are injected (e.g., via a [`foreach` binding](foreach-binding.html)). +You can hook into Knockout's logic for traversing the DOM by providing a *node preprocessor*. This is a function that Knockout will call once for each DOM node that it walks over, both when the UI is first bound, and later when any new DOM subtrees are injected (e.g., via a [`foreach` binding](../foreach-binding/)). To do this, define a `preprocessNode` function on your binding provider: diff --git a/tko.io/src/docs/binding-context/binding-syntax.md b/tko.io/src/docs/binding-context/binding-syntax.md index e0e3df8ff..5da2b5c4b 100644 --- a/tko.io/src/docs/binding-context/binding-syntax.md +++ b/tko.io/src/docs/binding-context/binding-syntax.md @@ -26,7 +26,7 @@ Your value: Cellphone: ``` -The binding *name* should generally match a registered binding handler (either built-in or [custom](custom-bindings.html)) or be a parameter for another binding. If the name matches neither of those, Knockout will ignore it (without any error or warning). So if a binding doesn't appear to work, first check that the name is correct. +The binding *name* should generally match a registered binding handler (either built-in or [custom](../../binding-context/custom-bindings/)) or be a parameter for another binding. If the name matches neither of those, Knockout will ignore it (without any error or warning). So if a binding doesn't appear to work, first check that the name is correct. #### Binding values diff --git a/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md b/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md index bc6ff5ca9..d4c931643 100644 --- a/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md +++ b/tko.io/src/docs/binding-context/custom-bindings-controlling-descendant-bindings.md @@ -41,7 +41,7 @@ To see this take effect, here's a sample usage: ### Example: Supplying additional values to descendant bindings -Normally, bindings that use `controlsDescendantBindings` will also call `ko.applyBindingsToDescendants(someBindingContext, element)` to apply the descendant bindings against some modified [binding context](binding-context.html). For example, you could have a binding called `withProperties` that attaches some extra properties to the binding context that will then be available to all descendant bindings: +Normally, bindings that use `controlsDescendantBindings` will also call `ko.applyBindingsToDescendants(someBindingContext, element)` to apply the descendant bindings against some modified [binding context](../../binding-context/binding-context/). For example, you could have a binding called `withProperties` that attaches some extra properties to the binding context that will then be available to all descendant bindings: ```javascript ko.bindingHandlers.withProperties = { diff --git a/tko.io/src/docs/binding-context/custom-bindings-disposal.md b/tko.io/src/docs/binding-context/custom-bindings-disposal.md index 300b9aac3..23f1a4307 100644 --- a/tko.io/src/docs/binding-context/custom-bindings-disposal.md +++ b/tko.io/src/docs/binding-context/custom-bindings-disposal.md @@ -5,7 +5,7 @@ title: Custom Bindings Disposal # Custom Disposal Logic -In a typical Knockout application, DOM elements are dynamically added and removed, for example using the [`template`](template-binding.html) binding or via control-flow bindings ([`if`](if-binding.html), [`ifnot`](ifnot-binding.html), [`with`](with-binding.html), and [`foreach`](foreach-binding.html)). When creating a custom binding, it is often desirable to add clean-up logic that runs when an element associated with your custom binding is removed by Knockout. +In a typical Knockout application, DOM elements are dynamically added and removed, for example using the [`template`](template-binding.html) binding or via control-flow bindings ([`if`](../if-binding/), [`ifnot`](ifnot-binding.html), [`with`](../with-binding/), and [`foreach`](../foreach-binding/)). When creating a custom binding, it is often desirable to add clean-up logic that runs when an element associated with your custom binding is removed by Knockout. ### Registering a callback on the disposal of an element diff --git a/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md b/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md index 105e0dab8..a7aa1024c 100644 --- a/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md +++ b/tko.io/src/docs/binding-context/custom-bindings-for-virtual-elements.md @@ -8,7 +8,7 @@ title: Custom Bindings For Virtual Elements *Note: Creating custom bindings that support virtual elements is an advanced technique, typically used only when creating libraries of reusable bindings. It's not something you'll normally need to do when building applications with Knockout.* -Knockout's *control flow bindings* (e.g., [`if`](if-binding.html) and [`foreach`](foreach-binding.html)) can be applied not only to regular DOM elements, but also to "virtual" DOM elements defined by a special comment-based syntax. For example: +Knockout's *control flow bindings* (e.g., [`if`](../if-binding/) and [`foreach`](../foreach-binding/)) can be applied not only to regular DOM elements, but also to "virtual" DOM elements defined by a special comment-based syntax. For example: ```html
    diff --git a/tko.io/src/docs/bindings/checked-binding.md b/tko.io/src/docs/bindings/checked-binding.md index 329c7eddb..36edb5b09 100644 --- a/tko.io/src/docs/bindings/checked-binding.md +++ b/tko.io/src/docs/bindings/checked-binding.md @@ -11,7 +11,7 @@ The `checked` binding links a checkable form control — i.e., a checkbox (` When the user checks the associated form control, this updates the value on your view model. Likewise, when you update the value in your view model, this checks or unchecks the form control on screen. -Note: For text boxes, drop-down lists, and all non-checkable form controls, use [the `value` binding](value-binding.html) to read and write the element's value, not the `checked` binding. +Note: For text boxes, drop-down lists, and all non-checkable form controls, use [the `value` binding](../value-binding/) to read and write the element's value, not the `checked` binding. ### Example with checkbox diff --git a/tko.io/src/docs/bindings/click-binding.md b/tko.io/src/docs/bindings/click-binding.md index 30bae7bd0..14a7700a9 100644 --- a/tko.io/src/docs/bindings/click-binding.md +++ b/tko.io/src/docs/bindings/click-binding.md @@ -35,7 +35,7 @@ some UI for each item in a collection, and you need to know which item's UI was Two points to note about this example: - * If you're inside a nested [binding context](binding-context.html), for example if you're inside a `foreach` or a `with` block, but your handler function + * If you're inside a nested [binding context](../../binding-context/binding-context/), for example if you're inside a `foreach` or a `with` block, but your handler function is on the root viewmodel or some other parent context, you'll need to use a prefix such as `$parent` or `$root` to locate the handler function. * In your viewmodel, it's often useful to declare `self` (or some other variable) as an alias for `this`. Doing so avoids any problems diff --git a/tko.io/src/docs/bindings/css-binding.md b/tko.io/src/docs/bindings/css-binding.md index fbd809b50..6f0433c02 100644 --- a/tko.io/src/docs/bindings/css-binding.md +++ b/tko.io/src/docs/bindings/css-binding.md @@ -8,7 +8,7 @@ title: Css Binding ### Purpose The `css` binding adds or removes one or more named CSS classes to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative. -(Note: If you don't want to apply a CSS class but instead want to assign a `style` attribute value directly, see [the style binding](style-binding.html).) +(Note: If you don't want to apply a CSS class but instead want to assign a `style` attribute value directly, see [the style binding](../style-binding/).) ### Example with static classes ```html diff --git a/tko.io/src/docs/bindings/disable-binding.md b/tko.io/src/docs/bindings/disable-binding.md index a1d48dc19..e8af8f6c2 100644 --- a/tko.io/src/docs/bindings/disable-binding.md +++ b/tko.io/src/docs/bindings/disable-binding.md @@ -8,4 +8,4 @@ title: Disable Binding ### Purpose The `disable` binding causes the associated DOM element to be disabled only when the parameter value is `true`. This is useful with form elements like `input`, `select`, and `textarea`. -This is the mirror image of the `enable` binding. For more information, see [documentation for the `enable` binding](#enable-binding), because `disable` works in exactly the same way except that it negates whatever parameter you pass to it. +This is the mirror image of the `enable` binding. For more information, see [documentation for the `enable` binding](../enable-binding/), because `disable` works in exactly the same way except that it negates whatever parameter you pass to it. diff --git a/tko.io/src/docs/bindings/event-binding.md b/tko.io/src/docs/bindings/event-binding.md index 5aa221a0c..efcd8a462 100644 --- a/tko.io/src/docs/bindings/event-binding.md +++ b/tko.io/src/docs/bindings/event-binding.md @@ -75,7 +75,7 @@ function MyViewModel() { Two points to note about this example: - * If you're inside a nested [binding context](binding-context.html), for example if you're inside a `foreach` or a `with` block, but your handler function + * If you're inside a nested [binding context](../../binding-context/binding-context/), for example if you're inside a `foreach` or a `with` block, but your handler function is on the root viewmodel or some other parent context, you'll need to use a prefix such as `$parent` or `$root` to locate the handler function. * In your viewmodel, it's often useful to declare `self` (or some other variable) as an alias for `this`. Doing so avoids any problems @@ -124,7 +124,7 @@ Alternatively, if you prefer to avoid the function literal in your view, you can ### Note 3: Allowing the default action -By default, Knockout will prevent the event from taking any default action. For example if you use the `event` binding to capture the `keypress` event of an `input` tag, the browser will only call your handler function and will *not* add the value of the key to the `input` element's value. A more common example is using [the click binding](click-binding.html), which internally uses this binding, where your handler function will be called, but the browser will *not* navigate to the link's `href`. This is a useful default because when you use the `click` binding, it's normally because you're using the link as part of a UI that manipulates your view model, not as a regular hyperlink to another web page. +By default, Knockout will prevent the event from taking any default action. For example if you use the `event` binding to capture the `keypress` event of an `input` tag, the browser will only call your handler function and will *not* add the value of the key to the `input` element's value. A more common example is using [the click binding](../click-binding/), which internally uses this binding, where your handler function will be called, but the browser will *not* navigate to the link's `href`. This is a useful default because when you use the `click` binding, it's normally because you're using the link as part of a UI that manipulates your view model, not as a regular hyperlink to another web page. However, if you *do* want to let the default action proceed, just return `true` from your `event` handler function. diff --git a/tko.io/src/docs/bindings/foreach-binding.md b/tko.io/src/docs/bindings/foreach-binding.md index 60df74b4c..bb8248fcf 100644 --- a/tko.io/src/docs/bindings/foreach-binding.md +++ b/tko.io/src/docs/bindings/foreach-binding.md @@ -9,7 +9,7 @@ title: Foreach Binding ### Purpose The `foreach` binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables. -Assuming your array is an [observable array](observableArrays.html), whenever you later add, remove, or re-order array entries, the binding will efficiently update the UI to match - inserting or removing more copies of the markup, or re-ordering existing DOM elements, without affecting any other DOM elements. This is far faster than regenerating the entire `foreach` output after each array change. +Assuming your array is an [observable array](../../observables/observableArrays/), whenever you later add, remove, or re-order array entries, the binding will efficiently update the UI to match - inserting or removing more copies of the markup, or re-ordering existing DOM elements, without affecting any other DOM elements. This is far faster than regenerating the entire `foreach` output after each array change. Of course, you can arbitrarily nest any number of `foreach` bindings along with other control-flow bindings such as `if` and `with`. @@ -48,7 +48,7 @@ The following example shows that, if your array is observable, then the UI will As shown in the above examples, bindings within the `foreach` block can refer to properties on the array entries. For example, [Example 1](#example_1_iterating_over_an_array) referenced the `firstName` and `lastName` properties on each array entry. -But what if you want to refer to the array entry itself (not just one of its properties)? In that case, you can use the [special context property](binding-context.html) `$data`. Within a `foreach` block, it means "the current item". For example, +But what if you want to refer to the array entry itself (not just one of its properties)? In that case, you can use the [special context property](../../binding-context/binding-context/) `$data`. Within a `foreach` block, it means "the current item". For example, @@ -76,11 +76,11 @@ Similarly, you can use `$parent` to refer to data from outside the `foreach`, e.
``` -For more information about `$index` and other context properties such as `$parent`, see documentation for [binding context properties](binding-context.html). +For more information about `$index` and other context properties such as `$parent`, see documentation for [binding context properties](../../binding-context/binding-context/). ### Note 3: Using "as" to give an alias to "foreach" items -As described in Note 1, you can refer to each array entry using the `$data` [context variable](binding-context.html). In some cases though, it may be useful to give the current item a more descriptive name using the `as` option like: +As described in Note 1, you can refer to each array entry using the `$data` [context variable](../../binding-context/binding-context/). In some cases though, it may be useful to give the current item a more descriptive name using the `as` option like: ```html
    @@ -142,7 +142,7 @@ By default, the `foreach` binding will skip over (i.e., hide) any array entries If you need to run some further custom logic on the generated DOM elements, you can use any of the `afterRender`/`afterAdd`/`beforeRemove`/`beforeMove`/`afterMove` callbacks described below. -> **Note:** These callbacks are *only* intended for triggering animations related to changes in a list. If your goal is actually to attach other behaviors to new DOM elements when they have been added (e.g., event handlers, or to activate third-party UI controls), then your work will be much easier if you implement that new behavior as a [custom binding](custom-bindings.html) instead, because then you can use that behavior anywhere, independently of the `foreach` binding. +> **Note:** These callbacks are *only* intended for triggering animations related to changes in a list. If your goal is actually to attach other behaviors to new DOM elements when they have been added (e.g., event handlers, or to activate third-party UI controls), then your work will be much easier if you implement that new behavior as a [custom binding](../../binding-context/custom-bindings/) instead, because then you can use that behavior anywhere, independently of the `foreach` binding. Here's a trivial example that uses `afterAdd` to apply the classic "yellow fade" effect to newly-added items. It requires the [jQuery Color plugin](https://github.com/jquery/jquery-color) to enable animation of background colors. diff --git a/tko.io/src/docs/bindings/html-binding.md b/tko.io/src/docs/bindings/html-binding.md index 85506ca84..5a0bc9432 100644 --- a/tko.io/src/docs/bindings/html-binding.md +++ b/tko.io/src/docs/bindings/html-binding.md @@ -47,4 +47,4 @@ viewModel.details("For further details, view the report ` element will then display one item for each item in your array. -Note: For a multi-select list, to set which of the options are selected, or to read which of the options are selected, use [the `selectedOptions` binding](selectedOptions-binding.html). For a single-select list, you can also read and write the selected option using [the `value` binding](value-binding.html). +Note: For a multi-select list, to set which of the options are selected, or to read which of the options are selected, use [the `selectedOptions` binding](../selectedOptions-binding/). For a single-select list, you can also read and write the selected option using [the `value` binding](../value-binding/). ### Example 1: Drop-down list @@ -141,7 +141,7 @@ Note that the only difference between examples 3 and 4 is the `optionsText` valu * `selectedOptions` - For a multi-select list, you can read and write the selection state using `selectedOptions`. Technically this is a separate binding, so it has [its own documentation](selectedOptions-binding.html). + For a multi-select list, you can read and write the selection state using `selectedOptions`. Technically this is a separate binding, so it has [its own documentation](../selectedOptions-binding/). * `valueAllowUnset` diff --git a/tko.io/src/docs/bindings/selectedOptions-binding.md b/tko.io/src/docs/bindings/selectedOptions-binding.md index 7a50b60da..f9719712f 100644 --- a/tko.io/src/docs/bindings/selectedOptions-binding.md +++ b/tko.io/src/docs/bindings/selectedOptions-binding.md @@ -12,7 +12,7 @@ The `selectedOptions` binding controls which elements in a multi-select list are When the user selects or de-selects an item in the multi-select list, this adds or removes the corresponding value to an array on your view model. Likewise, assuming it's an *observable* array on your view model, then whenever you add or remove (e.g., via `push` or `splice`) items to this array, the corresponding items in the UI become selected or deselected. It's a 2-way binding. -Note: To control which element in a single-select drop-down list is selected, you can use [the `value` binding](value-binding.html) instead. +Note: To control which element in a single-select drop-down list is selected, you can use [the `value` binding](../value-binding/) instead. ### Example ```html @@ -38,7 +38,7 @@ viewModel.chosenCountries.push('France'); // Now France is selected too This should be an array (or an observable array). KO sets the element's selected options to match the contents of the array. Any previous selection state will be overwritten. - If your parameter is an observable array, the binding will update the element's selection whenever the array changes (e.g., via `push`, `pop` or [other observable array methods](observableArrays.html)). If the parameter isn't observable, it will only set the element's selection state once and will not update it again later. + If your parameter is an observable array, the binding will update the element's selection whenever the array changes (e.g., via `push`, `pop` or [other observable array methods](../../observables/observableArrays/)). If the parameter isn't observable, it will only set the element's selection state once and will not update it again later. Whether or not the parameter is an observable array, KO will detect when the user selects or deselects an item in the multi-select list, and will update the array to match. This is how you can read which of the options is selected. @@ -48,6 +48,6 @@ viewModel.chosenCountries.push('France'); // Now France is selected too ### Note: Letting the user select from arbitrary JavaScript objects -In the example code above, the user can choose from an array of string values. You're *not* limited to providing strings - your `options` array can contain arbitrary JavaScript objects if you wish. See [the `options` binding](options-binding.html) for details on how to control how arbitrary objects should be displayed in the list. +In the example code above, the user can choose from an array of string values. You're *not* limited to providing strings - your `options` array can contain arbitrary JavaScript objects if you wish. See [the `options` binding](../options-binding/) for details on how to control how arbitrary objects should be displayed in the list. In this scenario, the values you can read and write using `selectedOptions` are those objects themselves, *not* their textual representations. This leads to much cleaner and more elegant code in most cases. Your view model can imagine that the user chooses from an array of arbitrary objects, without having to care how those objects are mapped to an on-screen representation. diff --git a/tko.io/src/docs/bindings/style-binding.md b/tko.io/src/docs/bindings/style-binding.md index 9e1ae0eb4..7ffffe413 100644 --- a/tko.io/src/docs/bindings/style-binding.md +++ b/tko.io/src/docs/bindings/style-binding.md @@ -8,7 +8,7 @@ title: Style Binding ### Purpose The `style` binding adds or removes one or more style values to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative, or to set the width of a bar to match a numerical value that changes. -(Note: If you don't want to apply an explicit style value but instead want to assign a CSS class, see [the css binding](css-binding.html).) +(Note: If you don't want to apply an explicit style value but instead want to assign a CSS class, see [the css binding](../css-binding/).) ### Example diff --git a/tko.io/src/docs/bindings/template-binding.md b/tko.io/src/docs/bindings/template-binding.md index 85e7fb058..ba3ccf4cd 100644 --- a/tko.io/src/docs/bindings/template-binding.md +++ b/tko.io/src/docs/bindings/template-binding.md @@ -98,7 +98,7 @@ This gives the same result as embedding an anonymous template directly inside th ### Note 3: Using "as" to give an alias to "foreach" items -When nesting `foreach` templates, it's often useful to refer to items at higher levels in the hierarchy. One way to do this is to refer to `$parent` or other [binding context](binding-context.html) variables in your bindings. +When nesting `foreach` templates, it's often useful to refer to items at higher levels in the hierarchy. One way to do this is to refer to `$parent` or other [binding context](../../binding-context/binding-context/) variables in your bindings. A simpler and more elegant option, however, is to use `as` to declare a name for your iteration variables. For example: @@ -148,7 +148,7 @@ Tip: Remember to pass a *string literal value* to as (e.g., `as: 'season'`, *not Sometimes you might want to run custom post-processing logic on the DOM elements generated by your templates. For example, if you're using a JavaScript widgets library such as jQuery UI, you might want to intercept your templates' output so that you can run jQuery UI commands on it to transform some of the rendered elements into date pickers, sliders, or anything else. -Generally, the best way to perform such post-processing on DOM elements is to write a [custom binding](custom-bindings.html), but if you really just want to access the raw DOM elements emitted by a template, you can use `afterRender`. +Generally, the best way to perform such post-processing on DOM elements is to write a [custom binding](../../binding-context/custom-bindings/), but if you really just want to access the raw DOM elements emitted by a template, you can use `afterRender`. Pass a function reference (either a function literal, or give the name of a function on your view model), and Knockout will invoke it immediately after rendering or re-rendering your template. If you're using `foreach`, Knockout will invoke your `afterRender` callback for each item added to your observable array. For example, @@ -167,7 +167,7 @@ viewModel.myPostProcessingLogic = function(elements) { } ``` -If you are using `foreach` and only want to be notified about elements that are specifically being added or are being removed, you can use `afterAdd` and `beforeRemove` instead. For details, see documentation for the [`foreach` binding](foreach-binding.html). +If you are using `foreach` and only want to be notified about elements that are specifically being added or are being removed, you can use `afterAdd` and `beforeRemove` instead. For details, see documentation for the [`foreach` binding](../foreach-binding/). ### Note 5: Dynamically choosing which template is used @@ -198,7 +198,7 @@ javascript: |- If your function references observable values, then the binding will update whenever any of those values change. This will cause the data to be re-rendered using the appropriate template. -If your function accepts a second parameter, then it will receive the entire [binding context](binding-context.html). You can then access `$parent` or any other [binding context](binding-context.html) variable when dynamically choosing a template. For example, you could amend the preceding code snippet as follows: +If your function accepts a second parameter, then it will receive the entire [binding context](../../binding-context/binding-context/). You can then access `$parent` or any other [binding context](../../binding-context/binding-context/) variable when dynamically choosing a template. For example, you could amend the preceding code snippet as follows: ```javascript displayMode: function(employee, bindingContext) { @@ -258,7 +258,7 @@ The [Underscore.js template engine](http://documentcloud.github.com/underscore/# ``` -Here's [a simple implementation of integrating Underscore templates with Knockout](http://jsfiddle.net/rniemeyer/NW5Vn/). The integration code is just 16 lines long, but it's enough to support Knockout `data-bind` attributes (and hence nested templates) and Knockout [binding context](binding-context.html) variables (`$parent`, `$root`, etc.). +Here's [a simple implementation of integrating Underscore templates with Knockout](http://jsfiddle.net/rniemeyer/NW5Vn/). The integration code is just 16 lines long, but it's enough to support Knockout `data-bind` attributes (and hence nested templates) and Knockout [binding context](../../binding-context/binding-context/) variables (`$parent`, `$root`, etc.). If you're not a fan of the `<%= ... %>` delimiters, you can configure the Underscore template engine to use any other delimiter characters of your choice. diff --git a/tko.io/src/docs/bindings/text-binding.md b/tko.io/src/docs/bindings/text-binding.md index f852306b7..3de24465a 100644 --- a/tko.io/src/docs/bindings/text-binding.md +++ b/tko.io/src/docs/bindings/text-binding.md @@ -31,7 +31,7 @@ Typically this is useful with elements like `` or `` that traditionall ### Note 1: Using functions and expressions to detemine text values -If you want to detemine text programmatically, one option is to create a [computed observable](computedObservables.html), and use its evaluator function as a place for your code that works out what text to display. +If you want to detemine text programmatically, one option is to create a [computed observable](../../computed/computedObservables/), and use its evaluator function as a place for your code that works out what text to display. For example, @@ -59,7 +59,7 @@ viewModel.myMessage("Hello, world!"); ... this would *not* render as italic text, but would render as literal text with visible angle brackets. -If you need to set HTML content in this manner, see [the html binding](html-binding.html). +If you need to set HTML content in this manner, see [the html binding](../html-binding/). ### Note 3: Using "text" without a container element diff --git a/tko.io/src/docs/bindings/unobtrusive-event-handling.md b/tko.io/src/docs/bindings/unobtrusive-event-handling.md index 6d542ee0b..002476cdc 100644 --- a/tko.io/src/docs/bindings/unobtrusive-event-handling.md +++ b/tko.io/src/docs/bindings/unobtrusive-event-handling.md @@ -16,7 +16,7 @@ In most cases, data-bind attributes provide a clean and succinct way to bind to As an alternative, Knockout provides two helper functions that allow you to identify the data associated with a DOM element: * `ko.dataFor(element)` - returns the data that was available for binding against the element - * `ko.contextFor(element)` - returns the entire [binding context](binding-context.html) that was available to the DOM element. + * `ko.contextFor(element)` - returns the entire [binding context](../../binding-context/binding-context/) that was available to the DOM element. These helper functions can be used in event handlers that are attached unobtrusively using something like jQuery's `bind` or `click`. The above function could be attached to each link with a `remove` class like: diff --git a/tko.io/src/docs/bindings/value-binding.md b/tko.io/src/docs/bindings/value-binding.md index 1a5083fd1..248219f3f 100644 --- a/tko.io/src/docs/bindings/value-binding.md +++ b/tko.io/src/docs/bindings/value-binding.md @@ -14,7 +14,7 @@ The `value` binding links the associated DOM element's value with a property on When the user edits the value in the associated form control, it updates the value on your view model. Likewise, when you update the value in your view model, this updates the value of the form control on screen. -Note: If you're working with checkboxes or radio buttons, use [the `checked` binding](checked-binding.html) to read and write your element's checked state, not the `value` binding. +Note: If you're working with checkboxes or radio buttons, use [the `checked` binding](../checked-binding/) to read and write your element's checked state, not the `value` binding. ### Example @@ -62,7 +62,7 @@ Note: If you're working with checkboxes or radio buttons, use [the `checked` bin ### Note 1: Working with drop-down lists (i.e., `` elements). The `value` binding works in conjunction with the `options` binding to let you read and write values that are arbitrary JavaScript objects, not just string values. This is very useful if you want to let the user select from a set of model objects. For examples of this, see [the `options` binding](options-binding.html) or for handling multi-select lists, see the documentation for [the `selectedOptions` binding](selectedOptions-binding.html). +Knockout has special support for drop-down lists (i.e., `` element that does not use the `options` binding. In this case, you can choose to specify your `` elements and Knockout will set the selected value appropriately. diff --git a/tko.io/src/docs/bindings/with-binding.md b/tko.io/src/docs/bindings/with-binding.md index 641dbd004..69ac082d3 100644 --- a/tko.io/src/docs/bindings/with-binding.md +++ b/tko.io/src/docs/bindings/with-binding.md @@ -7,9 +7,9 @@ title: With Binding # `with` Binding ### Purpose -The `with` binding creates a new [binding context](binding-context.html), so that descendant elements are bound in the context of a specified object. +The `with` binding creates a new [binding context](../../binding-context/binding-context/), so that descendant elements are bound in the context of a specified object. -Of course, you can arbitrarily nest `with` bindings along with the other control-flow bindings such as [`if`](if-binding.html) and [`foreach`](foreach-binding.html). +Of course, you can arbitrarily nest `with` bindings along with the other control-flow bindings such as [`if`](../if-binding/) and [`foreach`](../foreach-binding/). ### Example 1 @@ -38,7 +38,7 @@ javascript: |- This interactive example demonstrates that: * The `with` binding will dynamically add or remove descendant elements depending on whether the associated value is `null`/`undefined` or not - * If you want to access data/functions from parent binding contexts, you can use [special context properties such as `$parent` and `root`](binding-context.html). + * If you want to access data/functions from parent binding contexts, you can use [special context properties such as `$parent` and `root`](../../binding-context/binding-context/). ```example html: |- @@ -97,7 +97,7 @@ javascript: |- ### Note 1: Using "with" without a container element -Just like other control flow elements such as [`if`](if-binding.html) and [`foreach`](foreach-binding.html), you can use `with` without any container element to host it. This is useful if you need to use `with` in a place where it would not be legal to introduce a new container element just to hold the `with` binding. See the documentation for [`if`](if-binding.html) or [`foreach`](foreach-binding.html) for more details. +Just like other control flow elements such as [`if`](../if-binding/) and [`foreach`](../foreach-binding/), you can use `with` without any container element to host it. This is useful if you need to use `with` in a place where it would not be legal to introduce a new container element just to hold the `with` binding. See the documentation for [`if`](../if-binding/) or [`foreach`](../foreach-binding/) for more details. Example: diff --git a/tko.io/src/docs/components/component-custom-elements.md b/tko.io/src/docs/components/component-custom-elements.md index 5f4c565ce..7ed9872bb 100644 --- a/tko.io/src/docs/components/component-custom-elements.md +++ b/tko.io/src/docs/components/component-custom-elements.md @@ -81,7 +81,7 @@ In the following example, In general, if a parameter's evaluation does not involve evaluating an observable (in this case, the value did not involve observables at all), then the value is passed literally. If the value was an object, then the child component could mutate it, but since it's not observable the parent would not know the child had done so. * `simpleObservable` - * This will be the [`ko.observable`](observables.html) instance declared on the parent viewmodel as `myObservable`. It is not a wrapper --- it's the actual same instance as referenced by the parent. So if the child viewmodel writes to this observable, the parent viewmodel will receive that change. + * This will be the [`ko.observable`](../../observables/observables/) instance declared on the parent viewmodel as `myObservable`. It is not a wrapper --- it's the actual same instance as referenced by the parent. So if the child viewmodel writes to this observable, the parent viewmodel will receive that change. In general, if a parameter's evaluation does not involve evaluating an observable (in this case, the observable was simply passed without evaluating it), then the value is passed literally. @@ -113,7 +113,7 @@ Consider a special list component that can be invoked as follows: By default, the DOM nodes inside `` will be stripped out (without being bound to any viewmodel) and replaced by the component's output. However, those DOM nodes aren't lost: they are remembered, and are supplied to the component in two ways: - * As an array, `$componentTemplateNodes`, available to any binding expression in the component's template (i.e., as a [binding context](binding-context.html) property). Usually this is the most convenient way to use the supplied markup. See the example below. + * As an array, `$componentTemplateNodes`, available to any binding expression in the component's template (i.e., as a [binding context](../../binding-context/binding-context/) property). Usually this is the most convenient way to use the supplied markup. See the example below. * As an array, `componentInfo.templateNodes`, passed to its [`createViewModel` function](component-registration.html#a-createviewmodel-factory-function) The component can then choose to use the supplied DOM nodes as part of its output however it wishes, such as by using `template: { nodes: $componentTemplateNodes }` on any element in the component's template. @@ -175,7 +175,7 @@ A custom element can have a regular `data-bind` attribute (in addition to any `p ``` -However, it does not make sense to use bindings that would modify the element's contents, such as the [`text`](text-binding.html) or [`template`](template-binding.html) bindings, since they would overwrite the template injected by your component. +However, it does not make sense to use bindings that would modify the element's contents, such as the [`text`](../text-binding/) or [`template`](template-binding.html) bindings, since they would overwrite the template injected by your component. Knockout will prevent the use of any bindings that use [`controlsDescendantBindings`](custom-bindings-controlling-descendant-bindings.html), because this also would clash with the component when trying to bind its viewmodel to the injected template. Therefore if you want to use a control flow binding such as `if` or `foreach`, then you must wrap it around your custom element rather than using it directly on the custom element, e.g.,: diff --git a/tko.io/src/docs/components/component-registration.md b/tko.io/src/docs/components/component-registration.md index af2eb2472..f5cbe3887 100644 --- a/tko.io/src/docs/components/component-registration.md +++ b/tko.io/src/docs/components/component-registration.md @@ -92,7 +92,7 @@ ko.components.register('my-component', { }); ``` -Note that, typically, it's best to perform direct DOM manipulation only through [custom bindings](custom-bindings.html) rather than acting on `componentInfo.element` from inside `createViewModel`. This leads to more modular, reusable code. +Note that, typically, it's best to perform direct DOM manipulation only through [custom bindings](../../binding-context/custom-bindings/) rather than acting on `componentInfo.element` from inside `createViewModel`. This leads to more modular, reusable code. The `componentInfo.templateNodes` array is useful if you want to build a component that accepts arbitrary markup to influence its output (for example, a grid, list, dialog, or tab set that injects supplied markup into itself). For a complete example, see [passing markup into components](component-custom-elements.html#passing-markup-into-components). @@ -312,7 +312,7 @@ ko.components.register('my-component', { Knockout does not call `require([moduleName], ...)` until your component is being instantiated. This is how components get loaded on demand, not up front. -For example, if your component is inside some other element with an [`if` binding](if-binding.html) (or another control flow binding), then it will not cause the AMD module to be loaded until the `if` condition is true. Of course, if the AMD module was already loaded (e.g., in a preloaded bundle) then the `require` call will not trigger any additional HTTP requests, so you can control what is preloaded and what is loaded on demand. +For example, if your component is inside some other element with an [`if` binding](../if-binding/) (or another control flow binding), then it will not cause the AMD module to be loaded until the `if` condition is true. Of course, if the AMD module was already loaded (e.g., in a preloaded bundle) then the `require` call will not trigger any additional HTTP requests, so you can control what is preloaded and what is loaded on demand. ## Registering components as a single AMD module diff --git a/tko.io/src/docs/computed/computed-dependency-tracking.md b/tko.io/src/docs/computed/computed-dependency-tracking.md index 727286261..42282eb5e 100644 --- a/tko.io/src/docs/computed/computed-dependency-tracking.md +++ b/tko.io/src/docs/computed/computed-dependency-tracking.md @@ -18,7 +18,7 @@ So, Knockout doesn't just detect dependencies the first time the evaluator runs The other neat trick is that declarative bindings are simply implemented as computed observables. So, if a binding reads the value of an observable, that binding becomes dependent on that observable, which causes that binding to be re-evaluated if the observable changes. -*Pure* computed observables work slightly differently. For more details, see the documentation for [*pure* computed observables](computed-pure.html). +*Pure* computed observables work slightly differently. For more details, see the documentation for [*pure* computed observables](../computed-pure/). ## Controlling dependencies using peek @@ -36,7 +36,7 @@ ko.computed(function() { }, this); ``` -Note: If you just want to prevent a computed observable from updating too often, see the [`rateLimit` extender](rateLimit-observable.html). +Note: If you just want to prevent a computed observable from updating too often, see the [`rateLimit` extender](../../observables/rateLimit-observable/). ## Ignoring dependencies within a computed diff --git a/tko.io/src/docs/computed/computed-pure.md b/tko.io/src/docs/computed/computed-pure.md index 33d8e56a7..a7e8ea64b 100644 --- a/tko.io/src/docs/computed/computed-pure.md +++ b/tko.io/src/docs/computed/computed-pure.md @@ -78,7 +78,7 @@ You should not use the *pure* feature for a computed observable that is meant to }); ``` -The reason you shouldn't use a *pure* computed if the evaluator has important side effects is simply that the evaluator will not run whenever the computed has no active subscribers (and so is sleeping). If it's important for the evaluator to always run when dependencies change, use a [regular computed](computedObservables.html) instead. +The reason you shouldn't use a *pure* computed if the evaluator has important side effects is simply that the evaluator will not run whenever the computed has no active subscribers (and so is sleeping). If it's important for the evaluator to always run when dependencies change, use a [regular computed](../../computed/computedObservables/) instead. ### State-change notifications diff --git a/tko.io/src/docs/computed/computed-reference.md b/tko.io/src/docs/computed/computed-reference.md index 574ed5231..20898994e 100644 --- a/tko.io/src/docs/computed/computed-reference.md +++ b/tko.io/src/docs/computed/computed-reference.md @@ -21,12 +21,12 @@ A computed observable can be constructed using one of the following forms: * `read` --- Required. A function that is used to evaluate the computed observable's current value. * `write` --- Optional. If given, makes the computed observable writable. This is a function that receives values that other code is trying to write to your computed observable. It's up to you to supply custom logic to handle the incoming values, typically by writing the values to some underlying observable(s). * `owner` --- Optional. If given, defines the value of `this` whenever KO invokes your `read` or `write` callbacks. - * `pure` --- Optional. If this option is `true`, the computed observable will be set up as a [*pure* computed observable](computed-pure.html). This option is an alternative to the `ko.pureComputed` constructor. + * `pure` --- Optional. If this option is `true`, the computed observable will be set up as a [*pure* computed observable](../computed-pure/). This option is an alternative to the `ko.pureComputed` constructor. * `deferEvaluation` --- Optional. If this option is `true`, then the value of the computed observable will not be evaluated until something actually attempts to access its value or manually subscribes to it. By default, a computed observable has its value determined immediately during creation. * `disposeWhen` --- Optional. If given, this function is executed before each re-evaluation to determine if the computed observable should be disposed. A `true`-ish result will trigger disposal of the computed observable. * `disposeWhenNodeIsRemoved` --- Optional. If given, disposal of the computed observable will be triggered when the specified DOM node is removed by KO. This feature is used to dispose computed observables used in bindings when nodes are removed by the `template` and control-flow bindings. -1. `ko.pureComputed( evaluator [, targetObject] )` --- Constructs a [*pure* computed observable](computed-pure.html) using the given evaluator function and optional object to use for `this`. Unlike `ko.computed`, this method doesn't accept an `options` parameter. +1. `ko.pureComputed( evaluator [, targetObject] )` --- Constructs a [*pure* computed observable](../computed-pure/) using the given evaluator function and optional object to use for `this`. Unlike `ko.computed`, this method doesn't accept an `options` parameter. 1. `ko.pureComputed( options )` --- Constructs a *pure* computed observable using an `options` object. This accepts the `read`, `write`, and `owner` options described above. @@ -35,7 +35,7 @@ A computed observable can be constructed using one of the following forms: A computed observable provides the following functions: * `dispose()` --- Manually disposes the computed observable, clearing all subscriptions to dependencies. This function is useful if you want to stop a computed observable from being updated or want to clean up memory for a computed observable that has dependencies on observables that won't be cleaned. -* `extend(extenders)` --- Applies the given [extenders](extenders.html) to the computed observable. +* `extend(extenders)` --- Applies the given [extenders](../../observables/extenders/) to the computed observable. * `getDependenciesCount()` --- Returns the current number of dependencies of the computed observable. * `getSubscriptionsCount( [event] )` --- Returns the current number of subscriptions (either from other computed observables or manual subscriptions) of the computed observable. Optionally, pass an event name (like `"change"`) to return just the count of subscriptions for that event. * `isActive()` --- Returns whether the computed observable may be updated in the future. A computed observable is inactive if it has no dependencies. diff --git a/tko.io/src/docs/computed/computed-writable.md b/tko.io/src/docs/computed/computed-writable.md index 3df6e1fa3..4c5b0ccb7 100644 --- a/tko.io/src/docs/computed/computed-writable.md +++ b/tko.io/src/docs/computed/computed-writable.md @@ -22,7 +22,7 @@ Going back to the classic "first name + last name = full name" example, you can This is the exact opposite of the [Hello World](../examples/helloWorld.html) example, in that here the first and last names are not editable, but the combined full name is editable. -The preceding view model code demonstrates the *single parameter syntax* for initializing computed observables. See the [computed observable reference](computed-reference.html) for the full list of available options. +The preceding view model code demonstrates the *single parameter syntax* for initializing computed observables. See the [computed observable reference](../computed-reference/) for the full list of available options. ### Example 2: Selecting/deselecting all items diff --git a/tko.io/src/docs/computed/computedObservables.md b/tko.io/src/docs/computed/computedObservables.md index 279ec653d..6360126e5 100644 --- a/tko.io/src/docs/computed/computedObservables.md +++ b/tko.io/src/docs/computed/computedObservables.md @@ -5,7 +5,7 @@ title: ComputedObservables # Computed Observables Overview -What if you've got an [observable](observables.html) for `firstName`, and another for `lastName`, and you want to display the full name? That's where *computed observables* come in - these are functions that are dependent on one or more other observables, and will automatically update whenever any of these dependencies change. +What if you've got an [observable](../../observables/observables/) for `firstName`, and another for `lastName`, and you want to display the full name? That's where *computed observables* come in - these are functions that are dependent on one or more other observables, and will automatically update whenever any of these dependencies change. For example, given the following view model class, @@ -74,7 +74,7 @@ Then, changes to `items` or `selectedIndexes` will ripple through the chain of c ### Forcing computed observables to always notify subscribers -When a computed observable returns a primitive value (a number, string, boolean, or null), the dependencies of the observable are normally only notified if the value actually changed. However, it is possible to use the built-in `notify` [extender](extenders.html) to ensure that a computed observable's subscribers are always notified on an update, even if the value is the same. You would apply the extender like this: +When a computed observable returns a primitive value (a number, string, boolean, or null), the dependencies of the observable are normally only notified if the value actually changed. However, it is possible to use the built-in `notify` [extender](../../observables/extenders/) to ensure that a computed observable's subscribers are always notified on an update, even if the value is the same. You would apply the extender like this: ```javascript myViewModel.fullName = ko.computed(function() { @@ -84,7 +84,7 @@ myViewModel.fullName = ko.computed(function() { ### Delaying and/or suppressing change notifications -Normally, a computed observable updates and notifies its subscribers immediately, as soon as its dependencies change. But if a computed observable has many dependencies or involves expensive updates, you may get better performance by limiting or delaying the computed observable's updates and notifications. This is accomplished using the [`rateLimit` extender](rateLimit-observable.html) like this: +Normally, a computed observable updates and notifies its subscribers immediately, as soon as its dependencies change. But if a computed observable has many dependencies or involves expensive updates, you may get better performance by limiting or delaying the computed observable's updates and notifications. This is accomplished using the [`rateLimit` extender](../../observables/rateLimit-observable/) like this: ```javascript // Ensure updates no more than once per 50-millisecond period @@ -240,7 +240,7 @@ ko.computed(function() { }, this); ``` -Note: If you just want to prevent a computed observable from updating too often, see the [`rateLimit` extender](rateLimit-observable.html). +Note: If you just want to prevent a computed observable from updating too often, see the [`rateLimit` extender](../../observables/rateLimit-observable/). ### Note: Why circular dependencies aren't meaningful @@ -291,7 +291,7 @@ A computed observable can be constructed using one of the following forms: A computed observable provides the following functions: * `dispose()` --- Manually disposes the computed observable, clearing all subscriptions to dependencies. This function is useful if you want to stop a computed observable from being updated or want to clean up memory for a computed observable that has dependencies on observables that won't be cleaned. -* `extend(extenders)` --- Applies the given [extenders](extenders.html) to the computed observable. +* `extend(extenders)` --- Applies the given [extenders](../../observables/extenders/) to the computed observable. * `getDependenciesCount()` --- Returns the current number of dependencies of the computed observable. * `getSubscriptionsCount()` --- Returns the current number of subscriptions (either from other computed observables or manual subscriptions) of the computed observable. * `isActive()` --- Returns whether the computed observable may be updated in the future. A computed observable is inactive if it has no dependencies. diff --git a/tko.io/src/docs/observables/observables.md b/tko.io/src/docs/observables/observables.md index 0dd431a68..e4c621090 100644 --- a/tko.io/src/docs/observables/observables.md +++ b/tko.io/src/docs/observables/observables.md @@ -147,7 +147,7 @@ Observables implement a variety of functions useful for working in an asynchrono ## Forcing observables to always notify subscribers -When writing to an observable that contains a primitive value (a number, string, boolean, or null), the dependencies of the observable are normally only notified if the value actually changed. However, it is possible to use the built-in `notify` [extender](extenders.html) to ensure that an observable's subscribers are always notified on a write, even if the value is the same. You would apply the extender to an observable like this: +When writing to an observable that contains a primitive value (a number, string, boolean, or null), the dependencies of the observable are normally only notified if the value actually changed. However, it is possible to use the built-in `notify` [extender](../../observables/extenders/) to ensure that an observable's subscribers are always notified on a write, even if the value is the same. You would apply the extender to an observable like this: ```javascript myViewModel.personName.extend({ notify: 'always' }); @@ -155,7 +155,7 @@ myViewModel.personName.extend({ notify: 'always' }); ## Delaying and/or suppressing change notifications -Normally, an observable notifies its subscribers immediately, as soon as it's changed. But if an observable is changed repeatedly or triggers expensive updates, you may get better performance by limiting or delaying the observable's change notifications. This is accomplished using the [`rateLimit` extender](rateLimit-observable.html) like this: +Normally, an observable notifies its subscribers immediately, as soon as it's changed. But if an observable is changed repeatedly or triggers expensive updates, you may get better performance by limiting or delaying the observable's change notifications. This is accomplished using the [`rateLimit` extender](../../observables/rateLimit-observable/) like this: ```javascript // Ensure it notifies about changes no more than once per 50-millisecond period diff --git a/tko.io/src/docs/observables/rateLimit-observable.md b/tko.io/src/docs/observables/rateLimit-observable.md index f3725136e..304609f13 100644 --- a/tko.io/src/docs/observables/rateLimit-observable.md +++ b/tko.io/src/docs/observables/rateLimit-observable.md @@ -8,9 +8,9 @@ title: RateLimit Observable *Note: This rate-limit API was added in Knockout 3.1.0. For previous versions, the [`throttle` extender](throttle-extender.html) provides similar functionality.* -Normally, an [observable](observables.html) that is changed notifies its subscribers immediately, so that any computed observables or bindings that depend on the observable are updated synchronously. The `rateLimit` extender, however, causes an observable to suppress and delay change notifications for a specified period of time. A rate-limited observable therefore updates dependencies asynchronously. +Normally, an [observable](../../observables/observables/) that is changed notifies its subscribers immediately, so that any computed observables or bindings that depend on the observable are updated synchronously. The `rateLimit` extender, however, causes an observable to suppress and delay change notifications for a specified period of time. A rate-limited observable therefore updates dependencies asynchronously. -The `rateLimit` extender can be applied to any type of observable, including [observable arrays](observableArrays.html) and [computed observables](computedObservables.html). The main use cases for rate-limiting are: +The `rateLimit` extender can be applied to any type of observable, including [observable arrays](../../observables/observableArrays/) and [computed observables](../../computed/computedObservables/). The main use cases for rate-limiting are: * Making things respond after a certain delay * Combining multiple changes into a single update From 59f5e53f3d76763e3ae3867f1067a074958bf119 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 11:51:28 -0500 Subject: [PATCH 06/12] tko.io) restyle binding handling --- tko.io/.eleventy.js | 5 +++++ tko.io/src/_layouts/base.njk | 2 +- tko.io/src/css/tko.css | 10 ++++++++++ .../unobtrusive-event-handling.md | 0 4 files changed, 16 insertions(+), 1 deletion(-) rename tko.io/src/docs/{bindings => binding-context}/unobtrusive-event-handling.md (100%) diff --git a/tko.io/.eleventy.js b/tko.io/.eleventy.js index 61b29d989..436971dc0 100644 --- a/tko.io/.eleventy.js +++ b/tko.io/.eleventy.js @@ -69,6 +69,11 @@ export default function(eleventyConfig) { .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", diff --git a/tko.io/src/_layouts/base.njk b/tko.io/src/_layouts/base.njk index 28d32ac3a..9c3b277d2 100644 --- a/tko.io/src/_layouts/base.njk +++ b/tko.io/src/_layouts/base.njk @@ -21,7 +21,7 @@
    Bindings
    {%- for page in collections.bindingDocs -%} -
    {{ page.fileSlug | formatTitle }} + {{ page.fileSlug | formatBindingName }} {%- endfor -%}
    Observables
    diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css index b175773c7..9c0a05598 100644 --- a/tko.io/src/css/tko.css +++ b/tko.io/src/css/tko.css @@ -305,6 +305,16 @@ blockquote { background-color: #3b82f6; } +/* Binding name code styling in sidebar */ +.sidebar .binding-name code { + background-color: transparent; + color: inherit; + padding: 0; + border-radius: 0; + font-size: 0.8125rem; + font-weight: 500; +} + @keyframes slideIn { from { opacity: 0; diff --git a/tko.io/src/docs/bindings/unobtrusive-event-handling.md b/tko.io/src/docs/binding-context/unobtrusive-event-handling.md similarity index 100% rename from tko.io/src/docs/bindings/unobtrusive-event-handling.md rename to tko.io/src/docs/binding-context/unobtrusive-event-handling.md From 25d9bf0bbc60d5f27e4e977e6af429a85871d796 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 14:55:26 -0500 Subject: [PATCH 07/12] tko.io) revise styles, fix code examples, remove patreon --- builds/reference/docs/intro.md | 5 --- builds/reference/src/index.ts | 13 ++++++ tko.io/.eleventy.js | 1 + tko.io/.gitignore | 1 + tko.io/package.json | 2 + tko.io/src/_layouts/base.njk | 1 - tko.io/src/css/tko.css | 73 ++++++++++++++++++++++++++-------- tko.io/src/index.md | 22 +++++----- tko.io/src/js/examples.js | 25 ++++++++---- 9 files changed, 100 insertions(+), 43 deletions(-) diff --git a/builds/reference/docs/intro.md b/builds/reference/docs/intro.md index f8126fe0f..4b26f43c5 100644 --- a/builds/reference/docs/intro.md +++ b/builds/reference/docs/intro.md @@ -24,11 +24,6 @@ TKO has a comprehensive suite of tests that ensure its correct functioning and a ## Sponsors -Support Knockout: - - - via Patreon to Brian M Hunt - ## First Example diff --git a/builds/reference/src/index.ts b/builds/reference/src/index.ts index 10928995e..7b815a5a7 100644 --- a/builds/reference/src/index.ts +++ b/builds/reference/src/index.ts @@ -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' @@ -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) { + 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, diff --git a/tko.io/.eleventy.js b/tko.io/.eleventy.js index 436971dc0..0b6552fe3 100644 --- a/tko.io/.eleventy.js +++ b/tko.io/.eleventy.js @@ -9,6 +9,7 @@ export default function(eleventyConfig) { // Copy static assets eleventyConfig.addPassthroughCopy("src/css"); eleventyConfig.addPassthroughCopy("src/js"); + eleventyConfig.addPassthroughCopy("src/lib"); eleventyConfig.addPassthroughCopy("src/CNAME"); // Watch for changes diff --git a/tko.io/.gitignore b/tko.io/.gitignore index 7b652d135..b35c36980 100644 --- a/tko.io/.gitignore +++ b/tko.io/.gitignore @@ -1,3 +1,4 @@ node_modules/ _site/ package-lock.json +src/lib/ diff --git a/tko.io/package.json b/tko.io/package.json index e22d45b25..15d5b0d80 100644 --- a/tko.io/package.json +++ b/tko.io/package.json @@ -4,6 +4,8 @@ "type": "module", "description": "TKO documentation site", "scripts": { + "prebuild": "mkdir -p src/lib && cd ../builds/knockout && make browser && cp dist/browser.min.js ../../tko.io/src/lib/ko.js && cd ../reference && make browser && cp dist/browser.min.js ../../tko.io/src/lib/tko.js", + "predev": "npm run prebuild", "dev": "bunx @11ty/eleventy --serve", "build": "bunx @11ty/eleventy", "start": "bunx @11ty/eleventy --serve" diff --git a/tko.io/src/_layouts/base.njk b/tko.io/src/_layouts/base.njk index 9c3b277d2..4acbdd35b 100644 --- a/tko.io/src/_layouts/base.njk +++ b/tko.io/src/_layouts/base.njk @@ -10,7 +10,6 @@ diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css index 9c0a05598..6c101097b 100644 --- a/tko.io/src/css/tko.css +++ b/tko.io/src/css/tko.css @@ -28,6 +28,7 @@ body { height: calc(100vh - 64px); grid-template-columns: 280px 1fr; grid-template-areas: 'sidebar main'; + position: relative; } @media screen and (max-width: 991px) { @@ -352,20 +353,22 @@ main { grid-area: main; height: calc(100vh - 64px); background-color: #ffffff; - padding: 3rem; + padding: 3rem 0; overflow-y: auto; overflow-x: hidden; + /* Use CSS grid to allow examples to break out while keeping content centered */ + display: grid; + grid-template-columns: minmax(3rem, 1fr) minmax(0, 1000px) minmax(3rem, 1fr); + grid-auto-rows: auto; } -main > *:first-child { - margin-top: 0; +main > * { + /* Most content goes in the center column */ + grid-column: 2; } -/* Improved readability for main content */ -main { - max-width: 1000px; - margin-left: auto; - margin-right: auto; +main > *:first-child { + margin-top: 0; } h1 code { @@ -412,8 +415,11 @@ pre { margin: 1.5em 0; border-radius: 8px; overflow-x: auto; + overflow-y: visible; line-height: 1.6; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + min-height: fit-content; + height: auto; } pre code { @@ -524,16 +530,22 @@ pre::-webkit-scrollbar-thumb:hover { * INTERACTIVE EXAMPLES */ .example-container { - margin: 2rem 0; + /* Span all 3 columns of the main grid */ + grid-column: 1 / -1; + margin: 2rem 3rem; + height: 400px; border: 1px solid #e5e7eb; - border-radius: 8px; + border-radius: 0; overflow: hidden; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: row; } .example-textarea { - width: 100%; - min-height: 250px; + flex: 1; + min-width: 0; + height: 100%; padding: 1.25rem 1.5rem; font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', 'Inconsolata', monospace; font-size: 0.875rem; @@ -541,23 +553,29 @@ pre::-webkit-scrollbar-thumb:hover { background-color: #1e293b; color: #e2e8f0; border: none; - border-bottom: 2px solid #3b82f6; - resize: vertical; + border-right: 2px solid #3b82f6; + resize: none; outline: none; } .example-textarea:focus { - border-bottom-color: #2563eb; + border-right-color: #2563eb; } .example-result { - width: 100%; - min-height: 250px; + flex: 1; + min-width: 0; + height: 100%; border: none; background-color: #ffffff; + overflow: hidden; } .example-error { + position: absolute; + bottom: 0; + left: 50%; + right: 0; padding: 1rem 1.5rem; background-color: #fef2f2; color: #991b1b; @@ -565,6 +583,27 @@ pre::-webkit-scrollbar-thumb:hover { font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; line-height: 1.5; + z-index: 10; +} + +/* Responsive: stack vertically on mobile */ +@media (max-width: 768px) { + .example-container { + flex-direction: column; + } + + .example-textarea { + border-right: none; + border-bottom: 2px solid #3b82f6; + } + + .example-textarea:focus { + border-bottom-color: #2563eb; + } + + .example-error { + left: 0; + } } /** diff --git a/tko.io/src/index.md b/tko.io/src/index.md index 084d505f3..0278dd9ed 100644 --- a/tko.io/src/index.md +++ b/tko.io/src/index.md @@ -22,14 +22,10 @@ Knockout helps you create rich, responsive, maintainable applications built on a TKO has a comprehensive suite of tests that ensure its correct functioning and allow easy verification on different Javascript browsers and platforms. -## Sponsors - -Support Knockout [via Patreon to Brian M Hunt](https://patreon.com/brianmhunt) ## First Example ```jsx -// Simple observable example const viewModel = { firstName: ko.observable('John'), lastName: ko.observable('Doe') @@ -39,15 +35,17 @@ viewModel.fullName = ko.computed(() => { return viewModel.firstName() + ' ' + viewModel.lastName(); }); -ko.applyBindings(viewModel); -``` +const { node } = ko.jsx.render( +
    +

    First name:

    +

    Last name:

    +

    Hello, !

    +
    +); -```html -
    -

    First name:

    -

    Last name:

    -

    Hello, !

    -
    +document.getElementById('root').appendChild(node); + +ko.applyBindings(viewModel); ``` ## Supported Platforms diff --git a/tko.io/src/js/examples.js b/tko.io/src/js/examples.js index 9f49339aa..b8048d1ec 100644 --- a/tko.io/src/js/examples.js +++ b/tko.io/src/js/examples.js @@ -14,9 +14,12 @@ async function initEsbuild() { async function transformJSX(code) { try { await initEsbuild(); + const result = await esbuild.transform(code, { loader: 'jsx', - jsx: 'transform' + jsx: 'transform', + jsxFactory: 'ko.jsx.createElement', + jsxFragment: 'ko.jsx.Fragment' }); return { success: true, code: result.code }; } catch (error) { @@ -56,6 +59,7 @@ function createExampleContainer(codeBlock) { async function runExample(textarea, iframe) { const code = textarea.value; const result = await transformJSX(code); + const container = textarea.parentElement; if (result.success) { // Create a complete HTML document for the iframe @@ -72,7 +76,12 @@ async function runExample(textarea, iframe) {
    - + + @@ -86,17 +95,17 @@ async function runExample(textarea, iframe) { doc.close(); // Remove any error display - const existingError = iframe.nextElementSibling; - if (existingError && existingError.className === 'example-error') { + const existingError = container.querySelector('.example-error'); + if (existingError) { existingError.remove(); } } else { - // Display error - let errorDiv = iframe.nextElementSibling; - if (!errorDiv || errorDiv.className !== 'example-error') { + // Display error overlaying the preview panel + let errorDiv = container.querySelector('.example-error'); + if (!errorDiv) { errorDiv = document.createElement('div'); errorDiv.className = 'example-error'; - iframe.parentNode.insertBefore(errorDiv, iframe.nextSibling); + container.appendChild(errorDiv); } errorDiv.textContent = `Error: ${result.error}`; } From e75196fda0fef586e3df4df414560c4a988757bd Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Tue, 23 Dec 2025 15:17:27 -0500 Subject: [PATCH 08/12] tko.io) updates to editable areas --- tko.io/src/css/tko.css | 42 ++++++++++++++++-------- tko.io/src/js/examples.js | 67 +++++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/tko.io/src/css/tko.css b/tko.io/src/css/tko.css index 6c101097b..402dfc9b8 100644 --- a/tko.io/src/css/tko.css +++ b/tko.io/src/css/tko.css @@ -532,33 +532,40 @@ pre::-webkit-scrollbar-thumb:hover { .example-container { /* Span all 3 columns of the main grid */ grid-column: 1 / -1; - margin: 2rem 3rem; + margin: 1.5em 3rem; height: 400px; border: 1px solid #e5e7eb; - border-radius: 0; + border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); display: flex; flex-direction: row; + position: relative; } -.example-textarea { +.example-editor { flex: 1; min-width: 0; height: 100%; - padding: 1.25rem 1.5rem; + border-right: 2px solid #3b82f6; + overflow: auto; +} + +.example-editor .cm-editor { + height: 100%; font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', 'Inconsolata', monospace; font-size: 0.875rem; - line-height: 1.6; - background-color: #1e293b; - color: #e2e8f0; - border: none; - border-right: 2px solid #3b82f6; - resize: none; - outline: none; } -.example-textarea:focus { +.example-editor .cm-scroller { + overflow: auto; +} + +.example-editor .cm-focused { + outline: none !important; +} + +.example-editor:focus-within { border-right-color: #2563eb; } @@ -590,17 +597,24 @@ pre::-webkit-scrollbar-thumb:hover { @media (max-width: 768px) { .example-container { flex-direction: column; + margin: 1.5em 1rem; + height: 600px; } - .example-textarea { + .example-editor { border-right: none; border-bottom: 2px solid #3b82f6; + max-height: 50%; } - .example-textarea:focus { + .example-editor:focus-within { border-bottom-color: #2563eb; } + .example-result { + max-height: 50%; + } + .example-error { left: 0; } diff --git a/tko.io/src/js/examples.js b/tko.io/src/js/examples.js index b8048d1ec..f385b20e3 100644 --- a/tko.io/src/js/examples.js +++ b/tko.io/src/js/examples.js @@ -1,4 +1,7 @@ import * as esbuild from 'https://cdn.jsdelivr.net/npm/esbuild-wasm@0.24.0/esm/browser.min.js'; +import { EditorView, basicSetup } from 'https://esm.sh/codemirror@6.0.1'; +import { javascript } from 'https://esm.sh/@codemirror/lang-javascript@6.2.2'; +import { oneDark } from 'https://esm.sh/@codemirror/theme-one-dark@6.1.2'; let esbuildInitialized = false; @@ -31,35 +34,50 @@ function createExampleContainer(codeBlock) { const container = document.createElement('div'); container.className = 'example-container'; - const textarea = document.createElement('textarea'); - textarea.className = 'example-textarea'; - textarea.value = codeBlock.textContent.trim(); + // Create editor wrapper + const editorWrapper = document.createElement('div'); + editorWrapper.className = 'example-editor'; const iframe = document.createElement('iframe'); iframe.className = 'example-result'; - container.appendChild(textarea); + container.appendChild(editorWrapper); container.appendChild(iframe); // Replace the code block with our interactive container const pre = codeBlock.parentElement; pre.parentNode.replaceChild(container, pre); - // Run the code initially - runExample(textarea, iframe); - - // Re-run on change (debounced) + // Create CodeMirror editor + const initialCode = codeBlock.textContent.trim(); let timeout; - textarea.addEventListener('input', () => { - clearTimeout(timeout); - timeout = setTimeout(() => runExample(textarea, iframe), 500); + + const editor = new EditorView({ + doc: initialCode, + extensions: [ + basicSetup, + javascript({ jsx: true }), + oneDark, + EditorView.updateListener.of((update) => { + if (update.docChanged) { + // Re-run on change (debounced) + clearTimeout(timeout); + timeout = setTimeout(() => { + const code = editor.state.doc.toString(); + runExample(code, iframe, container); + }, 500); + } + }) + ], + parent: editorWrapper }); + + // Run the code initially + runExample(initialCode, iframe, container); } -async function runExample(textarea, iframe) { - const code = textarea.value; +async function runExample(code, iframe, container) { const result = await transformJSX(code); - const container = textarea.parentElement; if (result.success) { // Create a complete HTML document for the iframe @@ -76,23 +94,26 @@ async function runExample(textarea, iframe) {
    - + - `; - // Write to iframe - const doc = iframe.contentDocument || iframe.contentWindow.document; - doc.open(); - doc.write(html); - doc.close(); + // Use srcdoc for better iframe content handling + iframe.srcdoc = html; // Remove any error display const existingError = container.querySelector('.example-error'); From 71743605d6704a494c48e7a1eb82982cfed119ef Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Wed, 24 Dec 2025 09:29:45 -0500 Subject: [PATCH 09/12] Update tko.io/package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tko.io/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tko.io/package.json b/tko.io/package.json index 15d5b0d80..430d86f81 100644 --- a/tko.io/package.json +++ b/tko.io/package.json @@ -1,5 +1,5 @@ { - "name": "tko-docs", + "name": "tko.io", "version": "1.0.0", "type": "module", "description": "TKO documentation site", From 41745d7e85d19da8ca13939cb41b212121af2082 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Wed, 24 Dec 2025 09:30:20 -0500 Subject: [PATCH 10/12] Update tko.io/src/docs/advanced/databind-parser.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tko.io/src/docs/advanced/databind-parser.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tko.io/src/docs/advanced/databind-parser.md b/tko.io/src/docs/advanced/databind-parser.md index 17eb88a27..9b7405602 100644 --- a/tko.io/src/docs/advanced/databind-parser.md +++ b/tko.io/src/docs/advanced/databind-parser.md @@ -1,9 +1,9 @@ --- layout: base.njk -title: Databind Parser +title: DataBind Parser --- -# Databind Parser +# DataBind Parser tko.provider is a binding provider for [Knockout](http://knockoutjs.com), namely it parses HTML attributes and converts them to handlers of bidirectional updates. From 67e2d98409eeccb0f0ed5d57e6321d9729106240 Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Wed, 24 Dec 2025 09:30:43 -0500 Subject: [PATCH 11/12] Update tko.io/package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tko.io/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tko.io/package.json b/tko.io/package.json index 430d86f81..8896b963f 100644 --- a/tko.io/package.json +++ b/tko.io/package.json @@ -4,7 +4,10 @@ "type": "module", "description": "TKO documentation site", "scripts": { - "prebuild": "mkdir -p src/lib && cd ../builds/knockout && make browser && cp dist/browser.min.js ../../tko.io/src/lib/ko.js && cd ../reference && make browser && cp dist/browser.min.js ../../tko.io/src/lib/tko.js", + "build:libdir": "mkdir -p src/lib", + "build:ko": "cd ../builds/knockout && make browser && cp dist/browser.min.js ../../tko.io/src/lib/ko.js", + "build:tko": "cd ../builds/reference && make browser && cp dist/browser.min.js ../../tko.io/src/lib/tko.js", + "prebuild": "npm run build:libdir && npm run build:ko && npm run build:tko", "predev": "npm run prebuild", "dev": "bunx @11ty/eleventy --serve", "build": "bunx @11ty/eleventy", From d7e4b57e9a7ad7ac70aae1bf71fea0861c92899f Mon Sep 17 00:00:00 2001 From: Brian M Hunt Date: Wed, 24 Dec 2025 09:42:05 -0500 Subject: [PATCH 12/12] Update tko.io/src/3to4.md Co-authored-by: phillipc --- tko.io/src/3to4.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tko.io/src/3to4.md b/tko.io/src/3to4.md index a1fff9fc6..55b5413c9 100644 --- a/tko.io/src/3to4.md +++ b/tko.io/src/3to4.md @@ -29,7 +29,8 @@ To see the differences, have a look at the [CHANGELOG.md](https://github.com/kno | `foreach` binding | FastForEach | Template foreach | Template foreach | | `each` binding | FastForEach | FastForEach | Template foreach - +# Security advantages in TKO +TKO is ready for strict Content Security Policies (CSP) and does not use `evil-eval` or `new function` techniques. It also provides HTML sanitization through configurable options. To sanitize HTML templates, override the `options.sanitizeHtmlTemplate` setting using a library such as DOMPurify or Validator.js. # Build System TKO has a complete build system rewrite and a conversion of the code from a concatenation of source files to discrete ES6 modules with `import` and `export` statements. This process has been complex and taken years, with the result being a Knockout with a strong foundation of discrete reusable components.