diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 0000000..0c643ca --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,29 @@ +name: Run Unit Tests +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: [master] + tags: ['*'] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + run-unit-tests: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Use Node.js 22 + uses: actions/setup-node@v5 + with: + node-version: 22 + cache: 'npm' + + - run: npm ci + + - name: Run unit tests + run: npx vitest run diff --git a/cypress/e2e/eip7594.cy.ts b/cypress/e2e/eip7594.cy.ts index 2cb69b0..ce38f7d 100644 --- a/cypress/e2e/eip7594.cy.ts +++ b/cypress/e2e/eip7594.cy.ts @@ -13,7 +13,8 @@ describe('EIP-7594/PeerDAS Tests', () => { ) // Select different example - cy.get('#eip-7594-c').find('select').select(2) + cy.get('#eip-7594-c .e-select').click() + cy.get('#eip-7594-c [role="option"]').eq(1).click() cy.get('#eip-7594-c textarea', { timeout: 10000 }).should( 'contain.value', '00000000000000000000000000000000000000000', @@ -27,13 +28,9 @@ describe('EIP-7594/PeerDAS Tests', () => { .should('contain.text', text, { timeout: 25000 }) text = '0x8bd1e6e38b2b54735c6f0102022510cf2abc2e4c6c5a437cba9831662c9f112e61e2a6ced8ce63b3de18cb9cc99ae21e' - cy.get('#eip-7594-c .4844-box') - .find('p') - .should('contain.text', text, { timeout: 25000 }) + cy.get('#eip-7594-c .4844-box').find('p').should('contain.text', text, { timeout: 25000 }) text = '0x8d90ed38068f3561132d9264db8c8dfb5237af24a6e28c3d4e72d3ad8d51d97be5733ddc382c9718822cb29ccc26364e' - cy.get('#eip-7594-c .7594-box') - .find('p:first') - .should('contain.text', text, { timeout: 25000 }) + cy.get('#eip-7594-c .7594-box').find('p:first').should('contain.text', text, { timeout: 25000 }) }) }) diff --git a/cypress/e2e/eip7883_Precompile_R.cy.ts b/cypress/e2e/eip7883_Precompile_R.cy.ts index 89824bc..018616e 100644 --- a/cypress/e2e/eip7883_Precompile_R.cy.ts +++ b/cypress/e2e/eip7883_Precompile_R.cy.ts @@ -15,10 +15,7 @@ describe('EIP-7823/Precompile Component Tests', () => { cy.contains('h1', 'Feel Your Protocol') cy.contains('h3', 'ModExp') - cy.get('#eip-7883-c textarea', { timeout: 10000 }).should( - 'have.value', - bytesExpected, - ) + cy.get('#eip-7883-c textarea', { timeout: 10000 }).should('have.value', bytesExpected) cy.get('#eip-7883-c input').eq(0).should('have.value', '03') cy.get('#eip-7883-c input').eq(1).should('have.value', '03') cy.get('#eip-7883-c input').eq(2).should('have.value', '02') @@ -94,7 +91,8 @@ describe('EIP-7823/Precompile Component Tests', () => { cy.get('input').eq(2).should('have.value', '0202') // examples - cy.get('.examples-select').select('Simple') + cy.get('.e-select').click() + cy.contains('[role="option"]', 'Simple').click() cy.get('input').eq(0).should('have.value', '03') cy.get('input').eq(1).should('have.value', '03') cy.get('input').eq(2).should('have.value', '02') diff --git a/cypress/e2e/eip7951.cy.ts b/cypress/e2e/eip7951.cy.ts index 2a447ed..342baf7 100644 --- a/cypress/e2e/eip7951.cy.ts +++ b/cypress/e2e/eip7951.cy.ts @@ -17,9 +17,8 @@ describe('EIP-7951/secp256r1 Precompile Support', () => { cy.get('.post-hardfork').find('p').eq(0).should('include.text', '6900 Gas') // Select different example - cy.get('#eip-7951-c') - .find('select') - .select('Invalid (Wycheproof), r value too large') + cy.get('#eip-7951-c .e-select').click() + cy.contains('[role="option"]', 'Invalid (Wycheproof), r value too large').click() cy.get('#eip-7951-c textarea', { timeout: 10000 }).should( 'contain.value', '532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25', diff --git a/cypress/e2e/sites.cy.ts b/cypress/e2e/sites.cy.ts new file mode 100644 index 0000000..35d3e6b --- /dev/null +++ b/cypress/e2e/sites.cy.ts @@ -0,0 +1,55 @@ +describe('Home', () => { + it('loads and displays topics and explorations', () => { + cy.visit('/') + cy.contains('h2', 'Fusaka').should('be.visible') + cy.get('.exploration-c').should('have.length.gte', 1) + }) + + it('topic link navigates to topic page', () => { + cy.visit('/') + cy.contains('h2', 'Fusaka').closest('a').click() + cy.url().should('include', '/fusaka') + }) + + it('exploration card navigates to exploration page', () => { + cy.visit('/') + cy.get('.exploration-c').first().closest('a').click() + cy.url().should('match', /\/eip-\d+/) + }) +}) + +describe('Topic (Fusaka)', () => { + it('loads all exploration widgets', () => { + cy.visit('/fusaka') + cy.get('#eip-7883-c', { timeout: 10000 }).should('exist') + cy.get('#eip-7594-c', { timeout: 10000 }).should('exist') + cy.get('#eip-7951-c', { timeout: 10000 }).should('exist') + }) +}) + +describe('Imprint', () => { + it('loads and shows key sections', () => { + cy.visit('/imprint') + cy.contains('h3', 'CONTACT') + cy.contains('h3', 'ACKNOWLEDGEMENTS') + cy.contains('h3', 'DATA') + }) +}) + +describe('Navigation', () => { + it('full navigation flow through the site', () => { + cy.visit('/') + cy.get('header').contains('Feel Your Protocol') + cy.get('footer').contains('Imprint') + + cy.get('#exploration-navi').click() + cy.contains('[role="option"]', 'EIP-7883').click() + cy.url().should('include', '/eip-7883') + + cy.get('footer').contains('Imprint').click() + cy.url().should('include', '/imprint') + + cy.contains('h1', 'Feel Your Protocol').click() + cy.url().should('eq', Cypress.config().baseUrl + '/') + }) +}) diff --git a/cypress/e2e/sites/home.cy.ts b/cypress/e2e/sites/home.cy.ts deleted file mode 100644 index 8bcd0d1..0000000 --- a/cypress/e2e/sites/home.cy.ts +++ /dev/null @@ -1,22 +0,0 @@ -describe('Home Page', () => { - beforeEach(() => { - cy.visit('/') - }) - - it('renders intro and project overview', () => { - cy.contains('Interactive Ethereum Protocol Explorations') - cy.contains('About the Project') - cy.contains('Feel Your Protocol is a collaborative open-source project') - }) - - it('renders topic box with dancer', () => { - cy.contains('h2', 'Fusaka') - cy.get('img').should('exist') - }) - - it('featured exploration boxes have entries and links work', () => { - cy.get('.exploration-c').should('have.length.gte', 1) - cy.get('.exploration-c').first().parents('a').click() - cy.url().should('include', '/eip-') - }) -}) diff --git a/cypress/e2e/sites/imprint.cy.ts b/cypress/e2e/sites/imprint.cy.ts deleted file mode 100644 index 0b9cbf0..0000000 --- a/cypress/e2e/sites/imprint.cy.ts +++ /dev/null @@ -1,22 +0,0 @@ -describe('Imprint Page', () => { - beforeEach(() => { - cy.visit('/imprint') - }) - - it('renders contact section', () => { - cy.contains('h3', 'CONTACT') - cy.contains('Holger Drewes') - }) - - it('renders acknowledgements', () => { - cy.contains('h3', 'ACKNOWLEDGEMENTS') - cy.contains('li', 'EthereumJS') - cy.contains('li', 'Midjourney') - cy.contains('li', 'Tailwind') - }) - - it('renders data privacy section', () => { - cy.contains('h3', 'DATA') - cy.contains('does not collect any personal data') - }) -}) diff --git a/cypress/e2e/sites/structure.cy.ts b/cypress/e2e/sites/structure.cy.ts deleted file mode 100644 index fc90d74..0000000 --- a/cypress/e2e/sites/structure.cy.ts +++ /dev/null @@ -1,45 +0,0 @@ -describe('Site Structure', () => { - it('header renders on every page', () => { - for (const path of ['/', '/fusaka', '/eip-7883-modexp-gas-cost-increase', '/imprint']) { - cy.visit(path) - cy.get('header').within(() => { - cy.contains('h1', 'Feel Your Protocol') - cy.get('nav').should('exist') - }) - } - }) - - it('footer renders on every page', () => { - for (const path of ['/', '/fusaka', '/eip-7883-modexp-gas-cost-increase', '/imprint']) { - cy.visit(path) - cy.get('footer').within(() => { - cy.contains('a', 'Imprint') - cy.contains('a', 'GitHub') - }) - } - }) - - it('navigation links work', () => { - cy.visit('/') - - cy.contains('h2', 'Fusaka').click() - cy.url().should('include', '/fusaka') - - cy.get('#exploration-navi').select('EIP-7883') - cy.url().should('include', '/eip-7883') - - cy.get('footer').contains('Imprint').click() - cy.url().should('include', '/imprint') - - cy.contains('h1', 'Feel Your Protocol').click() - cy.url().should('eq', Cypress.config().baseUrl + '/') - }) - - it('exploration dropdown lists all explorations', () => { - cy.visit('/') - cy.get('#exploration-navi option').should('have.length.gte', 4) - cy.get('#exploration-navi').contains('EIP-7594') - cy.get('#exploration-navi').contains('EIP-7883') - cy.get('#exploration-navi').contains('EIP-7951') - }) -}) diff --git a/cypress/e2e/sites/topic.cy.ts b/cypress/e2e/sites/topic.cy.ts deleted file mode 100644 index 1cd02ef..0000000 --- a/cypress/e2e/sites/topic.cy.ts +++ /dev/null @@ -1,35 +0,0 @@ -describe('Topic Page (Fusaka)', () => { - beforeEach(() => { - cy.visit('/fusaka') - }) - - it('renders topic title and external link', () => { - cy.contains('Fusaka') - cy.get('a[href="https://forkcast.org/upgrade/fusaka"]').should('exist') - }) - - it('displays all topic EIP widgets', () => { - cy.get('#eip-7883-c', { timeout: 10000 }).should('exist') - cy.get('#eip-7594-c', { timeout: 10000 }).should('exist') - cy.get('#eip-7951-c', { timeout: 10000 }).should('exist') - }) - - it('EIP widgets show titles', () => { - cy.get('#eip-7883-c').contains('h3', 'ModExp') - cy.get('#eip-7951-c').contains('h3', 'secp256r1') - cy.get('#eip-7594-c').contains('h3', 'Peer Data') - }) - - it('EIP widgets have EIP links', () => { - cy.get('#eip-7883-c .visit-exploration-button') - .should('have.attr', 'href') - .and('include', 'eip-7883') - cy.get('#eip-7951-c .visit-exploration-button') - .should('have.attr', 'href') - .and('include', 'eip-7951') - }) - - it('renders dancer image', () => { - cy.get('img').should('exist') - }) -}) diff --git a/dist/docs/404.html b/dist/docs/404.html index 668e0d9..5083a72 100644 --- a/dist/docs/404.html +++ b/dist/docs/404.html @@ -9,14 +9,14 @@ - +
- + \ No newline at end of file diff --git a/dist/docs/assets/app.CxFGIg_I.js b/dist/docs/assets/app.CxFGIg_I.js deleted file mode 100644 index 62f0438..0000000 --- a/dist/docs/assets/app.CxFGIg_I.js +++ /dev/null @@ -1 +0,0 @@ -import{t as p}from"./chunks/theme.B2b_1z_T.js";import{B as o,a6 as i,a7 as u,a8 as c,a9 as l,aa as f,ab as d,ac as m,ad as h,ae as g,af as A,d as v,u as y,p as C,x as P,ag as b,ah as w,ai as E,aj as R}from"./chunks/framework.DyoGesZS.js";function r(e){if(e.extends){const a=r(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const n=r(p),S=v({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=y();return C(()=>{P(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&b(),w(),E(),n.setup&&n.setup(),()=>R(n.Layout)}});async function T(){globalThis.__VITEPRESS__=!0;const e=x(),a=j();a.provide(u,e);const t=c(e.route);return a.provide(l,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),n.enhanceApp&&await n.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function j(){return A(S)}function x(){let e=o;return h(a=>{let t=g(a),s=null;return t&&(e&&(t=t.replace(/\.js$/,".lean.js")),s=import(t)),o&&(e=!1),s},n.NotFound)}o&&T().then(({app:e,router:a,data:t})=>{a.go(location.href,{initialLoad:!0}).then(()=>{i(a.route,t.site),e.mount("#app")})});export{T as createApp}; diff --git a/dist/docs/assets/chunks/@localSearchIndexroot.CMtAquuL.js b/dist/docs/assets/chunks/@localSearchIndexroot.CMtAquuL.js deleted file mode 100644 index c281603..0000000 --- a/dist/docs/assets/chunks/@localSearchIndexroot.CMtAquuL.js +++ /dev/null @@ -1 +0,0 @@ -const t='{"documentCount":40,"nextId":40,"documentIds":{"0":"/contributing/adding-an-exploration.html#adding-an-exploration","1":"/contributing/adding-an-exploration.html#overview","2":"/contributing/adding-an-exploration.html#step-1-create-the-exploration-folder","3":"/contributing/adding-an-exploration.html#step-2-create-info-ts","4":"/contributing/adding-an-exploration.html#step-3-create-myc-vue","5":"/contributing/adding-an-exploration.html#step-4-register-in-the-registry","6":"/contributing/adding-an-exploration.html#step-5-add-library-dependencies","7":"/contributing/adding-an-exploration.html#step-6-add-tests","8":"/contributing/adding-an-exploration.html#step-7-build-and-verify","9":"/contributing/adding-an-exploration.html#quick-checklist","10":"/contributing/how-to-contribute.html#how-to-contribute","11":"/contributing/how-to-contribute.html#ways-to-contribute","12":"/contributing/how-to-contribute.html#development-workflow","13":"/contributing/how-to-contribute.html#code-style","14":"/contributing/how-to-contribute.html#documentation","15":"/guide/architecture.html#architecture","16":"/guide/architecture.html#overview","17":"/guide/architecture.html#tech-stack","18":"/guide/architecture.html#content-structure","19":"/guide/architecture.html#the-src-explorations-folder","20":"/guide/architecture.html#exploration-metadata-info-ts","21":"/guide/architecture.html#key-design-decisions","22":"/guide/architecture.html#folder-per-exploration","23":"/guide/architecture.html#dynamic-views","24":"/guide/architecture.html#route-level-code-splitting","25":"/guide/architecture.html#library-forks","26":"/guide/getting-started.html#getting-started","27":"/guide/getting-started.html#what-is-feel-your-protocol","28":"/guide/getting-started.html#prerequisites","29":"/guide/getting-started.html#setup","30":"/guide/getting-started.html#development","31":"/guide/getting-started.html#building","32":"/guide/getting-started.html#project-structure","33":"/guide/library-forks.html#library-forks","34":"/guide/library-forks.html#why-forks","35":"/guide/library-forks.html#how-forks-work","36":"/guide/library-forks.html#npm-package-aliases","37":"/guide/library-forks.html#monorepo-libraries","38":"/guide/library-forks.html#per-route-isolation","39":"/guide/library-forks.html#adding-a-new-fork"},"fieldIds":{"title":0,"titles":1,"text":2},"fieldLength":{"0":[3,1,25],"1":[1,3,31],"2":[6,3,31],"3":[5,3,106],"4":[5,3,83],"5":[6,3,42],"6":[5,3,47],"7":[4,3,26],"8":[5,3,12],"9":[2,3,32],"10":[3,1,25],"11":[3,3,36],"12":[2,3,29],"13":[2,3,16],"14":[1,3,24],"15":[1,1,25],"16":[1,1,33],"17":[2,1,34],"18":[2,1,53],"19":[4,3,81],"20":[5,3,57],"21":[3,1,1],"22":[3,4,41],"23":[2,4,46],"24":[4,4,43],"25":[2,4,31],"26":[2,1,25],"27":[6,2,71],"28":[1,2,11],"29":[1,2,17],"30":[1,2,9],"31":[1,2,14],"32":[2,2,77],"33":[2,1,25],"34":[3,2,53],"35":[3,2,1],"36":[3,4,44],"37":[2,4,38],"38":[3,4,29],"39":[4,2,50]},"averageFieldLength":[2.9000000000000004,2.5,36.85000000000001],"storedFields":{"0":{"title":"Adding an Exploration","titles":[]},"1":{"title":"Overview","titles":["Adding an Exploration"]},"2":{"title":"Step 1: Create the Exploration Folder","titles":["Adding an Exploration"]},"3":{"title":"Step 2: Create info.ts","titles":["Adding an Exploration"]},"4":{"title":"Step 3: Create MyC.vue","titles":["Adding an Exploration"]},"5":{"title":"Step 4: Register in the Registry","titles":["Adding an Exploration"]},"6":{"title":"Step 5: Add Library Dependencies","titles":["Adding an Exploration"]},"7":{"title":"Step 6: Add Tests","titles":["Adding an Exploration"]},"8":{"title":"Step 7: Build and Verify","titles":["Adding an Exploration"]},"9":{"title":"Quick Checklist","titles":["Adding an Exploration"]},"10":{"title":"How to Contribute","titles":[]},"11":{"title":"Ways to Contribute","titles":["How to Contribute"]},"12":{"title":"Development Workflow","titles":["How to Contribute"]},"13":{"title":"Code Style","titles":["How to Contribute"]},"14":{"title":"Documentation","titles":["How to Contribute"]},"15":{"title":"Architecture","titles":[]},"16":{"title":"Overview","titles":["Architecture"]},"17":{"title":"Tech Stack","titles":["Architecture"]},"18":{"title":"Content Structure","titles":["Architecture"]},"19":{"title":"The src/explorations/ Folder","titles":["Architecture","Content Structure"]},"20":{"title":"Exploration Metadata (info.ts)","titles":["Architecture","Content Structure"]},"21":{"title":"Key Design Decisions","titles":["Architecture"]},"22":{"title":"Folder-per-Exploration","titles":["Architecture","Key Design Decisions"]},"23":{"title":"Dynamic Views","titles":["Architecture","Key Design Decisions"]},"24":{"title":"Route-Level Code Splitting","titles":["Architecture","Key Design Decisions"]},"25":{"title":"Library Forks","titles":["Architecture","Key Design Decisions"]},"26":{"title":"Getting Started","titles":[]},"27":{"title":"What is Feel Your Protocol?","titles":["Getting Started"]},"28":{"title":"Prerequisites","titles":["Getting Started"]},"29":{"title":"Setup","titles":["Getting Started"]},"30":{"title":"Development","titles":["Getting Started"]},"31":{"title":"Building","titles":["Getting Started"]},"32":{"title":"Project Structure","titles":["Getting Started"]},"33":{"title":"Library Forks","titles":[]},"34":{"title":"Why Forks?","titles":["Library Forks"]},"35":{"title":"How Forks Work","titles":["Library Forks"]},"36":{"title":"npm Package Aliases","titles":["Library Forks","How Forks Work"]},"37":{"title":"Monorepo Libraries","titles":["Library Forks","How Forks Work"]},"38":{"title":"Per-Route Isolation","titles":["Library Forks","How Forks Work"]},"39":{"title":"Adding a New Fork","titles":["Library Forks"]}},"dirtCount":0,"index":[["0",{"2":{"36":2}}],["^11",{"2":{"36":1}}],["^10",{"2":{"36":1}}],["→",{"2":{"31":2}}],["json",{"2":{"36":2}}],["js",{"2":{"28":2}}],["just",{"2":{"27":1}}],["$",{"2":{"24":1}}],["`",{"2":{"24":1}}],["└──",{"2":{"19":4,"32":9}}],["│",{"2":{"19":4,"32":48}}],["├──",{"2":{"19":9,"32":21}}],["zero",{"2":{"18":1}}],["quot",{"2":{"18":2,"27":2}}],["quick",{"0":{"9":1}}],["7951",{"2":{"19":1,"32":1}}],["7883",{"2":{"19":1,"27":1,"32":1}}],["7594",{"2":{"19":1,"32":1}}],["7",{"0":{"8":1}}],["6",{"0":{"7":1}}],["keeps",{"2":{"6":1,"23":1}}],["key",{"0":{"21":1},"1":{"22":1,"23":1,"24":1,"25":1},"2":{"6":1}}],["5",{"0":{"6":1}}],["rather",{"2":{"34":1}}],["running",{"2":{"27":1}}],["runs",{"2":{"16":1,"34":1}}],["run",{"2":{"8":3,"9":1,"12":4,"13":4,"14":1,"30":2,"31":2}}],["rule",{"2":{"6":1}}],["routes",{"2":{"19":1}}],["route",{"0":{"24":1,"38":1},"2":{"5":2,"17":1,"19":1,"23":1,"25":1,"32":1}}],["router",{"2":{"5":1,"17":1,"19":1,"32":2}}],["releases",{"2":{"34":1}}],["released",{"2":{"34":1}}],["related",{"2":{"27":1,"32":1}}],["resolved",{"2":{"37":1}}],["research",{"2":{"18":1}}],["result",{"2":{"4":1}}],["represents",{"2":{"18":1}}],["repos",{"2":{"39":1}}],["repository",{"2":{"12":1,"29":1}}],["report",{"2":{"11":1}}],["real",{"2":{"16":1,"27":1,"34":1}}],["reading",{"2":{"27":1}}],["reads",{"2":{"5":1,"19":1}}],["readable",{"2":{"3":1,"20":1}}],["require",{"2":{"25":1}}],["required",{"2":{"3":1}}],["request",{"2":{"12":1}}],["registration",{"2":{"5":1,"19":1}}],["registry",{"0":{"5":1},"2":{"3":1,"5":1,"9":1,"19":2,"20":1,"22":1,"32":2}}],["register",{"0":{"5":1}}],["renders",{"2":{"4":1}}],["reference",{"2":{"3":1,"39":1}}],["4",{"0":{"5":1}}],["versions",{"2":{"36":1}}],["version",{"2":{"34":2,"36":1}}],["verify",{"0":{"8":1},"2":{"7":1,"8":1}}],["v22",{"2":{"28":1}}],["v20",{"2":{"28":1}}],["v4",{"2":{"17":1}}],["visits",{"2":{"38":1}}],["visit",{"2":{"24":1}}],["view",{"2":{"23":1}}],["views",{"0":{"23":1},"2":{"23":1,"32":2}}],["vitepress",{"2":{"14":1,"17":1,"32":1}}],["vite",{"2":{"6":1,"16":1,"17":1,"38":1}}],["via",{"2":{"4":1,"6":1,"18":1}}],["v",{"2":{"4":1}}],["vue`",{"2":{"24":1}}],["vue",{"0":{"4":1},"2":{"1":1,"4":4,"6":2,"9":1,"16":1,"17":3,"19":5,"22":1,"23":3,"24":2,"32":10,"38":1,"39":1}}],[">",{"2":{"4":4}}],["3",{"0":{"4":1},"2":{"16":1,"17":1}}],["yet",{"2":{"34":1}}],["yes",{"2":{"3":4}}],["you",{"2":{"1":1,"4":1,"6":1,"27":3,"36":2,"39":1}}],["your",{"0":{"27":1},"2":{"0":1,"2":1,"3":1,"4":3,"6":3,"7":1,"10":1,"12":1,"15":1,"16":1,"26":1,"27":1,"33":1,"34":1,"39":3}}],["upstream",{"2":{"34":1,"39":1}}],["upcoming",{"2":{"18":1,"27":1}}],["utilities",{"2":{"32":1}}],["utility",{"2":{"32":1}}],["ui",{"2":{"4":2,"11":1,"32":3}}],["url",{"2":{"3":1,"39":1}}],["unaffected",{"2":{"38":1}}],["unit",{"2":{"12":2,"18":1}}],["unique",{"2":{"3":1}}],["under",{"2":{"0":2,"1":1,"10":2,"15":2,"26":2,"33":2}}],["using",{"2":{"23":1,"36":1}}],["usage",{"2":{"3":1,"4":1,"20":1}}],["usagetext",{"2":{"3":2,"19":1,"20":1}}],["user",{"2":{"38":1}}],["users",{"2":{"24":1}}],["used",{"2":{"34":1}}],["uses",{"2":{"24":1}}],["use",{"2":{"3":1,"4":1,"37":1}}],["+",{"2":{"3":1,"12":1}}],["x3c",{"2":{"3":2,"4":14}}],["xxxx",{"2":{"2":3,"3":4,"4":1,"5":1,"7":1,"20":3}}],["homeview",{"2":{"32":1}}],["how",{"0":{"10":1,"35":1},"1":{"11":1,"12":1,"13":1,"14":1,"36":1,"37":1,"38":1},"2":{"3":1}}],["has",{"2":{"37":1}}],["hands",{"2":{"27":1}}],["hardfork",{"2":{"18":1,"27":1}}],["haven",{"2":{"34":1}}],["have",{"2":{"11":1,"39":1}}],["helper",{"2":{"32":1}}],["heart",{"2":{"19":1}}],["heroicons",{"2":{"17":2}}],["here",{"2":{"4":2}}],["hex",{"2":{"4":1}}],["html",{"2":{"3":2,"20":2}}],["https",{"2":{"3":2,"20":2,"29":1}}],["href",{"2":{"3":2,"20":1}}],["human",{"2":{"3":1,"20":1}}],["hyphen",{"2":{"2":1}}],["pointing",{"2":{"39":1}}],["powered",{"2":{"19":1,"32":1}}],["poweredby=",{"2":{"4":1}}],["poweredbyc",{"2":{"4":3,"19":1,"32":1}}],["poweredby",{"2":{"3":2,"4":3,"20":1}}],["per",{"0":{"22":1,"38":1},"2":{"23":2,"32":1}}],["pull",{"2":{"12":1}}],["pre",{"2":{"37":1}}],["precompile",{"2":{"32":1,"34":1}}],["precompiles",{"2":{"4":1,"32":1}}],["prerequisites",{"0":{"28":1}}],["preview",{"2":{"14":1}}],["prettier",{"2":{"13":1}}],["producing",{"2":{"37":1}}],["production",{"2":{"8":1,"17":1}}],["provide",{"2":{"4":1}}],["project",{"0":{"32":1},"2":{"0":1,"10":1,"15":1,"19":1,"26":1,"33":1}}],["protocol",{"0":{"27":1},"2":{"0":1,"3":1,"10":1,"15":1,"16":2,"18":1,"26":1,"27":4,"33":1,"34":1}}],["p>",{"2":{"4":1}}],["p>widget",{"2":{"4":1}}],["packages",{"2":{"39":1}}],["package",{"0":{"36":1},"2":{"36":1,"37":1,"39":1}}],["passes",{"2":{"9":1}}],["paragraph",{"2":{"3":1}}],["pages",{"2":{"34":1,"38":1}}],["page",{"2":{"3":1,"24":1,"27":2,"38":1,"39":1}}],["path",{"2":{"3":3,"19":1,"20":1}}],["=",{"2":{"3":1,"4":2,"5":1,"20":1,"24":2}}],["2",{"0":{"3":1}}],["generic",{"2":{"32":1}}],["getting",{"0":{"26":1},"1":{"27":1,"28":1,"29":1,"30":1,"31":1,"32":1}}],["gets",{"2":{"16":1,"27":1}}],["git",{"2":{"29":1,"39":1}}],["github",{"2":{"3":1,"20":1,"29":1}}],["gas",{"2":{"27":1,"34":1}}],["glob",{"2":{"23":1,"24":2}}],["grouped",{"2":{"27":1}}],["group",{"2":{"18":1}}],["gt",{"2":{"9":2,"17":1}}],["goes",{"2":{"4":1}}],["g",{"2":{"2":1,"3":1,"7":1,"18":1,"27":1}}],["guides",{"2":{"11":1}}],["guide",{"2":{"1":1,"6":1}}],["lets",{"2":{"27":2}}],["lean",{"2":{"23":1}}],["level",{"0":{"24":1},"2":{"17":1}}],["logic",{"2":{"32":1}}],["loaded",{"2":{"24":1,"38":1}}],["loading",{"2":{"24":1}}],["loads",{"2":{"23":1}}],["locally",{"2":{"14":1}}],["lowercase",{"2":{"2":1}}],["lf",{"2":{"12":1,"13":1}}],["lt",{"2":{"9":2,"17":1}}],["lazy",{"2":{"24":1}}],["lang=",{"2":{"4":1}}],["layout",{"2":{"4":1}}],["like",{"2":{"37":1}}],["lib",{"2":{"32":1}}],["libraries",{"0":{"37":1},"2":{"6":1,"24":1,"25":1,"37":1}}],["library",{"0":{"6":1,"25":1,"33":1},"1":{"34":1,"35":1,"36":1,"37":1,"38":1,"39":1},"2":{"3":1,"6":4,"9":1,"16":1,"20":1,"25":2,"27":1,"34":2,"36":1,"39":1}}],["lists",{"2":{"23":1}}],["live",{"2":{"14":1,"18":1}}],["lives",{"2":{"1":1}}],["lint",{"2":{"12":1,"13":1}}],["linting",{"2":{"12":1,"13":1}}],["line",{"2":{"5":1}}],["links",{"2":{"32":1}}],["link",{"2":{"3":1,"4":1}}],["boilerplate",{"2":{"23":1}}],["both",{"2":{"0":1,"10":1,"13":1,"15":1,"26":1,"31":1,"33":1}}],["by",{"2":{"18":1,"19":1,"32":1,"34":1}}],["bundled",{"2":{"37":2,"39":1}}],["buttons",{"2":{"19":1}}],["built",{"2":{"14":1,"16":1}}],["building",{"0":{"31":1}}],["builds",{"2":{"17":1,"37":1}}],["build",{"0":{"8":1},"2":{"8":2,"9":2,"31":3,"32":1,"39":1}}],["bug",{"2":{"11":2}}],["based",{"2":{"23":1}}],["basic",{"2":{"7":1}}],["bashgit",{"2":{"29":1}}],["bashnpm",{"2":{"6":1,"8":1,"12":1,"14":1,"30":2,"31":1}}],["bashmkdir",{"2":{"2":1}}],["branch",{"2":{"12":1,"39":1}}],["browser",{"2":{"16":1,"27":1,"34":1}}],["browse",{"2":{"4":1}}],["brief",{"2":{"3":1}}],["b>",{"2":{"3":1}}],["b>what",{"2":{"3":1}}],["been",{"2":{"34":1}}],["better",{"2":{"11":1}}],["belonging",{"2":{"23":1}}],["belong",{"2":{"18":1}}],["belongs",{"2":{"3":1}}],["below",{"2":{"3":1}}],["be",{"2":{"2":1}}],["nightly",{"2":{"36":1}}],["npm",{"0":{"36":1},"2":{"8":1,"9":1,"12":2,"13":3,"28":1,"29":1,"31":1,"36":2,"39":1}}],["never",{"2":{"6":1,"39":1}}],["needs",{"2":{"6":1}}],["needed",{"2":{"5":1,"9":1,"19":1,"24":1}}],["need",{"2":{"4":1,"6":1,"34":1,"36":1}}],["new",{"0":{"39":1},"2":{"1":1,"2":1,"11":1,"22":1,"23":1,"34":1}}],["node",{"2":{"28":2}}],["no",{"2":{"3":5,"5":1,"19":1,"23":1,"37":1}}],["name",{"2":{"3":3,"7":1,"20":2,"23":1}}],["named",{"2":{"2":1}}],["cd",{"2":{"29":1}}],["clone",{"2":{"29":2}}],["clarify",{"2":{"11":1}}],["calculation",{"2":{"34":1}}],["called",{"2":{"27":1}}],["can",{"2":{"18":1,"22":1,"25":1,"27":1}}],["css",{"2":{"17":1}}],["chunk",{"2":{"24":1}}],["checklist",{"0":{"9":1}}],["changes",{"2":{"12":1,"14":1,"27":3,"34":1,"39":1}}],["change",{"2":{"0":1,"3":2,"10":1,"15":1,"16":1,"18":1,"26":1,"27":1,"33":1}}],["cy",{"2":{"7":1}}],["cypress",{"2":{"7":2,"17":1,"32":1}}],["custom",{"2":{"6":1,"25":1}}],["creating",{"2":{"22":1}}],["created",{"2":{"9":2}}],["creates",{"2":{"5":1}}],["create",{"0":{"2":1,"3":1,"4":1},"2":{"2":1,"3":1,"4":1,"12":1,"19":1,"39":2}}],["credits",{"2":{"3":1}}],["cost",{"2":{"27":1}}],["coexist",{"2":{"25":1,"36":1}}],["correct",{"2":{"23":1}}],["core",{"2":{"16":1,"18":1,"32":1}}],["code",{"0":{"13":1,"24":1},"2":{"6":2,"16":1,"17":1,"27":1,"34":1,"36":1,"38":2,"39":1}}],["config",{"2":{"32":1}}],["configuration",{"2":{"5":1}}],["conflicts",{"2":{"25":1}}],["concepts",{"2":{"18":1}}],["contains",{"2":{"20":1}}],["contained",{"2":{"19":1,"22":1}}],["contributors",{"2":{"22":1}}],["contribution",{"2":{"11":1}}],["contribute",{"0":{"10":1,"11":1},"1":{"11":1,"12":1,"13":1,"14":1}}],["content",{"0":{"18":1},"1":{"19":1,"20":1},"2":{"4":3,"18":1,"23":1,"32":1}}],["content>",{"2":{"4":1}}],["consistent",{"2":{"4":1}}],["constants",{"2":{"19":1}}],["const",{"2":{"3":1,"4":2,"5":1,"19":1,"20":1,"24":1}}],["comes",{"2":{"28":1}}],["composition",{"2":{"17":1}}],["componentmodules",{"2":{"24":2}}],["components",{"2":{"4":3,"32":4}}],["component",{"2":{"4":1,"19":1,"32":3}}],["com",{"2":{"3":1,"20":1,"29":1}}],["12+",{"2":{"28":1}}],["19+",{"2":{"28":1}}],["1",{"0":{"2":1},"2":{"36":4}}],["we",{"2":{"34":1,"37":1}}],["website",{"2":{"27":1,"29":2,"30":1,"31":3,"32":2}}],["webp",{"2":{"3":2,"20":1}}],["where",{"2":{"37":1}}],["when",{"2":{"23":1,"38":1}}],["why",{"0":{"34":1}}],["what",{"0":{"27":1}}],["work",{"0":{"35":1},"1":{"36":1,"37":1,"38":1}}],["workflow",{"0":{"12":1}}],["works",{"2":{"8":1}}],["waiting",{"2":{"34":1}}],["ways",{"0":{"11":1}}],["walks",{"2":{"1":1}}],["wrapper",{"2":{"4":2,"19":1,"32":1}}],["wiring",{"2":{"37":1}}],["widgets",{"2":{"25":1,"34":1}}],["widget",{"2":{"1":1,"3":1,"4":2,"6":1,"7":1,"9":1,"16":1,"18":1,"19":1,"22":1,"27":1,"32":1}}],["without",{"2":{"25":1}}],["with",{"2":{"1":1,"3":1,"4":1,"9":2,"13":2,"14":1,"16":1,"17":2,"22":1,"27":3,"28":1,"34":1,"37":2}}],["multiple",{"2":{"36":1}}],["meta",{"2":{"23":1,"24":2}}],["metadata",{"0":{"20":1},"2":{"1":1,"3":1,"9":1,"19":1,"20":1,"22":1,"32":1}}],["means",{"2":{"22":1}}],["modified",{"2":{"34":1}}],["modexp",{"2":{"27":1}}],["more",{"2":{"18":1}}],["most",{"2":{"11":1}}],["monorepos",{"2":{"37":1}}],["monorepo",{"0":{"37":1},"2":{"3":1,"37":2,"39":1}}],["maintain",{"2":{"34":1}}],["making",{"2":{"27":1}}],["make",{"2":{"12":1,"39":1}}],["markdown",{"2":{"14":1}}],["manual",{"2":{"5":1,"19":1}}],["matter",{"2":{"22":1}}],["material",{"2":{"3":1}}],["matches",{"2":{"3":1}}],["may",{"2":{"0":1,"10":1,"15":1,"26":1,"33":1}}],["myc",{"0":{"4":1},"2":{"1":1,"4":1,"6":2,"9":1,"19":3,"22":1,"23":1,"24":2,"32":3,"38":1,"39":1}}],["target",{"2":{"37":1}}],["targeted",{"2":{"34":1}}],["tailwind",{"2":{"17":1}}],["t",{"2":{"34":1}}],["typos",{"2":{"11":1}}],["types",{"2":{"32":2}}],["typescriptconst",{"2":{"24":1}}],["typescriptimport",{"2":{"3":1,"5":1,"20":1,"36":1}}],["typed",{"2":{"19":1}}],["type",{"2":{"3":1,"20":1}}],["tech",{"0":{"17":1}}],["testing",{"2":{"17":1}}],["test",{"2":{"7":1,"8":1,"9":1,"12":2}}],["tests",{"0":{"7":1},"2":{"8":1,"12":3,"32":1}}],["text",{"2":{"4":2}}],["template",{"2":{"4":1}}],["template>",{"2":{"4":3}}],["topicintroview",{"2":{"32":1}}],["topicview",{"2":{"23":1,"32":1}}],["topic",{"2":{"3":1,"18":1,"19":1,"23":2,"32":1}}],["topics",{"2":{"3":2,"18":5,"19":1,"20":1,"27":1,"32":1}}],["to",{"0":{"10":1,"11":1},"1":{"11":1,"12":1,"13":1,"14":1},"2":{"3":4,"4":1,"5":1,"7":1,"14":1,"18":1,"19":1,"22":1,"23":1,"25":1,"27":1,"38":1,"39":1}}],["title",{"2":{"3":4,"4":1,"19":2,"20":2,"32":1}}],["ts",{"0":{"3":1,"20":1},"2":{"1":1,"3":1,"4":2,"5":1,"7":1,"9":2,"18":1,"19":7,"20":1,"22":2,"32":5}}],["two",{"2":{"1":1,"18":1}}],["thanks",{"2":{"38":1}}],["than",{"2":{"34":1}}],["that",{"2":{"5":1,"16":1,"24":1,"25":1,"27":1,"34":2,"38":1}}],["through",{"2":{"1":1}}],["things",{"2":{"0":1,"10":1,"15":1,"26":1,"33":1}}],["this",{"2":{"0":1,"1":1,"3":2,"6":1,"10":1,"14":1,"15":1,"19":2,"22":1,"23":1,"26":1,"33":1,"39":1}}],["then",{"2":{"39":1}}],["they",{"2":{"24":1}}],["there",{"2":{"23":1}}],["them",{"2":{"19":1}}],["theme",{"2":{"18":1}}],["the",{"0":{"2":1,"5":1,"19":1},"2":{"0":1,"2":1,"3":5,"4":5,"5":2,"6":1,"10":1,"11":1,"12":1,"14":1,"15":1,"16":2,"18":2,"19":6,"23":3,"24":3,"25":2,"26":1,"27":3,"29":1,"30":2,"31":1,"32":1,"33":1,"34":1,"36":2,"37":3,"38":2,"39":3}}],["same",{"2":{"25":1,"36":1}}],["side",{"2":{"37":1}}],["single",{"2":{"22":1,"37":1,"39":1}}],["simple",{"2":{"16":1}}],["submit",{"2":{"12":1}}],["suggestion",{"2":{"11":1}}],["splitting",{"0":{"24":1},"2":{"6":1,"17":1,"38":1}}],["specific",{"2":{"6":1,"25":1,"34":1,"36":1,"38":1}}],["specifications",{"2":{"27":1}}],["specification",{"2":{"3":1}}],["some",{"2":{"6":1,"25":1,"34":1}}],["shared",{"2":{"4":1,"6":1,"19":2,"32":5,"39":1}}],["short",{"2":{"3":1,"20":1}}],["should",{"2":{"2":1}}],["slot",{"2":{"4":2}}],["several",{"2":{"37":1}}],["server",{"2":{"30":2}}],["separate",{"2":{"24":1}}],["separated",{"2":{"2":1}}],["self",{"2":{"19":1,"22":1}}],["see",{"2":{"6":1,"11":1,"25":1}}],["setup",{"0":{"29":1},"2":{"4":1,"17":1}}],["script>",{"2":{"4":1}}],["script",{"2":{"4":1,"17":1}}],["s",{"2":{"2":1,"3":1,"5":1,"6":2,"20":1,"22":1,"24":1,"38":4,"39":2}}],["structure",{"0":{"18":1,"32":1},"1":{"19":1,"20":1}}],["styling",{"2":{"17":1}}],["style",{"0":{"13":1}}],["start",{"2":{"30":2}}],["started",{"0":{"26":1},"1":{"27":1,"28":1,"29":1,"30":1,"31":1,"32":1}}],["static",{"2":{"23":1}}],["stack",{"0":{"17":1}}],["standard",{"2":{"14":1,"36":1}}],["stage",{"2":{"0":1,"10":1,"15":1,"26":1,"33":1}}],["step",{"0":{"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1}}],["src",{"0":{"19":1},"2":{"1":1,"2":2,"3":2,"4":1,"5":1,"9":3,"18":2,"19":1,"32":1}}],["output",{"2":{"32":1}}],["object",{"2":{"19":1,"20":1}}],["other",{"2":{"18":1,"38":1}}],["open",{"2":{"11":1}}],["of",{"2":{"3":2,"19":1,"22":1,"25":2,"27":1,"34":1,"36":1}}],["only",{"2":{"4":1,"6":2,"24":1,"38":2,"39":1}}],["on",{"2":{"3":1,"22":1,"23":1,"24":1,"27":1,"37":1}}],["one",{"2":{"1":1,"5":1,"22":1,"32":1}}],["organized",{"2":{"18":1}}],["org",{"2":{"3":1,"20":1}}],["or",{"2":{"2":1,"3":1,"6":1,"11":1,"18":2,"23":1,"28":1,"34":1,"39":1}}],["own",{"2":{"1":1,"16":1,"22":1,"27":1}}],["overview",{"0":{"1":1,"16":1}}],["icons",{"2":{"17":1}}],["isolation",{"0":{"38":1}}],["isolated",{"2":{"6":1,"22":1,"25":1}}],["is",{"0":{"27":1},"2":{"14":1,"16":2,"18":1,"19":2,"22":2,"24":1,"27":1,"36":1,"37":1,"38":2}}],["issue",{"2":{"11":1}}],["issues",{"2":{"11":1,"37":1}}],["if",{"2":{"6":2,"9":1,"39":1}}],["it",{"2":{"5":1,"6":2,"7":1,"27":1,"39":1}}],["its",{"2":{"1":1,"16":1,"20":1,"22":1,"25":1,"27":1,"38":1}}],["implementation",{"2":{"34":1}}],["improvements",{"2":{"11":1}}],["improve",{"2":{"11":2}}],["impactful",{"2":{"11":1}}],["imported",{"2":{"38":1}}],["imports",{"2":{"19":1}}],["import",{"2":{"4":4,"5":1,"6":2,"9":1,"22":1,"23":1,"24":2,"36":2,"39":1}}],["image",{"2":{"3":3,"20":1}}],["idea",{"2":{"16":1}}],["identifier",{"2":{"2":1,"3":1}}],["ids",{"2":{"3":1}}],["id",{"2":{"2":2,"3":2,"4":1,"5":1,"7":1,"9":2,"19":1,"20":1}}],["independent",{"2":{"36":1}}],["instead",{"2":{"23":1,"27":1}}],["install",{"2":{"6":2,"29":2,"36":1}}],["instructions",{"2":{"3":2,"20":1}}],["inputs",{"2":{"4":1}}],["internal",{"2":{"37":2}}],["interact",{"2":{"27":1}}],["interactively",{"2":{"27":1}}],["interactive",{"2":{"1":1,"3":1,"4":3,"9":1,"16":1,"18":1,"19":1,"27":2,"32":1}}],["intra",{"2":{"37":1}}],["intro",{"2":{"4":1}}],["introduction",{"2":{"3":2,"20":1}}],["introtext",{"2":{"3":2,"19":1,"20":1}}],["into",{"2":{"19":1,"27":1}}],["infourl",{"2":{"3":2,"19":1,"20":1}}],["info",{"0":{"3":1,"20":1},"2":{"1":1,"3":2,"4":5,"5":2,"9":1,"19":6,"20":2,"22":1,"32":3}}],["in",{"0":{"5":1},"2":{"0":1,"1":1,"2":1,"6":3,"7":1,"9":1,"10":1,"14":1,"15":1,"16":1,"18":2,"22":1,"26":1,"27":1,"33":1,"34":1,"36":2,"38":1,"39":3}}],["flat",{"2":{"20":1}}],["feature",{"2":{"12":1}}],["feelyourprotocol",{"2":{"29":1}}],["feel",{"0":{"27":1},"2":{"0":1,"10":1,"15":1,"16":1,"26":1,"27":1,"33":1,"34":1}}],["functions",{"2":{"32":1}}],["functionality",{"2":{"7":1}}],["fully",{"2":{"22":1,"36":1}}],["fusaka",{"2":{"3":3,"18":1,"20":2,"27":1}}],["focus",{"2":{"22":1}}],["footer",{"2":{"19":1}}],["found",{"2":{"11":1}}],["formatting",{"2":{"13":1}}],["formatted",{"2":{"3":2,"13":1,"20":2}}],["format",{"2":{"12":1,"13":1}}],["forks",{"0":{"25":1,"33":1,"34":1,"35":1},"1":{"34":1,"35":1,"36":2,"37":2,"38":2,"39":1},"2":{"6":1,"25":3,"34":1}}],["fork",{"0":{"39":1},"2":{"6":3,"12":1,"36":2,"37":1,"38":2,"39":4}}],["for",{"2":{"3":2,"4":3,"5":1,"17":5,"18":2,"24":2,"27":2,"34":2,"37":1,"39":2}}],["folder",{"0":{"2":1,"19":1,"22":1},"2":{"1":1,"2":1,"3":1,"14":1,"19":1,"22":3,"32":1}}],["fixed",{"2":{"13":1}}],["fixes",{"2":{"11":1}}],["fix",{"2":{"11":1}}],["file",{"2":{"6":1,"37":1,"39":1}}],["filename",{"2":{"3":1}}],["files",{"2":{"1":1,"14":1,"23":1}}],["field",{"2":{"3":1}}],["fields",{"2":{"3":1}}],["from",{"2":{"3":2,"4":5,"5":2,"19":1,"20":1,"36":2,"37":1}}],["frequently",{"2":{"0":1,"10":1,"15":1,"26":1,"33":1}}],["esm",{"2":{"37":2}}],["eslint",{"2":{"13":1}}],["evmexp",{"2":{"36":1}}],["evm",{"2":{"36":7}}],["ensures",{"2":{"25":1}}],["entry",{"2":{"9":1}}],["e2e",{"2":{"7":2,"8":2,"9":1,"12":2,"17":1,"32":1}}],["example",{"2":{"27":1,"34":1}}],["examples",{"2":{"4":1,"11":1}}],["existing",{"2":{"4":1,"5":1,"11":1,"34":1}}],["experimental",{"2":{"34":1,"36":2}}],["experiment",{"2":{"27":1}}],["explore",{"2":{"27":1}}],["explorationid",{"2":{"24":1}}],["explorationid=",{"2":{"4":1}}],["explorationview",{"2":{"23":1,"24":1,"32":1}}],["exploration=",{"2":{"4":1}}],["explorationcomponent",{"2":{"24":1}}],["explorationc>",{"2":{"4":1}}],["explorationc",{"2":{"4":5,"19":1,"32":1}}],["explorations",{"0":{"19":1},"2":{"1":1,"2":2,"3":1,"4":2,"5":5,"9":3,"18":5,"19":4,"23":1,"24":2,"27":1,"32":2}}],["exploration",{"0":{"0":1,"2":1,"20":1,"22":1},"1":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1},"2":{"1":1,"2":1,"3":5,"4":4,"6":1,"7":1,"11":3,"18":2,"19":2,"20":3,"22":3,"23":1,"24":1,"25":2,"27":1,"32":3,"34":2,"38":2,"39":1}}],["explanations",{"2":{"11":1}}],["exports",{"2":{"19":2}}],["export",{"2":{"3":1,"5":1,"20":1}}],["etc",{"2":{"4":1}}],["ethereumjs",{"2":{"3":3,"36":5,"37":1}}],["ethereum",{"2":{"3":1,"6":1,"16":1,"20":1,"25":1,"27":2,"34":1}}],["erc",{"2":{"2":1,"18":1}}],["eipxxxx",{"2":{"5":3}}],["eips",{"2":{"3":2,"20":2}}],["eip",{"2":{"2":2,"3":4,"4":1,"5":1,"7":1,"18":1,"19":3,"20":3,"27":1,"32":3}}],["e",{"2":{"2":1,"3":1,"7":1,"18":1,"27":1}}],["easy",{"2":{"27":1}}],["each",{"2":{"1":1,"6":1,"16":1,"18":2,"19":2,"20":1,"22":2,"24":1,"25":1,"27":1,"36":1,"38":1}}],["early",{"2":{"0":1,"10":1,"15":1,"26":1,"33":1}}],["dynamically",{"2":{"23":2}}],["dynamic",{"0":{"23":1}}],["dist",{"2":{"31":2,"32":1}}],["discover",{"2":{"27":1}}],["displays",{"2":{"4":1}}],["display",{"2":{"3":1}}],["directly",{"2":{"27":1}}],["different",{"2":{"25":1}}],["dictionary",{"2":{"19":1}}],["dict",{"2":{"19":1}}],["div>",{"2":{"4":2}}],["download",{"2":{"24":1}}],["document",{"2":{"39":1}}],["documentation",{"0":{"14":1},"2":{"0":1,"10":1,"11":1,"14":1,"15":1,"17":1,"26":1,"31":1,"32":1,"33":1}}],["docs",{"2":{"14":4,"30":2,"31":3,"32":2}}],["does",{"2":{"3":1}}],["dedicated",{"2":{"27":1}}],["demand",{"2":{"24":1}}],["decisions",{"0":{"21":1},"1":{"22":1,"23":1,"24":1,"25":1}}],["design",{"0":{"21":1},"1":{"22":1,"23":1,"24":1,"25":1}}],["description",{"2":{"3":2,"19":1,"20":1,"32":1}}],["descriptive",{"2":{"2":1}}],["defineasynccomponent",{"2":{"23":1,"24":1}}],["defined",{"2":{"18":1}}],["definitions",{"2":{"19":1,"32":1}}],["dev",{"2":{"14":1,"30":4}}],["development",{"0":{"12":1,"30":1},"2":{"0":2,"10":2,"15":2,"17":1,"26":2,"33":2}}],["dependencies",{"0":{"6":1},"2":{"6":1,"9":1,"22":1,"29":1,"36":1,"37":2}}],["access",{"2":{"39":1}}],["active",{"2":{"0":2,"10":2,"15":2,"26":2,"33":2}}],["alias",{"2":{"36":1,"39":1}}],["aliases",{"0":{"36":1},"2":{"36":1}}],["all",{"2":{"19":2,"20":1,"23":1,"37":1}}],["avoids",{"2":{"23":1}}],["api",{"2":{"17":1}}],["application",{"2":{"16":1}}],["auto",{"2":{"13":2}}],["automatically",{"2":{"4":1,"5":1,"19":1}}],["added",{"2":{"9":3}}],["add",{"0":{"6":1,"7":1},"2":{"5":1,"7":1,"11":2,"39":1}}],["adding",{"0":{"0":1,"39":1},"1":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1},"2":{"1":1,"11":1,"22":2,"23":1}}],["assembles",{"2":{"19":2}}],["assets",{"2":{"3":1}}],["as",{"2":{"4":1,"5":1,"14":1,"19":1,"20":1,"36":1}}],["around",{"2":{"18":1}}],["architecture",{"0":{"15":1},"1":{"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1},"2":{"25":1}}],["array",{"2":{"3":2,"18":1}}],["are",{"2":{"0":1,"10":1,"15":1,"18":1,"22":1,"23":1,"26":1,"27":1,"33":1,"34":1,"38":1}}],["after",{"2":{"2":1,"7":1}}],["a",{"0":{"39":1},"2":{"1":1,"2":1,"3":1,"6":2,"7":1,"11":3,"12":2,"16":1,"18":1,"19":2,"20":1,"22":4,"23":1,"24":1,"27":1,"34":3,"36":1,"37":1,"39":2}}],["any",{"2":{"2":1}}],["and",{"0":{"8":1},"2":{"0":2,"1":1,"2":1,"4":2,"5":1,"6":1,"9":1,"10":2,"12":1,"15":2,"17":2,"19":1,"22":2,"23":2,"26":2,"29":1,"31":1,"32":3,"33":2}}],["an",{"0":{"0":1},"1":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1},"2":{"0":1,"6":1,"10":1,"11":3,"15":1,"18":2,"26":1,"27":2,"33":1,"34":1,"39":1}}]],"serializationVersion":2}';export{t as default}; diff --git a/dist/docs/assets/chunks/VPLocalSearchBox.BOPGMrlA.js b/dist/docs/assets/chunks/VPLocalSearchBox.BOPGMrlA.js deleted file mode 100644 index 9394177..0000000 --- a/dist/docs/assets/chunks/VPLocalSearchBox.BOPGMrlA.js +++ /dev/null @@ -1,3 +0,0 @@ -import{Z as _t,z as Le,h as Se,al as Et,am as qe,an as Tt,ao as It,ap as kt,C as ne,d as Nt,aq as He,y as ue,ar as Ft,as as Rt,x as At,at as Ot,p as Ne,U as de,S as me,au as Ct,av as Mt,$ as Lt,B as Dt,a3 as Pt,b as zt,o as B,j as T,a4 as Vt,aw as jt,k as C,ax as $t,ay as Bt,c as q,e as ge,n as Ge,H as Qe,G as Ye,a as fe,t as he,az as Wt,aA as Ze,aB as Kt,a9 as Jt,ae as Ut,aC as qt,_ as Ht}from"./framework.DyoGesZS.js";import{u as lt}from"./theme.B2b_1z_T.js";const Gt={root:()=>_t(()=>import("./@localSearchIndexroot.CMtAquuL.js"),[])};var ct=["input:not([inert]):not([inert] *)","select:not([inert]):not([inert] *)","textarea:not([inert]):not([inert] *)","a[href]:not([inert]):not([inert] *)","button:not([inert]):not([inert] *)","[tabindex]:not(slot):not([inert]):not([inert] *)","audio[controls]:not([inert]):not([inert] *)","video[controls]:not([inert]):not([inert] *)",'[contenteditable]:not([contenteditable="false"]):not([inert]):not([inert] *)',"details>summary:first-of-type:not([inert]):not([inert] *)","details:not([inert]):not([inert] *)"],xe=ct.join(","),ut=typeof Element>"u",Z=ut?function(){}:Element.prototype.matches||Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector,_e=!ut&&Element.prototype.getRootNode?function(a){var e;return a==null||(e=a.getRootNode)===null||e===void 0?void 0:e.call(a)}:function(a){return a?.ownerDocument},Ee=function(e,t){var n;t===void 0&&(t=!0);var r=e==null||(n=e.getAttribute)===null||n===void 0?void 0:n.call(e,"inert"),i=r===""||r==="true",s=i||t&&e&&(typeof e.closest=="function"?e.closest("[inert]"):Ee(e.parentNode));return s},Qt=function(e){var t,n=e==null||(t=e.getAttribute)===null||t===void 0?void 0:t.call(e,"contenteditable");return n===""||n==="true"},dt=function(e,t,n){if(Ee(e))return[];var r=Array.prototype.slice.apply(e.querySelectorAll(xe));return t&&Z.call(e,xe)&&r.unshift(e),r=r.filter(n),r},Te=function(e,t,n){for(var r=[],i=Array.from(e);i.length;){var s=i.shift();if(!Ee(s,!1))if(s.tagName==="SLOT"){var o=s.assignedElements(),l=o.length?o:s.children,c=Te(l,!0,n);n.flatten?r.push.apply(r,c):r.push({scopeParent:s,candidates:c})}else{var f=Z.call(s,xe);f&&n.filter(s)&&(t||!e.includes(s))&&r.push(s);var p=s.shadowRoot||typeof n.getShadowRoot=="function"&&n.getShadowRoot(s),v=!Ee(p,!1)&&(!n.shadowRootFilter||n.shadowRootFilter(s));if(p&&v){var y=Te(p===!0?s.children:p.children,!0,n);n.flatten?r.push.apply(r,y):r.push({scopeParent:s,candidates:y})}else i.unshift.apply(i,s.children)}}return r},ft=function(e){return!isNaN(parseInt(e.getAttribute("tabindex"),10))},Y=function(e){if(!e)throw new Error("No node provided");return e.tabIndex<0&&(/^(AUDIO|VIDEO|DETAILS)$/.test(e.tagName)||Qt(e))&&!ft(e)?0:e.tabIndex},Yt=function(e,t){var n=Y(e);return n<0&&t&&!ft(e)?0:n},Zt=function(e,t){return e.tabIndex===t.tabIndex?e.documentOrder-t.documentOrder:e.tabIndex-t.tabIndex},ht=function(e){return e.tagName==="INPUT"},Xt=function(e){return ht(e)&&e.type==="hidden"},en=function(e){var t=e.tagName==="DETAILS"&&Array.prototype.slice.apply(e.children).some(function(n){return n.tagName==="SUMMARY"});return t},tn=function(e,t){for(var n=0;nsummary:first-of-type"),o=s?e.parentElement:e;if(Z.call(o,"details:not([open]) *"))return!0;if(!n||n==="full"||n==="full-native"||n==="legacy-full"){if(typeof r=="function"){for(var l=e;e;){var c=e.parentElement,f=_e(e);if(c&&!c.shadowRoot&&r(c)===!0)return Xe(e);e.assignedSlot?e=e.assignedSlot:!c&&f!==e.ownerDocument?e=f.host:e=c}e=l}if(an(e))return!e.getClientRects().length;if(n!=="legacy-full")return!0}else if(n==="non-zero-area")return Xe(e);return!1},ln=function(e){if(/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(e.tagName))for(var t=e.parentElement;t;){if(t.tagName==="FIELDSET"&&t.disabled){for(var n=0;n=0)},pt=function(e){var t=[],n=[];return e.forEach(function(r,i){var s=!!r.scopeParent,o=s?r.scopeParent:r,l=Yt(o,s),c=s?pt(r.candidates):o;l===0?s?t.push.apply(t,c):t.push(o):n.push({documentOrder:i,tabIndex:l,item:r,isScope:s,content:c})}),n.sort(Zt).reduce(function(r,i){return i.isScope?r.push.apply(r,i.content):r.push(i.content),r},[]).concat(t)},un=function(e,t){t=t||{};var n;return t.getShadowRoot?n=Te([e],t.includeContainer,{filter:De.bind(null,t),flatten:!1,getShadowRoot:t.getShadowRoot,shadowRootFilter:cn}):n=dt(e,t.includeContainer,De.bind(null,t)),pt(n)},dn=function(e,t){t=t||{};var n;return t.getShadowRoot?n=Te([e],t.includeContainer,{filter:Ie.bind(null,t),flatten:!0,getShadowRoot:t.getShadowRoot}):n=dt(e,t.includeContainer,Ie.bind(null,t)),n},ee=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return Z.call(e,xe)===!1?!1:De(t,e)},fn=ct.concat("iframe:not([inert]):not([inert] *)").join(","),Fe=function(e,t){if(t=t||{},!e)throw new Error("No node provided");return Z.call(e,fn)===!1?!1:Ie(t,e)};function Pe(a,e){(e==null||e>a.length)&&(e=a.length);for(var t=0,n=Array(e);t=a.length?{done:!0}:{done:!1,value:a[n++]}},e:function(l){throw l},f:r}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. -In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var i,s=!0,o=!1;return{s:function(){t=t.call(a)},n:function(){var l=t.next();return s=l.done,l},e:function(l){o=!0,i=l},f:function(){try{s||t.return==null||t.return()}finally{if(o)throw i}}}}function pn(a,e,t){return(e=yn(e))in a?Object.defineProperty(a,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):a[e]=t,a}function vn(a){if(typeof Symbol<"u"&&a[Symbol.iterator]!=null||a["@@iterator"]!=null)return Array.from(a)}function mn(){throw new TypeError(`Invalid attempt to spread non-iterable instance. -In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function tt(a,e){var t=Object.keys(a);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(a);e&&(n=n.filter(function(r){return Object.getOwnPropertyDescriptor(a,r).enumerable})),t.push.apply(t,n)}return t}function nt(a){for(var e=1;e0?e[e.length-1]:null},activateTrap:function(e,t){var n=W.getActiveTrap(e);t!==n&&W.pauseTrap(e);var r=e.indexOf(t);r===-1||e.splice(r,1),e.push(t)},deactivateTrap:function(e,t){var n=e.indexOf(t);n!==-1&&e.splice(n,1),W.unpauseTrap(e)},pauseTrap:function(e){var t=W.getActiveTrap(e);t?._setPausedState(!0)},unpauseTrap:function(e){var t=W.getActiveTrap(e);t&&!t._isManuallyPaused()&&t._setPausedState(!1)}},wn=function(e){return e.tagName&&e.tagName.toLowerCase()==="input"&&typeof e.select=="function"},Sn=function(e){return e?.key==="Escape"||e?.key==="Esc"||e?.keyCode===27},ve=function(e){return e?.key==="Tab"||e?.keyCode===9},xn=function(e){return ve(e)&&!e.shiftKey},_n=function(e){return ve(e)&&e.shiftKey},st=function(e){return setTimeout(e,0)},pe=function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r1&&arguments[1]!==void 0?arguments[1]:{},b=d.hasFallback,I=b===void 0?!1:b,m=d.params,h=m===void 0?[]:m,g=i[u];if(typeof g=="function"&&(g=g.apply(void 0,gn(h))),g===!0&&(g=void 0),!g){if(g===void 0||g===!1)return g;throw new Error("`".concat(u,"` was specified but was not a node, or did not return a node"))}var E=g;if(typeof g=="string"){try{E=n.querySelector(g)}catch(k){throw new Error("`".concat(u,'` appears to be an invalid selector; error="').concat(k.message,'"'))}if(!E&&!I)throw new Error("`".concat(u,"` as selector refers to no known node"))}return E},p=function(){var u=f("initialFocus",{hasFallback:!0});if(u===!1)return!1;if(u===void 0||u&&!Fe(u,i.tabbableOptions))if(c(n.activeElement)>=0)u=n.activeElement;else{var d=s.tabbableGroups[0],b=d&&d.firstTabbableNode;u=b||f("fallbackFocus")}else u===null&&(u=f("fallbackFocus"));if(!u)throw new Error("Your focus-trap needs to have at least one focusable element");return u},v=function(){if(s.containerGroups=s.containers.map(function(u){var d=un(u,i.tabbableOptions),b=dn(u,i.tabbableOptions),I=d.length>0?d[0]:void 0,m=d.length>0?d[d.length-1]:void 0,h=b.find(function(k){return ee(k)}),g=b.slice().reverse().find(function(k){return ee(k)}),E=!!d.find(function(k){return Y(k)>0});return{container:u,tabbableNodes:d,focusableNodes:b,posTabIndexesFound:E,firstTabbableNode:I,lastTabbableNode:m,firstDomTabbableNode:h,lastDomTabbableNode:g,nextTabbableNode:function(R){var P=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0,D=d.indexOf(R);return D<0?P?b.slice(b.indexOf(R)+1).find(function($){return ee($)}):b.slice(0,b.indexOf(R)).reverse().find(function($){return ee($)}):d[D+(P?1:-1)]}}}),s.tabbableGroups=s.containerGroups.filter(function(u){return u.tabbableNodes.length>0}),s.tabbableGroups.length<=0&&!f("fallbackFocus"))throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");if(s.containerGroups.find(function(u){return u.posTabIndexesFound})&&s.containerGroups.length>1)throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.")},y=function(u){var d=u.activeElement;if(d)return d.shadowRoot&&d.shadowRoot.activeElement!==null?y(d.shadowRoot):d},S=function(u){if(u!==!1&&u!==y(document)){if(!u||!u.focus){S(p());return}u.focus({preventScroll:!!i.preventScroll}),s.mostRecentlyFocusedNode=u,wn(u)&&u.select()}},w=function(u){var d=f("setReturnFocus",{params:[u]});return d||(d===!1?!1:u)},x=function(u){var d=u.target,b=u.event,I=u.isBackward,m=I===void 0?!1:I;d=d||be(b),v();var h=null;if(s.tabbableGroups.length>0){var g=c(d,b),E=g>=0?s.containerGroups[g]:void 0;if(g<0)m?h=s.tabbableGroups[s.tabbableGroups.length-1].lastTabbableNode:h=s.tabbableGroups[0].firstTabbableNode;else if(m){var k=s.tabbableGroups.findIndex(function(J){var U=J.firstTabbableNode;return d===U});if(k<0&&(E.container===d||Fe(d,i.tabbableOptions)&&!ee(d,i.tabbableOptions)&&!E.nextTabbableNode(d,!1))&&(k=g),k>=0){var R=k===0?s.tabbableGroups.length-1:k-1,P=s.tabbableGroups[R];h=Y(d)>=0?P.lastTabbableNode:P.lastDomTabbableNode}else ve(b)||(h=E.nextTabbableNode(d,!1))}else{var D=s.tabbableGroups.findIndex(function(J){var U=J.lastTabbableNode;return d===U});if(D<0&&(E.container===d||Fe(d,i.tabbableOptions)&&!ee(d,i.tabbableOptions)&&!E.nextTabbableNode(d))&&(D=g),D>=0){var $=D===s.tabbableGroups.length-1?0:D+1,A=s.tabbableGroups[$];h=Y(d)>=0?A.firstTabbableNode:A.firstDomTabbableNode}else ve(b)||(h=E.nextTabbableNode(d))}}else h=f("fallbackFocus");return h},N=function(u){var d=be(u);if(!(c(d,u)>=0)){if(pe(i.clickOutsideDeactivates,u)){o.deactivate({returnFocus:i.returnFocusOnDeactivate});return}pe(i.allowOutsideClick,u)||u.preventDefault()}},F=function(u){var d=be(u),b=c(d,u)>=0;if(b||d instanceof Document)b&&(s.mostRecentlyFocusedNode=d);else{u.stopImmediatePropagation();var I,m=!0;if(s.mostRecentlyFocusedNode)if(Y(s.mostRecentlyFocusedNode)>0){var h=c(s.mostRecentlyFocusedNode),g=s.containerGroups[h].tabbableNodes;if(g.length>0){var E=g.findIndex(function(k){return k===s.mostRecentlyFocusedNode});E>=0&&(i.isKeyForward(s.recentNavEvent)?E+1=0&&(I=g[E-1],m=!1))}}else s.containerGroups.some(function(k){return k.tabbableNodes.some(function(R){return Y(R)>0})})||(m=!1);else m=!1;m&&(I=x({target:s.mostRecentlyFocusedNode,isBackward:i.isKeyBackward(s.recentNavEvent)})),S(I||s.mostRecentlyFocusedNode||p())}s.recentNavEvent=void 0},G=function(u){var d=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!1;s.recentNavEvent=u;var b=x({event:u,isBackward:d});b&&(ve(u)&&u.preventDefault(),S(b))},z=function(u){(i.isKeyForward(u)||i.isKeyBackward(u))&&G(u,i.isKeyBackward(u))},j=function(u){Sn(u)&&pe(i.escapeDeactivates,u)!==!1&&(u.preventDefault(),o.deactivate())},L=function(u){var d=be(u);c(d,u)>=0||pe(i.clickOutsideDeactivates,u)||pe(i.allowOutsideClick,u)||(u.preventDefault(),u.stopImmediatePropagation())},V=function(){if(s.active)return W.activateTrap(r,o),s.delayInitialFocusTimer=i.delayInitialFocus?st(function(){S(p())}):S(p()),n.addEventListener("focusin",F,!0),n.addEventListener("mousedown",N,{capture:!0,passive:!1}),n.addEventListener("touchstart",N,{capture:!0,passive:!1}),n.addEventListener("click",L,{capture:!0,passive:!1}),n.addEventListener("keydown",z,{capture:!0,passive:!1}),n.addEventListener("keydown",j),o},O=function(u){s.active&&!s.paused&&o._setSubtreeIsolation(!1),s.adjacentElements.clear(),s.alreadySilent.clear();var d=new Set,b=new Set,I=et(u),m;try{for(I.s();!(m=I.n()).done;){var h=m.value;d.add(h);for(var g=typeof ShadowRoot<"u"&&h.getRootNode()instanceof ShadowRoot,E=h;E;){d.add(E);var k=E.parentElement,R=[];k?R=k.children:!k&&g&&(R=E.getRootNode().children,k=E.getRootNode().host,g=typeof ShadowRoot<"u"&&k.getRootNode()instanceof ShadowRoot);var P=et(R),D;try{for(P.s();!(D=P.n()).done;){var $=D.value;b.add($)}}catch(A){P.e(A)}finally{P.f()}E=k}}}catch(A){I.e(A)}finally{I.f()}d.forEach(function(A){b.delete(A)}),s.adjacentElements=b},K=function(){if(s.active)return n.removeEventListener("focusin",F,!0),n.removeEventListener("mousedown",N,!0),n.removeEventListener("touchstart",N,!0),n.removeEventListener("click",L,!0),n.removeEventListener("keydown",z,!0),n.removeEventListener("keydown",j),o},ie=function(u){var d=u.some(function(b){var I=Array.from(b.removedNodes);return I.some(function(m){return m===s.mostRecentlyFocusedNode})});d&&S(p())},ae=typeof window<"u"&&"MutationObserver"in window?new MutationObserver(ie):void 0,X=function(){ae&&(ae.disconnect(),s.active&&!s.paused&&s.containers.map(function(u){ae.observe(u,{subtree:!0,childList:!0})}))};return o={get active(){return s.active},get paused(){return s.paused},activate:function(u){if(s.active)return this;var d=l(u,"onActivate"),b=l(u,"onPostActivate"),I=l(u,"checkCanFocusTrap"),m=W.getActiveTrap(r),h=!1;if(m&&!m.paused){var g;(g=m._setSubtreeIsolation)===null||g===void 0||g.call(m,!1),h=!0}try{I||v(),s.active=!0,s.paused=!1,s.nodeFocusedBeforeActivation=y(n),d?.();var E=function(){I&&v(),V(),X(),i.isolateSubtrees&&o._setSubtreeIsolation(!0),b?.()};if(I)return I(s.containers.concat()).then(E,E),this;E()}catch(R){if(m===W.getActiveTrap(r)&&h){var k;(k=m._setSubtreeIsolation)===null||k===void 0||k.call(m,!0)}throw R}return this},deactivate:function(u){if(!s.active)return this;var d=nt({onDeactivate:i.onDeactivate,onPostDeactivate:i.onPostDeactivate,checkCanReturnFocus:i.checkCanReturnFocus},u);clearTimeout(s.delayInitialFocusTimer),s.delayInitialFocusTimer=void 0,s.paused||o._setSubtreeIsolation(!1),s.alreadySilent.clear(),K(),s.active=!1,s.paused=!1,X(),W.deactivateTrap(r,o);var b=l(d,"onDeactivate"),I=l(d,"onPostDeactivate"),m=l(d,"checkCanReturnFocus"),h=l(d,"returnFocus","returnFocusOnDeactivate");b?.();var g=function(){st(function(){h&&S(w(s.nodeFocusedBeforeActivation)),I?.()})};return h&&m?(m(w(s.nodeFocusedBeforeActivation)).then(g,g),this):(g(),this)},pause:function(u){return s.active?(s.manuallyPaused=!0,this._setPausedState(!0,u)):this},unpause:function(u){return s.active?(s.manuallyPaused=!1,r[r.length-1]!==this?this:this._setPausedState(!1,u)):this},updateContainerElements:function(u){var d=[].concat(u).filter(Boolean);return s.containers=d.map(function(b){return typeof b=="string"?n.querySelector(b):b}),i.isolateSubtrees&&O(s.containers),s.active&&(v(),i.isolateSubtrees&&!s.paused&&o._setSubtreeIsolation(!0)),X(),this}},Object.defineProperties(o,{_isManuallyPaused:{value:function(){return s.manuallyPaused}},_setPausedState:{value:function(u,d){if(s.paused===u)return this;if(s.paused=u,u){var b=l(d,"onPause"),I=l(d,"onPostPause");b?.(),K(),X(),o._setSubtreeIsolation(!1),I?.()}else{var m=l(d,"onUnpause"),h=l(d,"onPostUnpause");m?.(),o._setSubtreeIsolation(!0),v(),V(),X(),h?.()}return this}},_setSubtreeIsolation:{value:function(u){i.isolateSubtrees&&s.adjacentElements.forEach(function(d){var b;u?i.isolateSubtrees==="aria-hidden"?((d.ariaHidden==="true"||((b=d.getAttribute("aria-hidden"))===null||b===void 0?void 0:b.toLowerCase())==="true")&&s.alreadySilent.add(d),d.setAttribute("aria-hidden","true")):((d.inert||d.hasAttribute("inert"))&&s.alreadySilent.add(d),d.setAttribute("inert",!0)):s.alreadySilent.has(d)||(i.isolateSubtrees==="aria-hidden"?d.removeAttribute("aria-hidden"):d.removeAttribute("inert"))})}}}),o.updateContainerElements(e),o};function In(a,e={}){let t;const{immediate:n,...r}=e,i=ne(!1),s=ne(!1),o=p=>t&&t.activate(p),l=p=>t&&t.deactivate(p),c=()=>{t&&(t.pause(),s.value=!0)},f=()=>{t&&(t.unpause(),s.value=!1)};return Le(Se(()=>Et(qe(a)).map(p=>{const v=qe(p);return typeof v=="string"?v:Tt(v)}).filter(It)),p=>{if(p.length)if(!t)t=Tn(p,{...r,onActivate(){i.value=!0,e.onActivate&&e.onActivate()},onDeactivate(){i.value=!1,e.onDeactivate&&e.onDeactivate()}}),n&&o();else{const v=t?.active;t?.updateContainerElements(p),!v&&n&&o()}},{flush:"post"}),kt(()=>l()),{hasFocus:i,isPaused:s,activate:o,deactivate:l,pause:c,unpause:f}}class se{constructor(e,t=!0,n=[],r=5e3){this.ctx=e,this.iframes=t,this.exclude=n,this.iframesTimeout=r}static matches(e,t){const n=typeof t=="string"?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){let i=!1;return n.every(s=>r.call(e,s)?(i=!0,!1):!0),i}else return!1}getContexts(){let e,t=[];return typeof this.ctx>"u"||!this.ctx?e=[]:NodeList.prototype.isPrototypeOf(this.ctx)?e=Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?e=this.ctx:typeof this.ctx=="string"?e=Array.prototype.slice.call(document.querySelectorAll(this.ctx)):e=[this.ctx],e.forEach(n=>{const r=t.filter(i=>i.contains(n)).length>0;t.indexOf(n)===-1&&!r&&t.push(n)}),t}getIframeContents(e,t,n=()=>{}){let r;try{const i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch{n()}r&&t(r)}isIframeBlank(e){const t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}observeIframeLoad(e,t,n){let r=!1,i=null;const s=()=>{if(!r){r=!0,clearTimeout(i);try{this.isIframeBlank(e)||(e.removeEventListener("load",s),this.getIframeContents(e,t,n))}catch{n()}}};e.addEventListener("load",s),i=setTimeout(s,this.iframesTimeout)}onIframeReady(e,t,n){try{e.contentWindow.document.readyState==="complete"?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch{n()}}waitForIframes(e,t){let n=0;this.forEachIframe(e,()=>!0,r=>{n++,this.waitForIframes(r.querySelector("html"),()=>{--n||t()})},r=>{r||t()})}forEachIframe(e,t,n,r=()=>{}){let i=e.querySelectorAll("iframe"),s=i.length,o=0;i=Array.prototype.slice.call(i);const l=()=>{--s<=0&&r(o)};s||l(),i.forEach(c=>{se.matches(c,this.exclude)?l():this.onIframeReady(c,f=>{t(c)&&(o++,n(f)),l()},l)})}createIterator(e,t,n){return document.createNodeIterator(e,t,n,!1)}createInstanceOnIframe(e){return new se(e.querySelector("html"),this.iframes)}compareNodeIframe(e,t,n){const r=e.compareDocumentPosition(n),i=Node.DOCUMENT_POSITION_PRECEDING;if(r&i)if(t!==null){const s=t.compareDocumentPosition(n),o=Node.DOCUMENT_POSITION_FOLLOWING;if(s&o)return!0}else return!0;return!1}getIteratorNode(e){const t=e.previousNode();let n;return t===null?n=e.nextNode():n=e.nextNode()&&e.nextNode(),{prevNode:t,node:n}}checkIframeFilter(e,t,n,r){let i=!1,s=!1;return r.forEach((o,l)=>{o.val===n&&(i=l,s=o.handled)}),this.compareNodeIframe(e,t,n)?(i===!1&&!s?r.push({val:n,handled:!0}):i!==!1&&!s&&(r[i].handled=!0),!0):(i===!1&&r.push({val:n,handled:!1}),!1)}handleOpenIframes(e,t,n,r){e.forEach(i=>{i.handled||this.getIframeContents(i.val,s=>{this.createInstanceOnIframe(s).forEachNode(t,n,r)})})}iterateThroughNodes(e,t,n,r,i){const s=this.createIterator(t,e,r);let o=[],l=[],c,f,p=()=>({prevNode:f,node:c}=this.getIteratorNode(s),c);for(;p();)this.iframes&&this.forEachIframe(t,v=>this.checkIframeFilter(c,f,v,o),v=>{this.createInstanceOnIframe(v).forEachNode(e,y=>l.push(y),r)}),l.push(c);l.forEach(v=>{n(v)}),this.iframes&&this.handleOpenIframes(o,e,n,r),i()}forEachNode(e,t,n,r=()=>{}){const i=this.getContexts();let s=i.length;s||r(),i.forEach(o=>{const l=()=>{this.iterateThroughNodes(e,o,t,n,()=>{--s<=0&&r()})};this.iframes?this.waitForIframes(o,l):l()})}}let kn=class{constructor(e){this.ctx=e,this.ie=!1;const t=window.navigator.userAgent;(t.indexOf("MSIE")>-1||t.indexOf("Trident")>-1)&&(this.ie=!0)}set opt(e){this._opt=Object.assign({},{element:"",className:"",exclude:[],iframes:!1,iframesTimeout:5e3,separateWordSearch:!0,diacritics:!0,synonyms:{},accuracy:"partially",acrossElements:!1,caseSensitive:!1,ignoreJoiners:!1,ignoreGroups:0,ignorePunctuation:[],wildcards:"disabled",each:()=>{},noMatch:()=>{},filter:()=>!0,done:()=>{},debug:!1,log:window.console},e)}get opt(){return this._opt}get iterator(){return new se(this.ctx,this.opt.iframes,this.opt.exclude,this.opt.iframesTimeout)}log(e,t="debug"){const n=this.opt.log;this.opt.debug&&typeof n=="object"&&typeof n[t]=="function"&&n[t](`mark.js: ${e}`)}escapeStr(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}createRegExp(e){return this.opt.wildcards!=="disabled"&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),this.opt.wildcards!=="disabled"&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),e}createSynonymsRegExp(e){const t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(let i in t)if(t.hasOwnProperty(i)){const s=t[i],o=this.opt.wildcards!=="disabled"?this.setupWildcardsRegExp(i):this.escapeStr(i),l=this.opt.wildcards!=="disabled"?this.setupWildcardsRegExp(s):this.escapeStr(s);o!==""&&l!==""&&(e=e.replace(new RegExp(`(${this.escapeStr(o)}|${this.escapeStr(l)})`,`gm${n}`),r+`(${this.processSynomyms(o)}|${this.processSynomyms(l)})`+r))}return e}processSynomyms(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}setupWildcardsRegExp(e){return e=e.replace(/(?:\\)*\?/g,t=>t.charAt(0)==="\\"?"?":""),e.replace(/(?:\\)*\*/g,t=>t.charAt(0)==="\\"?"*":"")}createWildcardsRegExp(e){let t=this.opt.wildcards==="withSpaces";return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}setupIgnoreJoinersRegExp(e){return e.replace(/[^(|)\\]/g,(t,n,r)=>{let i=r.charAt(n+1);return/[(|)\\]/.test(i)||i===""?t:t+"\0"})}createJoinersRegExp(e){let t=[];const n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join(`[${t.join("")}]*`):e}createDiacriticsRegExp(e){const t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"];let r=[];return e.split("").forEach(i=>{n.every(s=>{if(s.indexOf(i)!==-1){if(r.indexOf(s)>-1)return!1;e=e.replace(new RegExp(`[${s}]`,`gm${t}`),`[${s}]`),r.push(s)}return!0})}),e}createMergedBlanksRegExp(e){return e.replace(/[\s]+/gmi,"[\\s]+")}createAccuracyRegExp(e){const t="!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿";let n=this.opt.accuracy,r=typeof n=="string"?n:n.value,i=typeof n=="string"?[]:n.limiters,s="";switch(i.forEach(o=>{s+=`|${this.escapeStr(o)}`}),r){case"partially":default:return`()(${e})`;case"complementary":return s="\\s"+(s||this.escapeStr(t)),`()([^${s}]*${e}[^${s}]*)`;case"exactly":return`(^|\\s${s})(${e})(?=$|\\s${s})`}}getSeparatedKeywords(e){let t=[];return e.forEach(n=>{this.opt.separateWordSearch?n.split(" ").forEach(r=>{r.trim()&&t.indexOf(r)===-1&&t.push(r)}):n.trim()&&t.indexOf(n)===-1&&t.push(n)}),{keywords:t.sort((n,r)=>r.length-n.length),length:t.length}}isNumeric(e){return Number(parseFloat(e))==e}checkRanges(e){if(!Array.isArray(e)||Object.prototype.toString.call(e[0])!=="[object Object]")return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];const t=[];let n=0;return e.sort((r,i)=>r.start-i.start).forEach(r=>{let{start:i,end:s,valid:o}=this.callNoMatchOnInvalidRanges(r,n);o&&(r.start=i,r.length=s-i,t.push(r),n=s)}),t}callNoMatchOnInvalidRanges(e,t){let n,r,i=!1;return e&&typeof e.start<"u"?(n=parseInt(e.start,10),r=n+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log(`Ignoring invalid or overlapping range: ${JSON.stringify(e)}`),this.opt.noMatch(e))):(this.log(`Ignoring invalid range: ${JSON.stringify(e)}`),this.opt.noMatch(e)),{start:n,end:r,valid:i}}checkWhitespaceRanges(e,t,n){let r,i=!0,s=n.length,o=t-s,l=parseInt(e.start,10)-o;return l=l>s?s:l,r=l+parseInt(e.length,10),r>s&&(r=s,this.log(`End range automatically set to the max value of ${s}`)),l<0||r-l<0||l>s||r>s?(i=!1,this.log(`Invalid range: ${JSON.stringify(e)}`),this.opt.noMatch(e)):n.substring(l,r).replace(/\s+/g,"")===""&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:l,end:r,valid:i}}getTextNodes(e){let t="",n=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,r=>{n.push({start:t.length,end:(t+=r.textContent).length,node:r})},r=>this.matchesExclude(r.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT,()=>{e({value:t,nodes:n})})}matchesExclude(e){return se.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}wrapRangeInTextNode(e,t,n){const r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),s=i.splitText(n-t);let o=document.createElement(r);return o.setAttribute("data-markjs","true"),this.opt.className&&o.setAttribute("class",this.opt.className),o.textContent=i.textContent,i.parentNode.replaceChild(o,i),s}wrapRangeInMappedTextNode(e,t,n,r,i){e.nodes.every((s,o)=>{const l=e.nodes[o+1];if(typeof l>"u"||l.start>t){if(!r(s.node))return!1;const c=t-s.start,f=(n>s.end?s.end:n)-s.start,p=e.value.substr(0,s.start),v=e.value.substr(f+s.start);if(s.node=this.wrapRangeInTextNode(s.node,c,f),e.value=p+v,e.nodes.forEach((y,S)=>{S>=o&&(e.nodes[S].start>0&&S!==o&&(e.nodes[S].start-=f),e.nodes[S].end-=f)}),n-=f,i(s.node.previousSibling,s.start),n>s.end)t=s.end;else return!1}return!0})}wrapMatches(e,t,n,r,i){const s=t===0?0:t+1;this.getTextNodes(o=>{o.nodes.forEach(l=>{l=l.node;let c;for(;(c=e.exec(l.textContent))!==null&&c[s]!=="";){if(!n(c[s],l))continue;let f=c.index;if(s!==0)for(let p=1;p{let l;for(;(l=e.exec(o.value))!==null&&l[s]!=="";){let c=l.index;if(s!==0)for(let p=1;pn(l[s],p),(p,v)=>{e.lastIndex=v,r(p)})}i()})}wrapRangeFromIndex(e,t,n,r){this.getTextNodes(i=>{const s=i.value.length;e.forEach((o,l)=>{let{start:c,end:f,valid:p}=this.checkWhitespaceRanges(o,s,i.value);p&&this.wrapRangeInMappedTextNode(i,c,f,v=>t(v,o,i.value.substring(c,f),l),v=>{n(v,o)})}),r()})}unwrapMatches(e){const t=e.parentNode;let n=document.createDocumentFragment();for(;e.firstChild;)n.appendChild(e.removeChild(e.firstChild));t.replaceChild(n,e),this.ie?this.normalizeTextNode(t):t.normalize()}normalizeTextNode(e){if(e){if(e.nodeType===3)for(;e.nextSibling&&e.nextSibling.nodeType===3;)e.nodeValue+=e.nextSibling.nodeValue,e.parentNode.removeChild(e.nextSibling);else this.normalizeTextNode(e.firstChild);this.normalizeTextNode(e.nextSibling)}}markRegExp(e,t){this.opt=t,this.log(`Searching with expression "${e}"`);let n=0,r="wrapMatches";const i=s=>{n++,this.opt.each(s)};this.opt.acrossElements&&(r="wrapMatchesAcrossElements"),this[r](e,this.opt.ignoreGroups,(s,o)=>this.opt.filter(o,s,n),i,()=>{n===0&&this.opt.noMatch(e),this.opt.done(n)})}mark(e,t){this.opt=t;let n=0,r="wrapMatches";const{keywords:i,length:s}=this.getSeparatedKeywords(typeof e=="string"?[e]:e),o=this.opt.caseSensitive?"":"i",l=c=>{let f=new RegExp(this.createRegExp(c),`gm${o}`),p=0;this.log(`Searching with expression "${f}"`),this[r](f,1,(v,y)=>this.opt.filter(y,c,n,p),v=>{p++,n++,this.opt.each(v)},()=>{p===0&&this.opt.noMatch(c),i[s-1]===c?this.opt.done(n):l(i[i.indexOf(c)+1])})};this.opt.acrossElements&&(r="wrapMatchesAcrossElements"),s===0?this.opt.done(n):l(i[0])}markRanges(e,t){this.opt=t;let n=0,r=this.checkRanges(e);r&&r.length?(this.log("Starting to mark with the following ranges: "+JSON.stringify(r)),this.wrapRangeFromIndex(r,(i,s,o,l)=>this.opt.filter(i,s,o,l),(i,s)=>{n++,this.opt.each(i,s)},()=>{this.opt.done(n)})):this.opt.done(n)}unmark(e){this.opt=e;let t=this.opt.element?this.opt.element:"*";t+="[data-markjs]",this.opt.className&&(t+=`.${this.opt.className}`),this.log(`Removal selector "${t}"`),this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT,n=>{this.unwrapMatches(n)},n=>{const r=se.matches(n,t),i=this.matchesExclude(n);return!r||i?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},this.opt.done)}};function Nn(a){const e=new kn(a);return this.mark=(t,n)=>(e.mark(t,n),this),this.markRegExp=(t,n)=>(e.markRegExp(t,n),this),this.markRanges=(t,n)=>(e.markRanges(t,n),this),this.unmark=t=>(e.unmark(t),this),this}const Fn="ENTRIES",mt="KEYS",gt="VALUES",M="";class Re{constructor(e,t){const n=e._tree,r=Array.from(n.keys());this.set=e,this._type=t,this._path=r.length>0?[{node:n,keys:r}]:[]}next(){const e=this.dive();return this.backtrack(),e}dive(){if(this._path.length===0)return{done:!0,value:void 0};const{node:e,keys:t}=te(this._path);if(te(t)===M)return{done:!1,value:this.result()};const n=e.get(te(t));return this._path.push({node:n,keys:Array.from(n.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const e=te(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack())}key(){return this.set._prefix+this._path.map(({keys:e})=>te(e)).filter(e=>e!==M).join("")}value(){return te(this._path).node.get(M)}result(){switch(this._type){case gt:return this.value();case mt:return this.key();default:return[this.key(),this.value()]}}[Symbol.iterator](){return this}}const te=a=>a[a.length-1],Rn=(a,e,t)=>{const n=new Map;if(e===void 0)return n;const r=e.length+1,i=r+t,s=new Uint8Array(i*r).fill(t+1);for(let o=0;o{const l=i*s;e:for(const c of a.keys())if(c===M){const f=r[l-1];f<=t&&n.set(o,[a.get(c),f])}else{let f=i;for(let p=0;pt)continue e}bt(a.get(c),e,t,n,r,f,s,o+c)}};class H{constructor(e=new Map,t=""){this._size=void 0,this._tree=e,this._prefix=t}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[t,n]=ke(this._tree,e.slice(this._prefix.length));if(t===void 0){const[r,i]=$e(n);for(const s of r.keys())if(s!==M&&s.startsWith(i)){const o=new Map;return o.set(s.slice(i.length),r.get(s)),new H(o,e)}}return new H(t,e)}clear(){this._size=void 0,this._tree.clear()}delete(e){return this._size=void 0,An(this._tree,e)}entries(){return new Re(this,Fn)}forEach(e){for(const[t,n]of this)e(t,n,this)}fuzzyGet(e,t){return Rn(this._tree,e,t)}get(e){const t=ze(this._tree,e);return t!==void 0?t.get(M):void 0}has(e){const t=ze(this._tree,e);return t!==void 0&&t.has(M)}keys(){return new Re(this,mt)}set(e,t){if(typeof e!="string")throw new Error("key must be a string");return this._size=void 0,Ae(this._tree,e).set(M,t),this}get size(){if(this._size)return this._size;this._size=0;const e=this.entries();for(;!e.next().done;)this._size+=1;return this._size}update(e,t){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const n=Ae(this._tree,e);return n.set(M,t(n.get(M))),this}fetch(e,t){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const n=Ae(this._tree,e);let r=n.get(M);return r===void 0&&n.set(M,r=t()),r}values(){return new Re(this,gt)}[Symbol.iterator](){return this.entries()}static from(e){const t=new H;for(const[n,r]of e)t.set(n,r);return t}static fromObject(e){return H.from(Object.entries(e))}}const ke=(a,e,t=[])=>{if(e.length===0||a==null)return[a,t];for(const n of a.keys())if(n!==M&&e.startsWith(n))return t.push([a,n]),ke(a.get(n),e.slice(n.length),t);return t.push([a,e]),ke(void 0,"",t)},ze=(a,e)=>{if(e.length===0||a==null)return a;for(const t of a.keys())if(t!==M&&e.startsWith(t))return ze(a.get(t),e.slice(t.length))},Ae=(a,e)=>{const t=e.length;e:for(let n=0;a&&n{const[t,n]=ke(a,e);if(t!==void 0){if(t.delete(M),t.size===0)yt(n);else if(t.size===1){const[r,i]=t.entries().next().value;wt(n,r,i)}}},yt=a=>{if(a.length===0)return;const[e,t]=$e(a);if(e.delete(t),e.size===0)yt(a.slice(0,-1));else if(e.size===1){const[n,r]=e.entries().next().value;n!==M&&wt(a.slice(0,-1),n,r)}},wt=(a,e,t)=>{if(a.length===0)return;const[n,r]=$e(a);n.set(r+e,t),n.delete(r)},$e=a=>a[a.length-1],Be="or",St="and",On="and_not";class re{constructor(e){if(e?.fields==null)throw new Error('MiniSearch: option "fields" must be provided');const t=e.autoVacuum==null||e.autoVacuum===!0?Me:e.autoVacuum;this._options={...Ce,...e,autoVacuum:t,searchOptions:{...rt,...e.searchOptions||{}},autoSuggestOptions:{...Pn,...e.autoSuggestOptions||{}}},this._index=new H,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=je,this.addFields(this._options.fields)}add(e){const{extractField:t,stringifyField:n,tokenize:r,processTerm:i,fields:s,idField:o}=this._options,l=t(e,o);if(l==null)throw new Error(`MiniSearch: document does not have ID field "${o}"`);if(this._idToShortId.has(l))throw new Error(`MiniSearch: duplicate ID ${l}`);const c=this.addDocumentId(l);this.saveStoredFields(c,e);for(const f of s){const p=t(e,f);if(p==null)continue;const v=r(n(p,f),f),y=this._fieldIds[f],S=new Set(v).size;this.addFieldLength(c,y,this._documentCount-1,S);for(const w of v){const x=i(w,f);if(Array.isArray(x))for(const N of x)this.addTerm(y,c,N);else x&&this.addTerm(y,c,x)}}}addAll(e){for(const t of e)this.add(t)}addAllAsync(e,t={}){const{chunkSize:n=10}=t,r={chunk:[],promise:Promise.resolve()},{chunk:i,promise:s}=e.reduce(({chunk:o,promise:l},c,f)=>(o.push(c),(f+1)%n===0?{chunk:[],promise:l.then(()=>new Promise(p=>setTimeout(p,0))).then(()=>this.addAll(o))}:{chunk:o,promise:l}),r);return s.then(()=>this.addAll(i))}remove(e){const{tokenize:t,processTerm:n,extractField:r,stringifyField:i,fields:s,idField:o}=this._options,l=r(e,o);if(l==null)throw new Error(`MiniSearch: document does not have ID field "${o}"`);const c=this._idToShortId.get(l);if(c==null)throw new Error(`MiniSearch: cannot remove document with ID ${l}: it is not in the index`);for(const f of s){const p=r(e,f);if(p==null)continue;const v=t(i(p,f),f),y=this._fieldIds[f],S=new Set(v).size;this.removeFieldLength(c,y,this._documentCount,S);for(const w of v){const x=n(w,f);if(Array.isArray(x))for(const N of x)this.removeTerm(y,c,N);else x&&this.removeTerm(y,c,x)}}this._storedFields.delete(c),this._documentIds.delete(c),this._idToShortId.delete(l),this._fieldLength.delete(c),this._documentCount-=1}removeAll(e){if(e)for(const t of e)this.remove(t);else{if(arguments.length>0)throw new Error("Expected documents to be present. Omit the argument to remove all documents.");this._index=new H,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldLength=new Map,this._avgFieldLength=[],this._storedFields=new Map,this._nextId=0}}discard(e){const t=this._idToShortId.get(e);if(t==null)throw new Error(`MiniSearch: cannot discard document with ID ${e}: it is not in the index`);this._idToShortId.delete(e),this._documentIds.delete(t),this._storedFields.delete(t),(this._fieldLength.get(t)||[]).forEach((n,r)=>{this.removeFieldLength(t,r,this._documentCount,n)}),this._fieldLength.delete(t),this._documentCount-=1,this._dirtCount+=1,this.maybeAutoVacuum()}maybeAutoVacuum(){if(this._options.autoVacuum===!1)return;const{minDirtFactor:e,minDirtCount:t,batchSize:n,batchWait:r}=this._options.autoVacuum;this.conditionalVacuum({batchSize:n,batchWait:r},{minDirtCount:t,minDirtFactor:e})}discardAll(e){const t=this._options.autoVacuum;try{this._options.autoVacuum=!1;for(const n of e)this.discard(n)}finally{this._options.autoVacuum=t}this.maybeAutoVacuum()}replace(e){const{idField:t,extractField:n}=this._options,r=n(e,t);this.discard(r),this.add(e)}vacuum(e={}){return this.conditionalVacuum(e)}conditionalVacuum(e,t){return this._currentVacuum?(this._enqueuedVacuumConditions=this._enqueuedVacuumConditions&&t,this._enqueuedVacuum!=null?this._enqueuedVacuum:(this._enqueuedVacuum=this._currentVacuum.then(()=>{const n=this._enqueuedVacuumConditions;return this._enqueuedVacuumConditions=je,this.performVacuuming(e,n)}),this._enqueuedVacuum)):this.vacuumConditionsMet(t)===!1?Promise.resolve():(this._currentVacuum=this.performVacuuming(e),this._currentVacuum)}async performVacuuming(e,t){const n=this._dirtCount;if(this.vacuumConditionsMet(t)){const r=e.batchSize||Ve.batchSize,i=e.batchWait||Ve.batchWait;let s=1;for(const[o,l]of this._index){for(const[c,f]of l)for(const[p]of f)this._documentIds.has(p)||(f.size<=1?l.delete(c):f.delete(p));this._index.get(o).size===0&&this._index.delete(o),s%r===0&&await new Promise(c=>setTimeout(c,i)),s+=1}this._dirtCount-=n}await null,this._currentVacuum=this._enqueuedVacuum,this._enqueuedVacuum=null}vacuumConditionsMet(e){if(e==null)return!0;let{minDirtCount:t,minDirtFactor:n}=e;return t=t||Me.minDirtCount,n=n||Me.minDirtFactor,this.dirtCount>=t&&this.dirtFactor>=n}get isVacuuming(){return this._currentVacuum!=null}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}has(e){return this._idToShortId.has(e)}getStoredFields(e){const t=this._idToShortId.get(e);if(t!=null)return this._storedFields.get(t)}search(e,t={}){const{searchOptions:n}=this._options,r={...n,...t},i=this.executeQuery(e,t),s=[];for(const[o,{score:l,terms:c,match:f}]of i){const p=c.length||1,v={id:this._documentIds.get(o),score:l*p,terms:Object.keys(f),queryTerms:c,match:f};Object.assign(v,this._storedFields.get(o)),(r.filter==null||r.filter(v))&&s.push(v)}return e===re.wildcard&&r.boostDocument==null||s.sort(at),s}autoSuggest(e,t={}){t={...this._options.autoSuggestOptions,...t};const n=new Map;for(const{score:i,terms:s}of this.search(e,t)){const o=s.join(" "),l=n.get(o);l!=null?(l.score+=i,l.count+=1):n.set(o,{score:i,terms:s,count:1})}const r=[];for(const[i,{score:s,terms:o,count:l}]of n)r.push({suggestion:i,terms:o,score:s/l});return r.sort(at),r}get documentCount(){return this._documentCount}get termCount(){return this._index.size}static loadJSON(e,t){if(t==null)throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");return this.loadJS(JSON.parse(e),t)}static async loadJSONAsync(e,t){if(t==null)throw new Error("MiniSearch: loadJSON should be given the same options used when serializing the index");return this.loadJSAsync(JSON.parse(e),t)}static getDefault(e){if(Ce.hasOwnProperty(e))return Oe(Ce,e);throw new Error(`MiniSearch: unknown option "${e}"`)}static loadJS(e,t){const{index:n,documentIds:r,fieldLength:i,storedFields:s,serializationVersion:o}=e,l=this.instantiateMiniSearch(e,t);l._documentIds=ye(r),l._fieldLength=ye(i),l._storedFields=ye(s);for(const[c,f]of l._documentIds)l._idToShortId.set(f,c);for(const[c,f]of n){const p=new Map;for(const v of Object.keys(f)){let y=f[v];o===1&&(y=y.ds),p.set(parseInt(v,10),ye(y))}l._index.set(c,p)}return l}static async loadJSAsync(e,t){const{index:n,documentIds:r,fieldLength:i,storedFields:s,serializationVersion:o}=e,l=this.instantiateMiniSearch(e,t);l._documentIds=await we(r),l._fieldLength=await we(i),l._storedFields=await we(s);for(const[f,p]of l._documentIds)l._idToShortId.set(p,f);let c=0;for(const[f,p]of n){const v=new Map;for(const y of Object.keys(p)){let S=p[y];o===1&&(S=S.ds),v.set(parseInt(y,10),await we(S))}++c%1e3===0&&await xt(0),l._index.set(f,v)}return l}static instantiateMiniSearch(e,t){const{documentCount:n,nextId:r,fieldIds:i,averageFieldLength:s,dirtCount:o,serializationVersion:l}=e;if(l!==1&&l!==2)throw new Error("MiniSearch: cannot deserialize an index created with an incompatible version");const c=new re(t);return c._documentCount=n,c._nextId=r,c._idToShortId=new Map,c._fieldIds=i,c._avgFieldLength=s,c._dirtCount=o||0,c._index=new H,c}executeQuery(e,t={}){if(e===re.wildcard)return this.executeWildcardQuery(t);if(typeof e!="string"){const v={...t,...e,queries:void 0},y=e.queries.map(S=>this.executeQuery(S,v));return this.combineResults(y,v.combineWith)}const{tokenize:n,processTerm:r,searchOptions:i}=this._options,s={tokenize:n,processTerm:r,...i,...t},{tokenize:o,processTerm:l}=s,p=o(e).flatMap(v=>l(v)).filter(v=>!!v).map(Dn(s)).map(v=>this.executeQuerySpec(v,s));return this.combineResults(p,s.combineWith)}executeQuerySpec(e,t){const n={...this._options.searchOptions,...t},r=(n.fields||this._options.fields).reduce((w,x)=>({...w,[x]:Oe(n.boost,x)||1}),{}),{boostDocument:i,weights:s,maxFuzzy:o,bm25:l}=n,{fuzzy:c,prefix:f}={...rt.weights,...s},p=this._index.get(e.term),v=this.termResults(e.term,e.term,1,e.termBoost,p,r,i,l);let y,S;if(e.prefix&&(y=this._index.atPrefix(e.term)),e.fuzzy){const w=e.fuzzy===!0?.2:e.fuzzy,x=w<1?Math.min(o,Math.round(e.term.length*w)):w;x&&(S=this._index.fuzzyGet(e.term,x))}if(y)for(const[w,x]of y){const N=w.length-e.term.length;if(!N)continue;S?.delete(w);const F=f*w.length/(w.length+.3*N);this.termResults(e.term,w,F,e.termBoost,x,r,i,l,v)}if(S)for(const w of S.keys()){const[x,N]=S.get(w);if(!N)continue;const F=c*w.length/(w.length+N);this.termResults(e.term,w,F,e.termBoost,x,r,i,l,v)}return v}executeWildcardQuery(e){const t=new Map,n={...this._options.searchOptions,...e};for(const[r,i]of this._documentIds){const s=n.boostDocument?n.boostDocument(i,"",this._storedFields.get(r)):1;t.set(r,{score:s,terms:[],match:{}})}return t}combineResults(e,t=Be){if(e.length===0)return new Map;const n=t.toLowerCase(),r=Cn[n];if(!r)throw new Error(`Invalid combination operator: ${t}`);return e.reduce(r)||new Map}toJSON(){const e=[];for(const[t,n]of this._index){const r={};for(const[i,s]of n)r[i]=Object.fromEntries(s);e.push([t,r])}return{documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:e,serializationVersion:2}}termResults(e,t,n,r,i,s,o,l,c=new Map){if(i==null)return c;for(const f of Object.keys(s)){const p=s[f],v=this._fieldIds[f],y=i.get(v);if(y==null)continue;let S=y.size;const w=this._avgFieldLength[v];for(const x of y.keys()){if(!this._documentIds.has(x)){this.removeTerm(v,x,t),S-=1;continue}const N=o?o(this._documentIds.get(x),t,this._storedFields.get(x)):1;if(!N)continue;const F=y.get(x),G=this._fieldLength.get(x)[v],z=Ln(F,S,this._documentCount,G,w,l),j=n*r*p*N*z,L=c.get(x);if(L){L.score+=j,zn(L.terms,e);const V=Oe(L.match,t);V?V.push(f):L.match[t]=[f]}else c.set(x,{score:j,terms:[e],match:{[t]:[f]}})}}return c}addTerm(e,t,n){const r=this._index.fetch(n,ot);let i=r.get(e);if(i==null)i=new Map,i.set(t,1),r.set(e,i);else{const s=i.get(t);i.set(t,(s||0)+1)}}removeTerm(e,t,n){if(!this._index.has(n)){this.warnDocumentChanged(t,e,n);return}const r=this._index.fetch(n,ot),i=r.get(e);i==null||i.get(t)==null?this.warnDocumentChanged(t,e,n):i.get(t)<=1?i.size<=1?r.delete(e):i.delete(t):i.set(t,i.get(t)-1),this._index.get(n).size===0&&this._index.delete(n)}warnDocumentChanged(e,t,n){for(const r of Object.keys(this._fieldIds))if(this._fieldIds[r]===t){this._options.logger("warn",`MiniSearch: document with ID ${this._documentIds.get(e)} has changed before removal: term "${n}" was not present in field "${r}". Removing a document after it has changed can corrupt the index!`,"version_conflict");return}}addDocumentId(e){const t=this._nextId;return this._idToShortId.set(e,t),this._documentIds.set(t,e),this._documentCount+=1,this._nextId+=1,t}addFields(e){for(let t=0;tObject.prototype.hasOwnProperty.call(a,e)?a[e]:void 0,Cn={[Be]:(a,e)=>{for(const t of e.keys()){const n=a.get(t);if(n==null)a.set(t,e.get(t));else{const{score:r,terms:i,match:s}=e.get(t);n.score=n.score+r,n.match=Object.assign(n.match,s),it(n.terms,i)}}return a},[St]:(a,e)=>{const t=new Map;for(const n of e.keys()){const r=a.get(n);if(r==null)continue;const{score:i,terms:s,match:o}=e.get(n);it(r.terms,s),t.set(n,{score:r.score+i,terms:r.terms,match:Object.assign(r.match,o)})}return t},[On]:(a,e)=>{for(const t of e.keys())a.delete(t);return a}},Mn={k:1.2,b:.7,d:.5},Ln=(a,e,t,n,r,i)=>{const{k:s,b:o,d:l}=i;return Math.log(1+(t-e+.5)/(e+.5))*(l+a*(s+1)/(a+s*(1-o+o*n/r)))},Dn=a=>(e,t,n)=>{const r=typeof a.fuzzy=="function"?a.fuzzy(e,t,n):a.fuzzy||!1,i=typeof a.prefix=="function"?a.prefix(e,t,n):a.prefix===!0,s=typeof a.boostTerm=="function"?a.boostTerm(e,t,n):1;return{term:e,fuzzy:r,prefix:i,termBoost:s}},Ce={idField:"id",extractField:(a,e)=>a[e],stringifyField:(a,e)=>a.toString(),tokenize:a=>a.split(Vn),processTerm:a=>a.toLowerCase(),fields:void 0,searchOptions:void 0,storeFields:[],logger:(a,e)=>{typeof console?.[a]=="function"&&console[a](e)},autoVacuum:!0},rt={combineWith:Be,prefix:!1,fuzzy:!1,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:Mn},Pn={combineWith:St,prefix:(a,e,t)=>e===t.length-1},Ve={batchSize:1e3,batchWait:10},je={minDirtFactor:.1,minDirtCount:20},Me={...Ve,...je},zn=(a,e)=>{a.includes(e)||a.push(e)},it=(a,e)=>{for(const t of e)a.includes(t)||a.push(t)},at=({score:a},{score:e})=>e-a,ot=()=>new Map,ye=a=>{const e=new Map;for(const t of Object.keys(a))e.set(parseInt(t,10),a[t]);return e},we=async a=>{const e=new Map;let t=0;for(const n of Object.keys(a))e.set(parseInt(n,10),a[n]),++t%1e3===0&&await xt(0);return e},xt=a=>new Promise(e=>setTimeout(e,a)),Vn=/[\n\r\p{Z}\p{P}]+/u;class jn{max;cache;constructor(e=10){this.max=e,this.cache=new Map}get(e){let t=this.cache.get(e);return t!==void 0&&(this.cache.delete(e),this.cache.set(e,t)),t}set(e,t){this.cache.has(e)?this.cache.delete(e):this.cache.size===this.max&&this.cache.delete(this.first()),this.cache.set(e,t)}first(){return this.cache.keys().next().value}clear(){this.cache.clear()}}function $n(a){const{localeIndex:e,theme:t}=lt();function n(r){const i=r.split("."),s=t.value.search?.options,o=s&&typeof s=="object",l=o&&s.locales?.[e.value]?.translations||null,c=o&&s.translations||null;let f=l,p=c,v=a;const y=i.pop();for(const S of i){let w=null;const x=v?.[S];x&&(w=v=x);const N=p?.[S];N&&(w=p=N);const F=f?.[S];F&&(w=f=F),x||(v=w),N||(p=w),F||(f=w)}return f?.[y]??p?.[y]??v?.[y]??""}return n}const Bn=["aria-owns"],Wn={class:"shell"},Kn=["title"],Jn={class:"search-actions before"},Un=["title"],qn=["aria-activedescendant","aria-controls","placeholder"],Hn={class:"search-actions"},Gn=["title"],Qn=["disabled","title"],Yn=["id","role","aria-labelledby"],Zn=["id","aria-selected"],Xn=["href","aria-label","onMouseenter","onFocusin","data-index"],es={class:"titles"},ts=["innerHTML"],ns={class:"title main"},ss=["innerHTML"],rs={key:0,class:"excerpt-wrapper"},is={key:0,class:"excerpt",inert:""},as=["innerHTML"],os={key:0,class:"no-results"},ls={class:"search-keyboard-shortcuts"},cs=["aria-label"],us=["aria-label"],ds=["aria-label"],fs=["aria-label"],hs=Nt({__name:"VPLocalSearchBox",emits:["close"],setup(a,{emit:e}){const t=e,n=ne(),r=ne(),i=ne(Gt),s=lt(),{activate:o}=In(n,{immediate:!0,allowOutsideClick:!0,clickOutsideDeactivates:!0,escapeDeactivates:!0}),{localeIndex:l,theme:c}=s,f=He(async()=>Ze(re.loadJSON((await i.value[l.value]?.())?.default,{fields:["title","titles","text"],storeFields:["title","titles"],searchOptions:{fuzzy:.2,prefix:!0,boost:{title:4,text:2,titles:1},...c.value.search?.provider==="local"&&c.value.search.options?.miniSearch?.searchOptions},...c.value.search?.provider==="local"&&c.value.search.options?.miniSearch?.options}))),v=Se(()=>c.value.search?.provider==="local"&&c.value.search.options?.disableQueryPersistence===!0).value?ue(""):Ft("vitepress:local-search-filter",""),y=Rt("vitepress:local-search-detailed-list",c.value.search?.provider==="local"&&c.value.search.options?.detailedView===!0),S=Se(()=>c.value.search?.provider==="local"&&(c.value.search.options?.disableDetailedView===!0||c.value.search.options?.detailedView===!1));At(()=>{S.value&&(y.value=!1)});const w=ne([]),x=ue(!1);Le(v,()=>{x.value=!1});const N=He(async()=>{if(r.value)return Ze(new Nn(r.value))},null),F=new jn(16);Ot(()=>[f.value,v.value,y.value],async([m,h,g],E,k)=>{E?.[0]!==m&&F.clear();let R=!1;if(k(()=>{R=!0}),!m)return;w.value=m.search(h).slice(0,16),x.value=!0;const P=g?await Promise.all(w.value.map(A=>G(A.id))):[];if(R)return;for(const{id:A,mod:J}of P){const U=A.slice(0,A.indexOf("#"));let oe=F.get(U);if(oe)continue;oe=new Map,F.set(U,oe);const le=J.default??J;if(le?.render||le?.setup){const Q=Kt(le);Q.config.warnHandler=()=>{},Q.provide(Jt,s),Object.defineProperties(Q.config.globalProperties,{$frontmatter:{get(){return s.frontmatter.value}},$params:{get(){return s.page.value.params}}});const We=document.createElement("div");Q.mount(We),We.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach(ce=>{const Ke=ce.querySelector("a")?.getAttribute("href"),Je=Ke?.startsWith("#")&&Ke.slice(1);if(!Je)return;let Ue="";for(;(ce=ce.nextElementSibling)&&!/^h[1-6]$/i.test(ce.tagName);)Ue+=ce.outerHTML;oe.set(Je,Ue)}),Q.unmount()}if(R)return}const D=new Set;if(w.value=w.value.map(A=>{const[J,U]=A.id.split("#"),le=F.get(J)?.get(U)??"";for(const Q in A.match)D.add(Q);return{...A,text:le}}),await de(),R)return;await new Promise(A=>{N.value?.unmark({done:()=>{N.value?.markRegExp(b(D),{done:A})}})});const $=n.value?.querySelectorAll(".result .excerpt")??[];for(const A of $)A.querySelector('mark[data-markjs="true"]')?.scrollIntoView({block:"center"});r.value?.firstElementChild?.scrollIntoView({block:"start"})},{debounce:200,immediate:!0});async function G(m){const h=Ut(m.slice(0,m.indexOf("#")));try{if(!h)throw new Error(`Cannot find file for id: ${m}`);return{id:m,mod:await import(h)}}catch(g){return console.error(g),{id:m,mod:{}}}}const z=ue(),j=Se(()=>v.value?.length<=0);function L(m=!0){z.value?.focus(),m&&z.value?.select()}Ne(()=>{L()});function V(m){m.pointerType==="mouse"&&L()}const O=ue(-1),K=ue(!0);Le(w,m=>{O.value=m.length?0:-1,ie()});function ie(){de(()=>{document.querySelector(".result.selected")?.scrollIntoView({block:"nearest"})})}me("ArrowUp",m=>{m.preventDefault(),O.value--,O.value<0&&(O.value=w.value.length-1),K.value=!0,ie()}),me("ArrowDown",m=>{m.preventDefault(),O.value++,O.value>=w.value.length&&(O.value=0),K.value=!0,ie()});const ae=Ct();me("Enter",m=>{if(m.isComposing||m.target instanceof HTMLButtonElement&&m.target.type!=="submit")return;const h=w.value[O.value];if(m.target instanceof HTMLInputElement&&!h){m.preventDefault();return}h&&(ae.go(h.id),t("close"))}),me("Escape",()=>{t("close")});const _=$n({button:{buttonText:"Search"},modal:{displayDetails:"Display detailed list",resetButtonTitle:"Reset search",backButtonTitle:"Close search",noResultsText:"No results for",footer:{selectText:"to select",selectKeyAriaLabel:"enter",navigateText:"to navigate",navigateUpKeyAriaLabel:"up arrow",navigateDownKeyAriaLabel:"down arrow",closeText:"to close",closeKeyAriaLabel:"escape"}}});Ne(()=>{window.history.pushState(null,"",null)}),Mt("popstate",m=>{m.preventDefault(),t("close")});const u=Lt(Dt?document.body:null);Ne(()=>{de(()=>{u.value=!0,de().then(()=>o())})}),Pt(()=>{u.value=!1});function d(){v.value="",de().then(()=>L(!1))}function b(m){return new RegExp([...m].sort((h,g)=>g.length-h.length).map(h=>`(${qt(h)})`).join("|"),"gi")}function I(m){if(!K.value)return;const h=m.target?.closest(".result"),g=Number.parseInt(h?.dataset.index);g>=0&&g!==O.value&&(O.value=g),K.value=!1}return(m,h)=>(B(),zt(Wt,{to:"body"},[T("div",{ref_key:"el",ref:n,role:"button","aria-owns":w.value?.length?"localsearch-list":void 0,"aria-expanded":"true","aria-haspopup":"listbox","aria-labelledby":"localsearch-label",class:"VPLocalSearchBox"},[T("div",{class:"backdrop",onClick:h[0]||(h[0]=g=>m.$emit("close"))}),T("div",Wn,[T("form",{class:"search-bar",onPointerup:h[4]||(h[4]=g=>V(g)),onSubmit:h[5]||(h[5]=Vt(()=>{},["prevent"]))},[T("label",{title:C(_)("button.buttonText"),id:"localsearch-label",for:"localsearch-input"},[...h[7]||(h[7]=[T("span",{"aria-hidden":"true",class:"vpi-search search-icon local-search-icon"},null,-1)])],8,Kn),T("div",Jn,[T("button",{class:"back-button",title:C(_)("modal.backButtonTitle"),onClick:h[1]||(h[1]=g=>m.$emit("close"))},[...h[8]||(h[8]=[T("span",{class:"vpi-arrow-left local-search-icon"},null,-1)])],8,Un)]),jt(T("input",{ref_key:"searchInput",ref:z,"onUpdate:modelValue":h[2]||(h[2]=g=>Bt(v)?v.value=g:null),"aria-activedescendant":O.value>-1?"localsearch-item-"+O.value:void 0,"aria-autocomplete":"both","aria-controls":w.value?.length?"localsearch-list":void 0,"aria-labelledby":"localsearch-label",autocapitalize:"off",autocomplete:"off",autocorrect:"off",class:"search-input",id:"localsearch-input",enterkeyhint:"go",maxlength:"64",placeholder:C(_)("button.buttonText"),spellcheck:"false",type:"search"},null,8,qn),[[$t,C(v)]]),T("div",Hn,[S.value?ge("",!0):(B(),q("button",{key:0,class:Ge(["toggle-layout-button",{"detailed-list":C(y)}]),type:"button",title:C(_)("modal.displayDetails"),onClick:h[3]||(h[3]=g=>O.value>-1&&(y.value=!C(y)))},[...h[9]||(h[9]=[T("span",{class:"vpi-layout-list local-search-icon"},null,-1)])],10,Gn)),T("button",{class:"clear-button",type:"reset",disabled:j.value,title:C(_)("modal.resetButtonTitle"),onClick:d},[...h[10]||(h[10]=[T("span",{class:"vpi-delete local-search-icon"},null,-1)])],8,Qn)])],32),T("ul",{ref_key:"resultsEl",ref:r,id:w.value?.length?"localsearch-list":void 0,role:w.value?.length?"listbox":void 0,"aria-labelledby":w.value?.length?"localsearch-label":void 0,class:"results",onMousemove:I},[(B(!0),q(Ye,null,Qe(w.value,(g,E)=>(B(),q("li",{key:g.id,id:"localsearch-item-"+E,"aria-selected":O.value===E?"true":"false",role:"option"},[T("a",{href:g.id,class:Ge(["result",{selected:O.value===E}]),"aria-label":[...g.titles,g.title].join(" > "),onMouseenter:k=>!K.value&&(O.value=E),onFocusin:k=>O.value=E,onClick:h[6]||(h[6]=k=>m.$emit("close")),"data-index":E},[T("div",null,[T("div",es,[h[12]||(h[12]=T("span",{class:"title-icon"},"#",-1)),(B(!0),q(Ye,null,Qe(g.titles,(k,R)=>(B(),q("span",{key:R,class:"title"},[T("span",{class:"text",innerHTML:k},null,8,ts),h[11]||(h[11]=T("span",{class:"vpi-chevron-right local-search-icon"},null,-1))]))),128)),T("span",ns,[T("span",{class:"text",innerHTML:g.title},null,8,ss)])]),C(y)?(B(),q("div",rs,[g.text?(B(),q("div",is,[T("div",{class:"vp-doc",innerHTML:g.text},null,8,as)])):ge("",!0),h[13]||(h[13]=T("div",{class:"excerpt-gradient-bottom"},null,-1)),h[14]||(h[14]=T("div",{class:"excerpt-gradient-top"},null,-1))])):ge("",!0)])],42,Xn)],8,Zn))),128)),C(v)&&!w.value.length&&x.value?(B(),q("li",os,[fe(he(C(_)("modal.noResultsText"))+' "',1),T("strong",null,he(C(v)),1),h[15]||(h[15]=fe('" ',-1))])):ge("",!0)],40,Yn),T("div",ls,[T("span",null,[T("kbd",{"aria-label":C(_)("modal.footer.navigateUpKeyAriaLabel")},[...h[16]||(h[16]=[T("span",{class:"vpi-arrow-up navigate-icon"},null,-1)])],8,cs),T("kbd",{"aria-label":C(_)("modal.footer.navigateDownKeyAriaLabel")},[...h[17]||(h[17]=[T("span",{class:"vpi-arrow-down navigate-icon"},null,-1)])],8,us),fe(" "+he(C(_)("modal.footer.navigateText")),1)]),T("span",null,[T("kbd",{"aria-label":C(_)("modal.footer.selectKeyAriaLabel")},[...h[18]||(h[18]=[T("span",{class:"vpi-corner-down-left navigate-icon"},null,-1)])],8,ds),fe(" "+he(C(_)("modal.footer.selectText")),1)]),T("span",null,[T("kbd",{"aria-label":C(_)("modal.footer.closeKeyAriaLabel")},"esc",8,fs),fe(" "+he(C(_)("modal.footer.closeText")),1)])])])],8,Bn)]))}}),bs=Ht(hs,[["__scopeId","data-v-8c9d8b44"]]);export{bs as default}; diff --git a/dist/docs/assets/chunks/theme.B2b_1z_T.js b/dist/docs/assets/chunks/theme.B2b_1z_T.js deleted file mode 100644 index 7eeb8cc..0000000 --- a/dist/docs/assets/chunks/theme.B2b_1z_T.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/chunks/VPLocalSearchBox.BOPGMrlA.js","assets/chunks/framework.DyoGesZS.js"])))=>i.map(i=>d[i]); -import{d as m,c as u,r as c,n as P,o,a as U,t as L,b as p,w as f,T as ve,e as h,_,u as Ke,i as Re,f as Ue,g as fe,h as k,j as v,k as i,l as O,m as $e,p as R,q as je,s as he,v as We,x as ee,y as V,z as D,A as Ve,B as Y,C as te,D as ne,E as ye,F as Se,G as S,H as x,I as j,J as g,K as H,L as Ne,M as ze,N as qe,O as F,P as ae,Q as Te,R as Je,S as ie,U as we,V as Me,W as Ye,X as Pe,Y as Xe,Z as Qe,$ as xe,a0 as Ae,a1 as Ze,a2 as et,a3 as tt,a4 as nt,a5 as at}from"./framework.DyoGesZS.js";const st=m({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(e){return(t,n)=>(o(),u("span",{class:P(["VPBadge",e.type])},[c(t.$slots,"default",{},()=>[U(L(e.text),1)])],2))}}),ot={key:0,class:"VPBackdrop"},it=m({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(e){return(t,n)=>(o(),p(ve,{name:"fade"},{default:f(()=>[e.show?(o(),u("div",ot)):h("",!0)]),_:1}))}}),rt=_(it,[["__scopeId","data-v-c79a1216"]]),$=Ke;function lt(e,t){let n,a=!1;return()=>{n&&clearTimeout(n),a?n=setTimeout(e,t):(e(),(a=!0)&&setTimeout(()=>a=!1,t))}}function re(e){return e.startsWith("/")?e:`/${e}`}function me(e){const{pathname:t,search:n,hash:a,protocol:s}=new URL(e,"http://a.com");if(Re(e)||e.startsWith("#")||!s.startsWith("http")||!Ue(t))return e;const{site:r}=$(),d=t.endsWith("/")||t.endsWith(".html")?e:e.replace(/(?:(^\.+)\/)?.*$/,`$1${t.replace(/(\.md)?$/,r.value.cleanUrls?"":".html")}${n}${a}`);return fe(d)}function X({correspondingLink:e=!1}={}){const{site:t,localeIndex:n,page:a,theme:s,hash:r}=$(),d=k(()=>({label:t.value.locales[n.value]?.label,link:t.value.locales[n.value]?.link||(n.value==="root"?"/":`/${n.value}/`)}));return{localeLinks:k(()=>Object.entries(t.value.locales).flatMap(([b,y])=>d.value.label===y.label?[]:{text:y.label,link:ct(y.link||(b==="root"?"/":`/${b}/`),s.value.i18nRouting!==!1&&e,a.value.relativePath.slice(d.value.link.length-1),!t.value.cleanUrls)+r.value,lang:y.lang,dir:y.dir})),currentLang:d}}function ct(e,t,n,a){return t?e.replace(/\/$/,"")+re(n.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,a?".html":"")):e}const ut={class:"NotFound"},dt={class:"code"},vt={class:"title"},ft={class:"quote"},ht={class:"action"},mt=["href","aria-label"],pt=m({__name:"NotFound",setup(e){const{theme:t}=$(),{currentLang:n}=X();return(a,s)=>(o(),u("div",ut,[v("p",dt,L(i(t).notFound?.code??"404"),1),v("h1",vt,L(i(t).notFound?.title??"PAGE NOT FOUND"),1),s[0]||(s[0]=v("div",{class:"divider"},null,-1)),v("blockquote",ft,L(i(t).notFound?.quote??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),v("div",ht,[v("a",{class:"link",href:i(fe)(i(t).notFound?.link??i(n).link),"aria-label":i(t).notFound?.linkLabel??"go to home"},L(i(t).notFound?.linkText??"Take me home"),9,mt)])]))}}),kt=_(pt,[["__scopeId","data-v-829df670"]]);function Ie(e,t){if(Array.isArray(e))return Q(e);if(e==null)return[];t=re(t);const n=Object.keys(e).sort((s,r)=>r.split("/").length-s.split("/").length).find(s=>t.startsWith(re(s))),a=n?e[n]:[];return Array.isArray(a)?Q(a):Q(a.items,a.base)}function gt(e){const t=[];let n=0;for(const a in e){const s=e[a];if(s.items){n=t.push(s);continue}t[n]||t.push({items:[]}),t[n].items.push(s)}return t}function _t(e){const t=[];function n(a){for(const s of a)s.text&&s.link&&t.push({text:s.text,link:s.link,docFooterText:s.docFooterText}),s.items&&n(s.items)}return n(e),t}function le(e,t){return Array.isArray(t)?t.some(n=>le(e,n)):O(e,t.link)?!0:t.items?le(e,t.items):!1}function Q(e,t){return[...e].map(n=>{const a={...n},s=a.base||t;return s&&a.link&&(a.link=s+a.link.replace(/^\//,s.endsWith("/")?"":"/")),a.items&&(a.items=Q(a.items,s)),a})}function bt(){const{hasSidebar:e}=G(),t=$e("(min-width: 960px)"),n=$e("(min-width: 1280px)");return{isAsideEnabled:k(()=>!n.value&&!t.value?!1:e.value?n.value:t.value)}}const $t=/\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/,ce=[];function He(e){return typeof e.outline=="object"&&!Array.isArray(e.outline)&&e.outline.label||e.outlineTitle||"On this page"}function yt(e){const t=[...document.querySelectorAll(".VPDoc h1, .VPDoc h2, .VPDoc h3, .VPDoc h4, .VPDoc h5, .VPDoc h6")].filter(n=>n.id&&n.hasChildNodes()).map(n=>{const a=Number(n.tagName[1]);return{element:n,title:Pt(n),link:"#"+n.id,level:a}});return Lt(t,e)}function Pt(e){let t="";for(const n of e.childNodes)if(n.nodeType===1){if($t.test(n.className))continue;t+=n.textContent}else n.nodeType===3&&(t+=n.textContent);return t.trim()}function Lt(e,t){if(t===!1)return[];const n=(typeof t=="object"&&!Array.isArray(t)?t.level:t)||2,[a,s]=typeof n=="number"?[n,n]:n==="deep"?[2,6]:n;return Nt(e,a,s)}function Vt(e,t){const{isAsideEnabled:n}=bt(),a=lt(r,100);let s=null;R(()=>{requestAnimationFrame(r),window.addEventListener("scroll",a)}),je(()=>{d(location.hash)}),he(()=>{window.removeEventListener("scroll",a)});function r(){if(!n.value)return;const l=window.scrollY,b=window.innerHeight,y=document.body.offsetHeight,N=Math.abs(l+b-y)<1,w=ce.map(({element:A,link:I})=>({link:I,top:St(A)})).filter(({top:A})=>!Number.isNaN(A)).sort((A,I)=>A.top-I.top);if(!w.length){d(null);return}if(l<1){d(null);return}if(N){d(w[w.length-1].link);return}let C=null;for(const{link:A,top:I}of w){if(I>l+We()+4)break;C=A}d(C)}function d(l){s&&s.classList.remove("active"),l==null?s=null:s=e.value.querySelector(`a[href="${decodeURIComponent(l)}"]`);const b=s;b?(b.classList.add("active"),t.value.style.top=b.offsetTop+39+"px",t.value.style.opacity="1"):(t.value.style.top="33px",t.value.style.opacity="0")}}function St(e){let t=0;for(;e!==document.body;){if(e===null)return NaN;t+=e.offsetTop,e=e.offsetParent}return t}function Nt(e,t,n){ce.length=0;const a=[],s=[];return e.forEach(r=>{const d={...r,children:[]};let l=s[s.length-1];for(;l&&l.level>=d.level;)s.pop(),l=s[s.length-1];if(d.element.classList.contains("ignore-header")||l&&"shouldIgnore"in l){s.push({level:d.level,shouldIgnore:!0});return}d.level>n||d.level{t=q.value?document.activeElement:void 0}),R(()=>{window.addEventListener("keyup",n)}),he(()=>{window.removeEventListener("keyup",n)});function n(a){a.key==="Escape"&&q.value&&(e(),t?.focus())}}function wt(){function e(){q.value=!0}function t(){q.value=!1}function n(){q.value?t():e()}return{isOpen:q,open:e,close:t,toggle:n}}function Mt(e){const{page:t,hash:n}=$(),a=V(!1),s=k(()=>e.value.collapsed!=null),r=k(()=>!!e.value.link),d=V(!1),l=()=>{d.value=O(t.value.relativePath,e.value.link)};D([t,e,n],l),R(l);const b=k(()=>d.value?!0:e.value.items?le(t.value.relativePath,e.value.items):!1),y=k(()=>!!(e.value.items&&e.value.items.length));ee(()=>{a.value=!!(s.value&&e.value.collapsed)}),Ve(()=>{(d.value||b.value)&&(a.value=!1)});function N(){s.value&&(a.value=!a.value)}return{collapsed:a,collapsible:s,isLink:r,isActiveLink:d,hasActiveLink:b,hasChildren:y,toggle:N}}const ue=te([]),J=te([]),de=te(!1);function G(){const{frontmatter:e,theme:t}=$(),n=k(()=>!!(e.value.isHome??e.value.layout==="home")),a=k(()=>e.value.sidebar!==!1&&J.value.length>0&&!n.value),s=k(()=>a.value&&de.value),r=k(()=>a.value?gt(J.value):[]),d=k(()=>n.value?!1:e.value.aside!=null?!!e.value.aside:t.value.aside!==!1),l=k(()=>d.value?e.value.aside==null?t.value.aside==="left":e.value.aside==="left":!1),b=k(()=>ue.value.length>0);return{isHome:n,sidebar:ye(J),sidebarGroups:r,hasSidebar:a,isSidebarEnabled:s,hasAside:d,leftAside:l,headers:ye(ue),hasLocalNav:b}}function xt({closeSidebar:e}){const{frontmatter:t,page:n,theme:a}=$();D(()=>[n.value.relativePath,a.value.sidebar],([r,d])=>{const l=d?Ie(d,r):[];JSON.stringify(l)!==JSON.stringify(J.value)&&(J.value=l)},{immediate:!0,deep:!0,flush:"sync"}),Se(()=>{ue.value=yt(t.value.outline??a.value.outline)}),Y&&(de.value=window.innerWidth>=960,window.addEventListener("resize",()=>{de.value=window.innerWidth>=960},{passive:!0}));const s=ne();D(()=>s.path,e),Tt(e)}const Be=Symbol("layout-info"),At=["href","title"],It=m({__name:"VPDocOutlineItem",props:{headers:{},root:{type:Boolean}},setup(e){return(t,n)=>{const a=j("VPDocOutlineItem",!0);return o(),u("ul",{class:P(["VPDocOutlineItem",e.root?"root":"nested"])},[(o(!0),u(S,null,x(e.headers,({children:s,link:r,title:d})=>(o(),u("li",null,[v("a",{class:"outline-link",href:r,title:d},L(d),9,At),s?.length?(o(),p(a,{key:0,headers:s},null,8,["headers"])):h("",!0)]))),256))],2)}}}),Ce=_(It,[["__scopeId","data-v-1ce71065"]]),Ht={class:"content"},Bt={"aria-level":"2",class:"outline-title",id:"doc-outline-aria-label",role:"heading"},Ct=m({__name:"VPDocAsideOutline",setup(e){const{theme:t}=$(),n=V(),a=V(),{headers:s,hasLocalNav:r}=G();return Vt(n,a),(d,l)=>(o(),u("nav",{"aria-labelledby":"doc-outline-aria-label",class:P(["VPDocAsideOutline",{"has-outline":i(r)}]),ref_key:"container",ref:n},[v("div",Ht,[v("div",{class:"outline-marker",ref_key:"marker",ref:a},null,512),v("div",Bt,L(i(He)(i(t))),1),g(Ce,{headers:i(s),root:!0},null,8,["headers"])])],2))}}),Et=_(Ct,[["__scopeId","data-v-60d5052e"]]),Ft={class:"VPDocAsideCarbonAds"},Ot=m({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(e){const t=()=>null;return(n,a)=>(o(),u("div",Ft,[g(i(t),{"carbon-ads":e.carbonAds},null,8,["carbon-ads"])]))}}),Dt={class:"VPDocAside"},Gt=m({__name:"VPDocAside",setup(e){const{theme:t}=$();return(n,a)=>(o(),u("div",Dt,[c(n.$slots,"aside-top",{},void 0,!0),c(n.$slots,"aside-outline-before",{},void 0,!0),g(Et),c(n.$slots,"aside-outline-after",{},void 0,!0),a[0]||(a[0]=v("div",{class:"spacer"},null,-1)),c(n.$slots,"aside-ads-before",{},void 0,!0),i(t).carbonAds?(o(),p(Ot,{key:0,"carbon-ads":i(t).carbonAds},null,8,["carbon-ads"])):h("",!0),c(n.$slots,"aside-ads-after",{},void 0,!0),c(n.$slots,"aside-bottom",{},void 0,!0)]))}}),Kt=_(Gt,[["__scopeId","data-v-3f215769"]]);function Rt(){const{theme:e,page:t}=$();return k(()=>{const{text:n="Edit this page",pattern:a=""}=e.value.editLink||{};let s;return typeof a=="function"?s=a(t.value):s=a.replace(/:path/g,t.value.filePath),{url:s,text:n}})}function Ut(){const{page:e,theme:t,frontmatter:n}=$();return k(()=>{const a=Ie(t.value.sidebar,e.value.relativePath),s=_t(a),r=jt(s,y=>y.link.replace(/[?#].*$/,"")),d=r.findIndex(y=>O(e.value.relativePath,y.link)),l=t.value.docFooter?.prev===!1&&!n.value.prev||n.value.prev===!1,b=t.value.docFooter?.next===!1&&!n.value.next||n.value.next===!1;return{prev:l?void 0:{text:(typeof n.value.prev=="string"?n.value.prev:typeof n.value.prev=="object"?n.value.prev.text:void 0)??r[d-1]?.docFooterText??r[d-1]?.text,link:(typeof n.value.prev=="object"?n.value.prev.link:void 0)??r[d-1]?.link},next:b?void 0:{text:(typeof n.value.next=="string"?n.value.next:typeof n.value.next=="object"?n.value.next.text:void 0)??r[d+1]?.docFooterText??r[d+1]?.text,link:(typeof n.value.next=="object"?n.value.next.link:void 0)??r[d+1]?.link}}})}function jt(e,t){const n=new Set;return e.filter(a=>{const s=t(a);return n.has(s)?!1:n.add(s)})}const B=m({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(e){const t=e,n=k(()=>t.tag??(t.href?"a":"span")),a=k(()=>t.href&&Ne.test(t.href)||t.target==="_blank");return(s,r)=>(o(),p(H(n.value),{class:P(["VPLink",{link:e.href,"vp-external-link-icon":a.value,"no-icon":e.noIcon}]),href:e.href?i(me)(e.href):void 0,target:e.target??(a.value?"_blank":void 0),rel:e.rel??(a.value?"noreferrer":void 0)},{default:f(()=>[c(s.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),Wt={class:"VPLastUpdated"},zt=["datetime"],qt=m({__name:"VPDocFooterLastUpdated",setup(e){const{theme:t,page:n,lang:a}=$(),{language:s}=qe(),r=ze("timeRef"),d=k(()=>new Date(n.value.lastUpdated)),l=k(()=>d.value.toISOString()),b=te("");return R(()=>{ee(()=>{const y=t.value.lastUpdated?.formatOptions?.forceLocale?a.value:s.value;b.value=new Intl.DateTimeFormat(y,t.value.lastUpdated?.formatOptions??{dateStyle:"medium",timeStyle:"medium"}).format(d.value),y&&a.value!==y?r.value?.setAttribute("lang",y):r.value?.removeAttribute("lang")})}),(y,N)=>(o(),u("p",Wt,[U(L(i(t).lastUpdated?.text||i(t).lastUpdatedText||"Last updated")+": ",1),v("time",{ref_key:"timeRef",ref:r,datetime:l.value},L(b.value),9,zt)]))}}),Jt=_(qt,[["__scopeId","data-v-3c637f39"]]),Yt={key:0,class:"VPDocFooter"},Xt={key:0,class:"edit-info"},Qt={key:0,class:"edit-link"},Zt={key:1,class:"last-updated"},en={key:1,class:"prev-next","aria-labelledby":"doc-footer-aria-label"},tn={class:"pager"},nn=["innerHTML"],an=["innerHTML"],sn={class:"pager"},on=["innerHTML"],rn=["innerHTML"],ln=m({__name:"VPDocFooter",setup(e){const{theme:t,page:n,frontmatter:a}=$(),s=Rt(),r=Ut(),d=k(()=>t.value.editLink&&a.value.editLink!==!1),l=k(()=>n.value.lastUpdated),b=k(()=>d.value||l.value||r.value.prev||r.value.next);return(y,N)=>b.value?(o(),u("footer",Yt,[c(y.$slots,"doc-footer-before",{},void 0,!0),d.value||l.value?(o(),u("div",Xt,[d.value?(o(),u("div",Qt,[g(B,{class:"edit-link-button",href:i(s).url,"no-icon":!0},{default:f(()=>[N[0]||(N[0]=v("span",{class:"vpi-square-pen edit-link-icon"},null,-1)),U(" "+L(i(s).text),1)]),_:1},8,["href"])])):h("",!0),l.value?(o(),u("div",Zt,[g(Jt)])):h("",!0)])):h("",!0),i(r).prev?.link||i(r).next?.link?(o(),u("nav",en,[N[1]||(N[1]=v("span",{class:"visually-hidden",id:"doc-footer-aria-label"},"Pager",-1)),v("div",tn,[i(r).prev?.link?(o(),p(B,{key:0,class:"pager-link prev",href:i(r).prev.link},{default:f(()=>[v("span",{class:"desc",innerHTML:i(t).docFooter?.prev||"Previous page"},null,8,nn),v("span",{class:"title",innerHTML:i(r).prev.text},null,8,an)]),_:1},8,["href"])):h("",!0)]),v("div",sn,[i(r).next?.link?(o(),p(B,{key:0,class:"pager-link next",href:i(r).next.link},{default:f(()=>[v("span",{class:"desc",innerHTML:i(t).docFooter?.next||"Next page"},null,8,on),v("span",{class:"title",innerHTML:i(r).next.text},null,8,rn)]),_:1},8,["href"])):h("",!0)])])):h("",!0)])):h("",!0)}}),cn=_(ln,[["__scopeId","data-v-e257564d"]]),un={class:"container"},dn={class:"aside-container"},vn={class:"aside-content"},fn={class:"content"},hn={class:"content-container"},mn={class:"main"},pn=m({__name:"VPDoc",setup(e){const{theme:t}=$(),n=ne(),{hasSidebar:a,hasAside:s,leftAside:r}=G(),d=k(()=>n.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(l,b)=>{const y=j("Content");return o(),u("div",{class:P(["VPDoc",{"has-sidebar":i(a),"has-aside":i(s)}])},[c(l.$slots,"doc-top",{},void 0,!0),v("div",un,[i(s)?(o(),u("div",{key:0,class:P(["aside",{"left-aside":i(r)}])},[b[0]||(b[0]=v("div",{class:"aside-curtain"},null,-1)),v("div",dn,[v("div",vn,[g(Kt,null,{"aside-top":f(()=>[c(l.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":f(()=>[c(l.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":f(()=>[c(l.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":f(()=>[c(l.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":f(()=>[c(l.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":f(()=>[c(l.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):h("",!0),v("div",fn,[v("div",hn,[c(l.$slots,"doc-before",{},void 0,!0),v("main",mn,[g(y,{class:P(["vp-doc",[d.value,i(t).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),g(cn,null,{"doc-footer-before":f(()=>[c(l.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),c(l.$slots,"doc-after",{},void 0,!0)])])]),c(l.$slots,"doc-bottom",{},void 0,!0)],2)}}}),kn=_(pn,[["__scopeId","data-v-7011f0d8"]]),gn=m({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{},target:{},rel:{}},setup(e){const t=e,n=k(()=>t.href&&Ne.test(t.href)),a=k(()=>t.tag||(t.href?"a":"button"));return(s,r)=>(o(),p(H(a.value),{class:P(["VPButton",[e.size,e.theme]]),href:e.href?i(me)(e.href):void 0,target:t.target??(n.value?"_blank":void 0),rel:t.rel??(n.value?"noreferrer":void 0)},{default:f(()=>[c(s.$slots,"default",{},()=>[U(L(e.text),1)],!0)]),_:3},8,["class","href","target","rel"]))}}),_n=_(gn,[["__scopeId","data-v-01bff58b"]]),bn=["src","alt"],$n=m({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(e){return(t,n)=>{const a=j("VPImage",!0);return e.image?(o(),u(S,{key:0},[typeof e.image=="string"||"src"in e.image?(o(),u("img",F({key:0,class:"VPImage"},typeof e.image=="string"?t.$attrs:{...e.image,...t.$attrs},{src:i(fe)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,bn)):(o(),u(S,{key:1},[g(a,F({class:"dark",image:e.image.dark,alt:e.image.alt},t.$attrs),null,16,["image","alt"]),g(a,F({class:"light",image:e.image.light,alt:e.image.alt},t.$attrs),null,16,["image","alt"])],64))],64)):h("",!0)}}}),Z=_($n,[["__scopeId","data-v-8426fc1a"]]),yn={class:"container"},Pn={class:"main"},Ln={class:"heading"},Vn=["innerHTML"],Sn=["innerHTML"],Nn=["innerHTML"],Tn={key:0,class:"actions"},wn={key:0,class:"image"},Mn={class:"image-container"},xn=m({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(e){const{heroImageSlotExists:t}=ae(Be,{heroImageSlotExists:k(()=>!1)});return(n,a)=>(o(),u("div",{class:P(["VPHero",{"has-image":e.image||i(t)}])},[v("div",yn,[v("div",Pn,[c(n.$slots,"home-hero-info-before",{},void 0,!0),c(n.$slots,"home-hero-info",{},()=>[v("h1",Ln,[e.name?(o(),u("span",{key:0,innerHTML:e.name,class:"name clip"},null,8,Vn)):h("",!0),e.text?(o(),u("span",{key:1,innerHTML:e.text,class:"text"},null,8,Sn)):h("",!0)]),e.tagline?(o(),u("p",{key:0,innerHTML:e.tagline,class:"tagline"},null,8,Nn)):h("",!0)],!0),c(n.$slots,"home-hero-info-after",{},void 0,!0),e.actions?(o(),u("div",Tn,[(o(!0),u(S,null,x(e.actions,s=>(o(),u("div",{key:s.link,class:"action"},[g(_n,{tag:"a",size:"medium",theme:s.theme,text:s.text,href:s.link,target:s.target,rel:s.rel},null,8,["theme","text","href","target","rel"])]))),128))])):h("",!0),c(n.$slots,"home-hero-actions-after",{},void 0,!0)]),e.image||i(t)?(o(),u("div",wn,[v("div",Mn,[a[0]||(a[0]=v("div",{class:"image-bg"},null,-1)),c(n.$slots,"home-hero-image",{},()=>[e.image?(o(),p(Z,{key:0,class:"image-src",image:e.image},null,8,["image"])):h("",!0)],!0)])])):h("",!0)])],2))}}),An=_(xn,[["__scopeId","data-v-e394c869"]]),In=m({__name:"VPHomeHero",setup(e){const{frontmatter:t}=$();return(n,a)=>i(t).hero?(o(),p(An,{key:0,class:"VPHomeHero",name:i(t).hero.name,text:i(t).hero.text,tagline:i(t).hero.tagline,image:i(t).hero.image,actions:i(t).hero.actions},{"home-hero-info-before":f(()=>[c(n.$slots,"home-hero-info-before")]),"home-hero-info":f(()=>[c(n.$slots,"home-hero-info")]),"home-hero-info-after":f(()=>[c(n.$slots,"home-hero-info-after")]),"home-hero-actions-after":f(()=>[c(n.$slots,"home-hero-actions-after")]),"home-hero-image":f(()=>[c(n.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):h("",!0)}}),Hn={class:"box"},Bn={key:0,class:"icon"},Cn=["innerHTML"],En=["innerHTML"],Fn=["innerHTML"],On={key:4,class:"link-text"},Dn={class:"link-text-value"},Gn=m({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(e){return(t,n)=>(o(),p(B,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:f(()=>[v("article",Hn,[typeof e.icon=="object"&&e.icon.wrap?(o(),u("div",Bn,[g(Z,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(o(),p(Z,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(o(),u("div",{key:2,class:"icon",innerHTML:e.icon},null,8,Cn)):h("",!0),v("h2",{class:"title",innerHTML:e.title},null,8,En),e.details?(o(),u("p",{key:3,class:"details",innerHTML:e.details},null,8,Fn)):h("",!0),e.linkText?(o(),u("div",On,[v("p",Dn,[U(L(e.linkText)+" ",1),n[0]||(n[0]=v("span",{class:"vpi-arrow-right link-text-icon"},null,-1))])])):h("",!0)])]),_:1},8,["href","rel","target","tag"]))}}),Kn=_(Gn,[["__scopeId","data-v-5219619b"]]),Rn={key:0,class:"VPFeatures"},Un={class:"container"},jn={class:"items"},Wn=m({__name:"VPFeatures",props:{features:{}},setup(e){const t=e,n=k(()=>{const a=t.features.length;if(a){if(a===2)return"grid-2";if(a===3)return"grid-3";if(a%3===0)return"grid-6";if(a>3)return"grid-4"}else return});return(a,s)=>e.features?(o(),u("div",Rn,[v("div",Un,[v("div",jn,[(o(!0),u(S,null,x(e.features,r=>(o(),u("div",{key:r.title,class:P(["item",[n.value]])},[g(Kn,{icon:r.icon,title:r.title,details:r.details,link:r.link,"link-text":r.linkText,rel:r.rel,target:r.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):h("",!0)}}),zn=_(Wn,[["__scopeId","data-v-a6181336"]]),qn=m({__name:"VPHomeFeatures",setup(e){const{frontmatter:t}=$();return(n,a)=>i(t).features?(o(),p(zn,{key:0,class:"VPHomeFeatures",features:i(t).features},null,8,["features"])):h("",!0)}}),Jn=m({__name:"VPHomeContent",setup(e){const{width:t}=Je({initialWidth:0,includeScrollbar:!1});return(n,a)=>(o(),u("div",{class:"vp-doc container",style:Te(i(t)?{"--vp-offset":`calc(50% - ${i(t)/2}px)`}:{})},[c(n.$slots,"default",{},void 0,!0)],4))}}),Yn=_(Jn,[["__scopeId","data-v-8e2d4988"]]),Xn=m({__name:"VPHome",setup(e){const{frontmatter:t,theme:n}=$();return(a,s)=>{const r=j("Content");return o(),u("div",{class:P(["VPHome",{"external-link-icon-enabled":i(n).externalLinkIcon}])},[c(a.$slots,"home-hero-before",{},void 0,!0),g(In,null,{"home-hero-info-before":f(()=>[c(a.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":f(()=>[c(a.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":f(()=>[c(a.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":f(()=>[c(a.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":f(()=>[c(a.$slots,"home-hero-image",{},void 0,!0)]),_:3}),c(a.$slots,"home-hero-after",{},void 0,!0),c(a.$slots,"home-features-before",{},void 0,!0),g(qn),c(a.$slots,"home-features-after",{},void 0,!0),i(t).markdownStyles!==!1?(o(),p(Yn,{key:0},{default:f(()=>[g(r)]),_:1})):(o(),p(r,{key:1}))],2)}}}),Qn=_(Xn,[["__scopeId","data-v-8b561e3d"]]),Zn={},ea={class:"VPPage"};function ta(e,t){const n=j("Content");return o(),u("div",ea,[c(e.$slots,"page-top"),g(n),c(e.$slots,"page-bottom")])}const na=_(Zn,[["render",ta]]),aa=m({__name:"VPContent",setup(e){const{page:t,frontmatter:n}=$(),{isHome:a,hasSidebar:s}=G();return(r,d)=>(o(),u("div",{class:P(["VPContent",{"has-sidebar":i(s),"is-home":i(a)}]),id:"VPContent"},[i(t).isNotFound?c(r.$slots,"not-found",{key:0},()=>[g(kt)],!0):i(n).layout==="page"?(o(),p(na,{key:1},{"page-top":f(()=>[c(r.$slots,"page-top",{},void 0,!0)]),"page-bottom":f(()=>[c(r.$slots,"page-bottom",{},void 0,!0)]),_:3})):i(n).layout==="home"?(o(),p(Qn,{key:2},{"home-hero-before":f(()=>[c(r.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":f(()=>[c(r.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":f(()=>[c(r.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":f(()=>[c(r.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":f(()=>[c(r.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":f(()=>[c(r.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":f(()=>[c(r.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":f(()=>[c(r.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":f(()=>[c(r.$slots,"home-features-after",{},void 0,!0)]),_:3})):i(n).layout&&i(n).layout!=="doc"?(o(),p(H(i(n).layout),{key:3})):(o(),p(kn,{key:4},{"doc-top":f(()=>[c(r.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":f(()=>[c(r.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":f(()=>[c(r.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":f(()=>[c(r.$slots,"doc-before",{},void 0,!0)]),"doc-after":f(()=>[c(r.$slots,"doc-after",{},void 0,!0)]),"aside-top":f(()=>[c(r.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":f(()=>[c(r.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":f(()=>[c(r.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":f(()=>[c(r.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":f(()=>[c(r.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":f(()=>[c(r.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}}),sa=_(aa,[["__scopeId","data-v-c87f25bf"]]),oa={class:"container"},ia=["innerHTML"],ra=["innerHTML"],la=m({__name:"VPFooter",setup(e){const{theme:t,frontmatter:n}=$(),{hasSidebar:a}=G();return(s,r)=>i(t).footer&&i(n).footer!==!1?(o(),u("footer",{key:0,class:P(["VPFooter",{"has-sidebar":i(a)}])},[v("div",oa,[i(t).footer.message?(o(),u("p",{key:0,class:"message",innerHTML:i(t).footer.message},null,8,ia)):h("",!0),i(t).footer.copyright?(o(),u("p",{key:1,class:"copyright",innerHTML:i(t).footer.copyright},null,8,ra)):h("",!0)])],2)):h("",!0)}}),ca=_(la,[["__scopeId","data-v-c3855bb3"]]),ua={class:"menu-text"},da={class:"header"},va={class:"outline"},fa=m({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(e){const t=e,{theme:n}=$(),a=V(!1),s=V(0),r=V(),d=V();function l(w){r.value?.contains(w.target)||(a.value=!1)}D(a,w=>{if(w){document.addEventListener("click",l);return}document.removeEventListener("click",l)}),ie("Escape",()=>{a.value=!1}),Se(()=>{a.value=!1});function b(){a.value=!a.value,s.value=window.innerHeight+Math.min(window.scrollY-t.navHeight,0)}function y(w){w.target.classList.contains("outline-link")&&(d.value&&(d.value.style.transition="none"),we(()=>{a.value=!1}))}function N(){a.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return(w,C)=>(o(),u("div",{class:"VPLocalNavOutlineDropdown",style:Te({"--vp-vh":s.value+"px"}),ref_key:"main",ref:r},[e.headers.length>0?(o(),u("button",{key:0,onClick:b,class:P({open:a.value})},[v("span",ua,L(i(He)(i(n))),1),C[0]||(C[0]=v("span",{class:"vpi-chevron-right icon"},null,-1))],2)):(o(),u("button",{key:1,onClick:N},L(i(n).returnToTopLabel||"Return to top"),1)),g(ve,{name:"flyout"},{default:f(()=>[a.value?(o(),u("div",{key:0,ref_key:"items",ref:d,class:"items",onClick:y},[v("div",da,[v("a",{class:"top-link",href:"#",onClick:N},L(i(n).returnToTopLabel||"Return to top"),1)]),v("div",va,[g(Ce,{headers:e.headers},null,8,["headers"])])],512)):h("",!0)]),_:1})],4))}}),ha=_(fa,[["__scopeId","data-v-0bf0e06f"]]),ma={class:"container"},pa=["aria-expanded"],ka={class:"menu-text"},ga=m({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(e){const{theme:t}=$(),{isHome:n,hasSidebar:a,headers:s,hasLocalNav:r}=G(),{y:d}=Me(),l=V(0);R(()=>{l.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))});const b=k(()=>({VPLocalNav:!0,"has-sidebar":a.value,empty:!r.value,fixed:!r.value&&!a.value}));return(y,N)=>!i(n)&&(i(r)||i(a)||i(d)>=l.value)?(o(),u("div",{key:0,class:P(b.value)},[v("div",ma,[i(a)?(o(),u("button",{key:0,class:"menu","aria-expanded":e.open,"aria-controls":"VPSidebarNav",onClick:N[0]||(N[0]=w=>y.$emit("open-menu"))},[N[1]||(N[1]=v("span",{class:"vpi-align-left menu-icon"},null,-1)),v("span",ka,L(i(t).sidebarMenuLabel||"Menu"),1)],8,pa)):h("",!0),g(ha,{headers:i(s),navHeight:l.value},null,8,["headers","navHeight"])])],2)):h("",!0)}}),_a=_(ga,[["__scopeId","data-v-db738f89"]]);function ba(){const e=V(!1);function t(){e.value=!0,window.addEventListener("resize",s)}function n(){e.value=!1,window.removeEventListener("resize",s)}function a(){e.value?n():t()}function s(){window.outerWidth>=768&&n()}const r=ne();return D(()=>r.path,n),{isScreenOpen:e,openScreen:t,closeScreen:n,toggleScreen:a}}const pe=Symbol("nav"),$a={},ya={class:"VPSwitch",type:"button",role:"switch"},Pa={class:"check"},La={key:0,class:"icon"};function Va(e,t){return o(),u("button",ya,[v("span",Pa,[e.$slots.default?(o(),u("span",La,[c(e.$slots,"default",{},void 0,!0)])):h("",!0)])])}const Sa=_($a,[["render",Va],["__scopeId","data-v-1d5665e3"]]),Na=m({__name:"VPSwitchAppearance",setup(e){const{isDark:t,theme:n}=$(),a=ae("toggle-appearance",()=>{t.value=!t.value}),s=V("");return Ve(()=>{s.value=t.value?n.value.lightModeSwitchTitle||"Switch to light theme":n.value.darkModeSwitchTitle||"Switch to dark theme"}),(r,d)=>(o(),p(Sa,{title:s.value,class:"VPSwitchAppearance","aria-checked":i(t),onClick:i(a)},{default:f(()=>[...d[0]||(d[0]=[v("span",{class:"vpi-sun sun"},null,-1),v("span",{class:"vpi-moon moon"},null,-1)])]),_:1},8,["title","aria-checked","onClick"]))}}),ke=_(Na,[["__scopeId","data-v-5337faa4"]]),Ta={key:0,class:"VPNavBarAppearance"},wa=m({__name:"VPNavBarAppearance",setup(e){const{site:t}=$();return(n,a)=>i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(o(),u("div",Ta,[g(ke)])):h("",!0)}}),Ma=_(wa,[["__scopeId","data-v-6c893767"]]),ge=V();let Ee=!1,oe=0;function xa(e){const t=V(!1);if(Y){!Ee&&Aa(),oe++;const n=D(ge,a=>{a===e.el.value||e.el.value?.contains(a)?(t.value=!0,e.onFocus?.()):(t.value=!1,e.onBlur?.())});he(()=>{n(),oe--,oe||Ia()})}return Ye(t)}function Aa(){document.addEventListener("focusin",Fe),Ee=!0,ge.value=document.activeElement}function Ia(){document.removeEventListener("focusin",Fe)}function Fe(){ge.value=document.activeElement}const Ha={class:"VPMenuLink"},Ba=["innerHTML"],Ca=m({inheritAttrs:!1,__name:"VPMenuLink",props:{item:{}},setup(e){const t=e,{page:n}=$(),a=k(()=>typeof t.item.link=="function"?t.item.link(n.value):t.item.link);return(s,r)=>(o(),u("div",Ha,[g(B,F(s.$attrs,{class:{active:i(O)(i(n).relativePath,e.item.activeMatch||a.value,!!e.item.activeMatch)},href:a.value,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon}),{default:f(()=>[v("span",{innerHTML:e.item.text},null,8,Ba)]),_:1},16,["class","href","target","rel","no-icon"])]))}}),se=_(Ca,[["__scopeId","data-v-faf5b206"]]),Ea={class:"VPMenuGroup"},Fa={key:0,class:"title"},Oa=m({__name:"VPMenuGroup",props:{text:{},items:{}},setup(e){return(t,n)=>(o(),u("div",Ea,[e.text?(o(),u("p",Fa,L(e.text),1)):h("",!0),(o(!0),u(S,null,x(e.items,a=>(o(),u(S,{key:JSON.stringify(a)},["link"in a?(o(),p(se,{key:0,item:a},null,8,["item"])):h("",!0)],64))),128))]))}}),Da=_(Oa,[["__scopeId","data-v-1963e1bb"]]),Ga={class:"VPMenu"},Ka={key:0,class:"items"},Ra=m({__name:"VPMenu",props:{items:{}},setup(e){return(t,n)=>(o(),u("div",Ga,[e.items?(o(),u("div",Ka,[(o(!0),u(S,null,x(e.items,a=>(o(),u(S,{key:JSON.stringify(a)},["link"in a?(o(),p(se,{key:0,item:a},null,8,["item"])):"component"in a?(o(),p(H(a.component),F({key:1,ref_for:!0},a.props),null,16)):(o(),p(Da,{key:2,text:a.text,items:a.items},null,8,["text","items"]))],64))),128))])):h("",!0),c(t.$slots,"default",{},void 0,!0)]))}}),Ua=_(Ra,[["__scopeId","data-v-25a6cce8"]]),ja=["aria-expanded","aria-label"],Wa={key:0,class:"text"},za=["innerHTML"],qa={key:1,class:"vpi-more-horizontal icon"},Ja={class:"menu"},Ya=m({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(e){const t=V(!1),n=V();xa({el:n,onBlur:a});function a(){t.value=!1}return(s,r)=>(o(),u("div",{class:"VPFlyout",ref_key:"el",ref:n,onMouseenter:r[1]||(r[1]=d=>t.value=!0),onMouseleave:r[2]||(r[2]=d=>t.value=!1)},[v("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":t.value,"aria-label":e.label,onClick:r[0]||(r[0]=d=>t.value=!t.value)},[e.button||e.icon?(o(),u("span",Wa,[e.icon?(o(),u("span",{key:0,class:P([e.icon,"option-icon"])},null,2)):h("",!0),e.button?(o(),u("span",{key:1,innerHTML:e.button},null,8,za)):h("",!0),r[3]||(r[3]=v("span",{class:"vpi-chevron-down text-icon"},null,-1))])):(o(),u("span",qa))],8,ja),v("div",Ja,[g(Ua,{items:e.items},{default:f(()=>[c(s.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}}),_e=_(Ya,[["__scopeId","data-v-42cb505d"]]),Xa=["href","aria-label","rel","innerHTML"],Qa=m({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{},me:{type:Boolean}},setup(e){const t=e,n=V();R(async()=>{await we();const s=n.value?.children[0];s instanceof HTMLElement&&s.className.startsWith("vpi-social-")&&(getComputedStyle(s).maskImage||getComputedStyle(s).webkitMaskImage)==="none"&&s.style.setProperty("--icon",`url('https://api.iconify.design/simple-icons/${t.icon}.svg')`)});const a=k(()=>typeof t.icon=="object"?t.icon.svg:``);return(s,r)=>(o(),u("a",{ref_key:"el",ref:n,class:"VPSocialLink no-icon",href:e.link,"aria-label":e.ariaLabel??(typeof e.icon=="string"?e.icon:""),target:"_blank",rel:e.me?"me noopener":"noopener",innerHTML:a.value},null,8,Xa))}}),Za=_(Qa,[["__scopeId","data-v-591a6b30"]]),es={class:"VPSocialLinks"},ts=m({__name:"VPSocialLinks",props:{links:{},me:{type:Boolean,default:!0}},setup(e){return(t,n)=>(o(),u("div",es,[(o(!0),u(S,null,x(e.links,({link:a,icon:s,ariaLabel:r})=>(o(),p(Za,{key:a,icon:s,link:a,ariaLabel:r,me:e.me},null,8,["icon","link","ariaLabel","me"]))),128))]))}}),be=_(ts,[["__scopeId","data-v-d07f11e6"]]),ns={key:0,class:"group translations"},as={class:"trans-title"},ss={key:1,class:"group"},os={class:"item appearance"},is={class:"label"},rs={class:"appearance-action"},ls={key:2,class:"group"},cs={class:"item social-links"},us=m({__name:"VPNavBarExtra",setup(e){const{site:t,theme:n}=$(),{localeLinks:a,currentLang:s}=X({correspondingLink:!0}),r=k(()=>a.value.length&&s.value.label||t.value.appearance||n.value.socialLinks);return(d,l)=>r.value?(o(),p(_e,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:f(()=>[i(a).length&&i(s).label?(o(),u("div",ns,[v("p",as,L(i(s).label),1),(o(!0),u(S,null,x(i(a),b=>(o(),p(se,{key:b.link,item:b,lang:b.lang,dir:b.dir},null,8,["item","lang","dir"]))),128))])):h("",!0),i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(o(),u("div",ss,[v("div",os,[v("p",is,L(i(n).darkModeSwitchLabel||"Appearance"),1),v("div",rs,[g(ke)])])])):h("",!0),i(n).socialLinks?(o(),u("div",ls,[v("div",cs,[g(be,{class:"social-links-list",links:i(n).socialLinks},null,8,["links"])])])):h("",!0)]),_:1})):h("",!0)}}),ds=_(us,[["__scopeId","data-v-bf2fac68"]]),vs=["aria-expanded"],fs=m({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(e){return(t,n)=>(o(),u("button",{type:"button",class:P(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:n[0]||(n[0]=a=>t.$emit("click"))},[...n[1]||(n[1]=[v("span",{class:"container"},[v("span",{class:"top"}),v("span",{class:"middle"}),v("span",{class:"bottom"})],-1)])],10,vs))}}),hs=_(fs,[["__scopeId","data-v-e5dd9c1c"]]),ms=["innerHTML"],ps=m({__name:"VPNavBarMenuLink",props:{item:{}},setup(e){const t=e,{page:n}=$(),a=k(()=>typeof t.item.link=="function"?t.item.link(n.value):t.item.link);return(s,r)=>(o(),p(B,{class:P({VPNavBarMenuLink:!0,active:i(O)(i(n).relativePath,e.item.activeMatch||a.value,!!e.item.activeMatch)}),href:a.value,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,tabindex:"0"},{default:f(()=>[v("span",{innerHTML:e.item.text},null,8,ms)]),_:1},8,["class","href","target","rel","no-icon"]))}}),ks=_(ps,[["__scopeId","data-v-52a1d768"]]),gs=m({__name:"VPNavBarMenuGroup",props:{item:{}},setup(e){const t=e,{page:n}=$(),a=r=>"component"in r?!1:"link"in r?O(n.value.relativePath,typeof r.link=="function"?r.link(n.value):r.link,!!t.item.activeMatch):r.items.some(a),s=k(()=>a(t.item));return(r,d)=>(o(),p(_e,{class:P({VPNavBarMenuGroup:!0,active:i(O)(i(n).relativePath,e.item.activeMatch,!!e.item.activeMatch)||s.value}),button:e.item.text,items:e.item.items},null,8,["class","button","items"]))}}),_s={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},bs=m({__name:"VPNavBarMenu",setup(e){const{theme:t}=$();return(n,a)=>i(t).nav?(o(),u("nav",_s,[a[0]||(a[0]=v("span",{id:"main-nav-aria-label",class:"visually-hidden"}," Main Navigation ",-1)),(o(!0),u(S,null,x(i(t).nav,s=>(o(),u(S,{key:JSON.stringify(s)},["link"in s?(o(),p(ks,{key:0,item:s},null,8,["item"])):"component"in s?(o(),p(H(s.component),F({key:1,ref_for:!0},s.props),null,16)):(o(),p(gs,{key:2,item:s},null,8,["item"]))],64))),128))])):h("",!0)}}),$s=_(bs,[["__scopeId","data-v-39714824"]]);function ys(e){const t=e.mode??"auto",n=Ps(e),a=e.askAi,s=!!(a&&typeof a=="object"&&a.sidePanel);switch(t){case"sidePanel":return{mode:t,showKeywordSearch:!1,useSidePanel:!0};case"hybrid":return n||console.error('[vitepress] mode: "hybrid" requires keyword search credentials (appId, apiKey, indexName).'),{mode:t,showKeywordSearch:n,useSidePanel:!0};case"modal":return{mode:t,showKeywordSearch:n,useSidePanel:!1};default:return{mode:"auto",showKeywordSearch:n,useSidePanel:s}}}function Ps(e){return!!(e.appId&&e.apiKey&&e.indexName)}function Oe(e,t){return[...(Array.isArray(e)?e:e?[e]:[]).map(s=>Array.isArray(s)?s.filter(r=>typeof r=="string"&&!r.startsWith("lang:")):s).filter(s=>typeof s=="string"?!s.startsWith("lang:"):Array.isArray(s)&&s.length>0),`lang:${t}`]}function Ls(e,t,n){const a=typeof e=="string",s=!a&&e.searchParameters?{...e.searchParameters}:void 0,r=s?.facetFilters??t.searchParameters?.facetFilters,d=Oe(r,n),l={...s,facetFilters:d.length?d:void 0},b={...a?{}:e,indexName:a?t.indexName:e.indexName,apiKey:a?t.apiKey:e.apiKey,appId:a?t.appId:e.appId,assistantId:a?e:e.assistantId};return Object.values(l).some(y=>y!=null)&&(b.searchParameters=l),b}function Vs(e,t,n){e=De(e,e.locales?.[t]||{});const a=Oe(e.searchParameters?.facetFilters,n),s=e.askAi?Ls(e.askAi,e,n):void 0;return{...e,searchParameters:{...e.searchParameters,facetFilters:a},askAi:s}}function De(e,t){const n={...e};for(const a in t){const s=t[a];if(s!==void 0){if(a==="searchParameters"){n[a]=s;continue}Pe(s)&&Pe(n[a])?n[a]=De(n[a],s):n[a]=s}}return delete n.locales,n}function Ss(e,t=(n,a)=>JSON.stringify(n)===JSON.stringify(a)){return k(n=>{const a=e();return n===void 0||!t(n,a)?a:n})}const Ns={},Ts={type:"button",class:"VPNavBarAskAiButton"};function ws(e,t){return o(),u("button",Ts,[...t[0]||(t[0]=[v("span",{class:"vpi-sparkles","aria-hidden":"true"},null,-1)])])}const Ms=_(Ns,[["render",ws],["__scopeId","data-v-4eb17e89"]]),xs={type:"button",class:"VPNavBarSearchButton"},As={class:"text"},Is=m({__name:"VPNavBarSearchButton",props:{text:{}},setup(e){return(t,n)=>(o(),u("button",xs,[n[0]||(n[0]=v("span",{class:"vpi-search","aria-hidden":"true"},null,-1)),v("span",As,L(e.text),1),n[1]||(n[1]=v("span",{class:"keys","aria-hidden":"true"},[v("kbd",{class:"key-cmd"},"⌘"),v("kbd",{class:"key-ctrl"},"Ctrl"),v("kbd",null,"K")],-1))]))}}),Le=_(Is,[["__scopeId","data-v-baa3be99"]]),Hs={class:"VPNavBarSearch"},Bs=m({__name:"VPNavBarSearch",setup(e){const t=Xe(()=>Qe(()=>import("./VPLocalSearchBox.BOPGMrlA.js"),__vite__mapDeps([0,1]))),n=()=>null,{theme:a,localeIndex:s,lang:r}=$(),d="local",l=Ss(()=>Vs(a.value.search?.options||{},s.value,r.value)),b=k(()=>ys(l.value)),y=k(()=>{if(!b.value.useSidePanel)return null;const T=l.value.askAi;return!T||typeof T=="string"||!T.sidePanel?null:T.sidePanel===!0?{}:T.sidePanel}),N=k(()=>y.value?.keyboardShortcuts?.["Ctrl/Cmd+I"]!==!1),w=V(null);let C=0;const A=V(!1),I=V(!1);R(()=>{});function W(T){A.value||(A.value=!0),w.value={target:T,nonce:++C}}const E=V(!1);ie("k",T=>{(T.ctrlKey||T.metaKey)&&(T.preventDefault(),E.value=!0)}),ie("/",T=>{z(T)||(T.preventDefault(),E.value=!0)});function z(T){const M=T.target,K=M.tagName;return M.isContentEditable||K==="INPUT"||K==="SELECT"||K==="TEXTAREA"}return(T,M)=>(o(),u("div",Hs,[i(d)==="algolia"?(o(),u(S,{key:0},[b.value.showKeywordSearch?(o(),p(Le,{key:0,text:i(l).translations?.button?.buttonText||"Search","aria-label":i(l).translations?.button?.buttonAriaLabel||"Search","aria-keyshortcuts":"/ control+k meta+k",onClick:M[0]||(M[0]=K=>W("search"))},null,8,["text","aria-label"])):h("",!0),y.value?(o(),p(Ms,{key:1,"aria-label":y.value.button?.translations?.buttonAriaLabel||"Ask AI","aria-keyshortcuts":N.value?"control+i meta+i":void 0,onClick:M[1]||(M[1]=K=>I.value?W("toggleAskAi"):W("askAi"))},null,8,["aria-label","aria-keyshortcuts"])):h("",!0),A.value?(o(),p(i(n),{key:2,"algolia-options":i(l),"open-request":w.value,onVnodeBeforeMount:M[2]||(M[2]=K=>I.value=!0)},null,8,["algolia-options","open-request"])):h("",!0)],64)):i(d)==="local"?(o(),u(S,{key:1},[g(Le,{text:i(l).translations?.button?.buttonText||"Search","aria-label":i(l).translations?.button?.buttonAriaLabel||"Search","aria-keyshortcuts":"/ control+k meta+k",onClick:M[3]||(M[3]=K=>E.value=!0)},null,8,["text","aria-label"]),E.value?(o(),p(i(t),{key:0,onClose:M[4]||(M[4]=K=>E.value=!1)})):h("",!0)],64)):h("",!0)]))}}),Cs=_(Bs,[["__scopeId","data-v-2fc7f2c6"]]),Es=m({__name:"VPNavBarSocialLinks",setup(e){const{theme:t}=$();return(n,a)=>i(t).socialLinks?(o(),p(be,{key:0,class:"VPNavBarSocialLinks",links:i(t).socialLinks},null,8,["links"])):h("",!0)}}),Fs=_(Es,[["__scopeId","data-v-0394ad82"]]),Os=["href","rel","target"],Ds=["innerHTML"],Gs={key:2},Ks=m({__name:"VPNavBarTitle",setup(e){const{site:t,theme:n}=$(),{hasSidebar:a}=G(),{currentLang:s}=X(),r=k(()=>typeof n.value.logoLink=="string"?n.value.logoLink:n.value.logoLink?.link),d=k(()=>typeof n.value.logoLink=="string"?void 0:n.value.logoLink?.rel),l=k(()=>typeof n.value.logoLink=="string"?void 0:n.value.logoLink?.target);return(b,y)=>(o(),u("div",{class:P(["VPNavBarTitle",{"has-sidebar":i(a)}])},[v("a",{class:"title",href:r.value??i(me)(i(s).link),rel:d.value,target:l.value},[c(b.$slots,"nav-bar-title-before",{},void 0,!0),i(n).logo?(o(),p(Z,{key:0,class:"logo",image:i(n).logo},null,8,["image"])):h("",!0),i(n).siteTitle?(o(),u("span",{key:1,innerHTML:i(n).siteTitle},null,8,Ds)):i(n).siteTitle===void 0?(o(),u("span",Gs,L(i(t).title),1)):h("",!0),c(b.$slots,"nav-bar-title-after",{},void 0,!0)],8,Os)],2))}}),Rs=_(Ks,[["__scopeId","data-v-1e38c6bc"]]),Us={class:"items"},js={class:"title"},Ws=m({__name:"VPNavBarTranslations",setup(e){const{theme:t}=$(),{localeLinks:n,currentLang:a}=X({correspondingLink:!0});return(s,r)=>i(n).length&&i(a).label?(o(),p(_e,{key:0,class:"VPNavBarTranslations",icon:"vpi-languages",label:i(t).langMenuLabel||"Change language"},{default:f(()=>[v("div",Us,[v("p",js,L(i(a).label),1),(o(!0),u(S,null,x(i(n),d=>(o(),p(se,{key:d.link,item:d,lang:d.lang,dir:d.dir},null,8,["item","lang","dir"]))),128))])]),_:1},8,["label"])):h("",!0)}}),zs=_(Ws,[["__scopeId","data-v-4c1766e2"]]),qs={class:"wrapper"},Js={class:"container"},Ys={class:"title"},Xs={class:"content"},Qs={class:"content-body"},Zs=m({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(e){const{y:t}=Me(),{isHome:n,hasSidebar:a}=G();return(s,r)=>(o(),u("div",{class:P(["VPNavBar",{"has-sidebar":i(a),home:i(n),top:i(t)===0,"screen-open":e.isScreenOpen}])},[v("div",qs,[v("div",Js,[v("div",Ys,[g(Rs,null,{"nav-bar-title-before":f(()=>[c(s.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":f(()=>[c(s.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),v("div",Xs,[v("div",Qs,[c(s.$slots,"nav-bar-content-before",{},void 0,!0),g(Cs,{class:"search"}),g($s,{class:"menu"}),g(zs,{class:"translations"}),g(Ma,{class:"appearance"}),g(Fs,{class:"social-links"}),g(ds,{class:"extra"}),c(s.$slots,"nav-bar-content-after",{},void 0,!0),g(hs,{class:"hamburger",active:e.isScreenOpen,onClick:r[0]||(r[0]=d=>s.$emit("toggle-screen"))},null,8,["active"])])])])]),r[1]||(r[1]=v("div",{class:"divider"},[v("div",{class:"divider-line"})],-1))],2))}}),eo=_(Zs,[["__scopeId","data-v-9ca1369d"]]),to={key:0,class:"VPNavScreenAppearance"},no={class:"text"},ao=m({__name:"VPNavScreenAppearance",setup(e){const{site:t,theme:n}=$();return(a,s)=>i(t).appearance&&i(t).appearance!=="force-dark"&&i(t).appearance!=="force-auto"?(o(),u("div",to,[v("p",no,L(i(n).darkModeSwitchLabel||"Appearance"),1),g(ke)])):h("",!0)}}),so=_(ao,[["__scopeId","data-v-b44890b2"]]),oo=["innerHTML"],io=m({__name:"VPNavScreenMenuLink",props:{item:{}},setup(e){const t=e,{page:n}=$(),a=k(()=>typeof t.item.link=="function"?t.item.link(n.value):t.item.link),s=k(()=>O(n.value.relativePath,t.item.activeMatch||a.value,!!t.item.activeMatch)),{closeScreen:r}=ae(pe);return(d,l)=>(o(),p(B,{class:P({VPNavScreenMenuLink:!0,active:s.value}),href:a.value,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,onClick:i(r)},{default:f(()=>[v("span",{innerHTML:e.item.text},null,8,oo)]),_:1},8,["class","href","target","rel","no-icon","onClick"]))}}),ro=_(io,[["__scopeId","data-v-b924ab8a"]]),lo=["innerHTML"],co=m({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(e){const t=e,{page:n}=$(),a=k(()=>typeof t.item.link=="function"?t.item.link(n.value):t.item.link),s=k(()=>O(n.value.relativePath,t.item.activeMatch||a.value,!!t.item.activeMatch)),{closeScreen:r}=ae(pe);return(d,l)=>(o(),p(B,{class:P({VPNavScreenMenuGroupLink:!0,active:s.value}),href:a.value,target:e.item.target,rel:e.item.rel,"no-icon":e.item.noIcon,onClick:i(r)},{default:f(()=>[v("span",{innerHTML:e.item.text},null,8,lo)]),_:1},8,["class","href","target","rel","no-icon","onClick"]))}}),Ge=_(co,[["__scopeId","data-v-ecf4b472"]]),uo={class:"VPNavScreenMenuGroupSection"},vo={key:0,class:"title"},fo=m({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(e){return(t,n)=>(o(),u("div",uo,[e.text?(o(),u("p",vo,L(e.text),1)):h("",!0),(o(!0),u(S,null,x(e.items,a=>(o(),p(Ge,{key:a.text,item:a},null,8,["item"]))),128))]))}}),ho=_(fo,[["__scopeId","data-v-4b7a798b"]]),mo=["aria-controls","aria-expanded"],po=["innerHTML"],ko=["id"],go={key:0,class:"item"},_o={key:1,class:"item"},bo={key:2,class:"group"},$o=m({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(e){const t=e,n=V(!1),a=k(()=>`NavScreenGroup-${t.text.replace(" ","-").toLowerCase()}`);function s(){n.value=!n.value}return(r,d)=>(o(),u("div",{class:P(["VPNavScreenMenuGroup",{open:n.value}])},[v("button",{class:"button","aria-controls":a.value,"aria-expanded":n.value,onClick:s},[v("span",{class:"button-text",innerHTML:e.text},null,8,po),d[0]||(d[0]=v("span",{class:"vpi-plus button-icon"},null,-1))],8,mo),v("div",{id:a.value,class:"items"},[(o(!0),u(S,null,x(e.items,l=>(o(),u(S,{key:JSON.stringify(l)},["link"in l?(o(),u("div",go,[g(Ge,{item:l},null,8,["item"])])):"component"in l?(o(),u("div",_o,[(o(),p(H(l.component),F({ref_for:!0},l.props,{"screen-menu":""}),null,16))])):(o(),u("div",bo,[g(ho,{text:l.text,items:l.items},null,8,["text","items"])]))],64))),128))],8,ko)],2))}}),yo=_($o,[["__scopeId","data-v-956364f9"]]),Po={key:0,class:"VPNavScreenMenu"},Lo=m({__name:"VPNavScreenMenu",setup(e){const{theme:t}=$();return(n,a)=>i(t).nav?(o(),u("nav",Po,[(o(!0),u(S,null,x(i(t).nav,s=>(o(),u(S,{key:JSON.stringify(s)},["link"in s?(o(),p(ro,{key:0,item:s},null,8,["item"])):"component"in s?(o(),p(H(s.component),F({key:1,ref_for:!0},s.props,{"screen-menu":""}),null,16)):(o(),p(yo,{key:2,text:s.text||"",items:s.items},null,8,["text","items"]))],64))),128))])):h("",!0)}}),Vo=m({__name:"VPNavScreenSocialLinks",setup(e){const{theme:t}=$();return(n,a)=>i(t).socialLinks?(o(),p(be,{key:0,class:"VPNavScreenSocialLinks",links:i(t).socialLinks},null,8,["links"])):h("",!0)}}),So={class:"list"},No=m({__name:"VPNavScreenTranslations",setup(e){const{localeLinks:t,currentLang:n}=X({correspondingLink:!0}),a=V(!1);function s(){a.value=!a.value}return(r,d)=>i(t).length&&i(n).label?(o(),u("div",{key:0,class:P(["VPNavScreenTranslations",{open:a.value}])},[v("button",{class:"title",onClick:s},[d[0]||(d[0]=v("span",{class:"vpi-languages icon lang"},null,-1)),U(" "+L(i(n).label)+" ",1),d[1]||(d[1]=v("span",{class:"vpi-chevron-down icon chevron"},null,-1))]),v("ul",So,[(o(!0),u(S,null,x(i(t),l=>(o(),u("li",{key:l.link,class:"item"},[g(B,{class:"link",href:l.link,lang:l.lang,dir:l.dir},{default:f(()=>[U(L(l.text),1)]),_:2},1032,["href","lang","dir"])]))),128))])],2)):h("",!0)}}),To=_(No,[["__scopeId","data-v-a4d9b172"]]),wo={key:0,class:"VPNavScreen",id:"VPNavScreen"},Mo={class:"container"},xo=m({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(e){const t=xe(Y?document.body:null);return(n,a)=>(o(),p(ve,{name:"fade",onEnter:a[0]||(a[0]=s=>t.value=!0),onAfterLeave:a[1]||(a[1]=s=>t.value=!1)},{default:f(()=>[e.open?(o(),u("div",wo,[v("div",Mo,[c(n.$slots,"nav-screen-content-before",{},void 0,!0),g(Lo,{class:"menu"}),g(To,{class:"translations"}),g(so,{class:"appearance"}),g(Vo,{class:"social-links"}),c(n.$slots,"nav-screen-content-after",{},void 0,!0)])])):h("",!0)]),_:3}))}}),Ao=_(xo,[["__scopeId","data-v-05f3d7bc"]]),Io={key:0,class:"VPNav"},Ho=m({__name:"VPNav",setup(e){const{isScreenOpen:t,closeScreen:n,toggleScreen:a}=ba(),{frontmatter:s}=$(),r=k(()=>s.value.navbar!==!1);return Ae(pe,{closeScreen:n}),ee(()=>{Y&&document.documentElement.classList.toggle("hide-nav",!r.value)}),(d,l)=>r.value?(o(),u("header",Io,[g(eo,{"is-screen-open":i(t),onToggleScreen:i(a)},{"nav-bar-title-before":f(()=>[c(d.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":f(()=>[c(d.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":f(()=>[c(d.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":f(()=>[c(d.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),g(Ao,{open:i(t)},{"nav-screen-content-before":f(()=>[c(d.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":f(()=>[c(d.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):h("",!0)}}),Bo=_(Ho,[["__scopeId","data-v-9f75dce3"]]),Co=["role","tabindex"],Eo={key:1,class:"items"},Fo=m({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(e){const t=e,{collapsed:n,collapsible:a,isLink:s,isActiveLink:r,hasActiveLink:d,hasChildren:l,toggle:b}=Mt(k(()=>t.item)),y=k(()=>l.value?"section":"div"),N=k(()=>s.value?"a":"div"),w=k(()=>l.value?t.depth+2===7?"p":`h${t.depth+2}`:"p"),C=k(()=>s.value?void 0:"button"),A=k(()=>[[`level-${t.depth}`],{collapsible:a.value},{collapsed:n.value},{"is-link":s.value},{"is-active":r.value},{"has-active":d.value}]);function I(E){"key"in E&&E.key!=="Enter"||!t.item.link&&b()}function W(){t.item.link&&b()}return(E,z)=>{const T=j("VPSidebarItem",!0);return o(),p(H(y.value),{class:P(["VPSidebarItem",A.value])},{default:f(()=>[e.item.text?(o(),u("div",F({key:0,class:"item",role:C.value},Ze(e.item.items?{click:I,keydown:I}:{},!0),{tabindex:e.item.items&&0}),[z[1]||(z[1]=v("div",{class:"indicator"},null,-1)),e.item.link?(o(),p(B,{key:0,tag:N.value,class:"link",href:e.item.link,rel:e.item.rel,target:e.item.target},{default:f(()=>[(o(),p(H(w.value),{class:"text",innerHTML:e.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(o(),p(H(w.value),{key:1,class:"text",innerHTML:e.item.text},null,8,["innerHTML"])),e.item.collapsed!=null&&e.item.items&&e.item.items.length?(o(),u("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:W,onKeydown:et(W,["enter"]),tabindex:"0"},[...z[0]||(z[0]=[v("span",{class:"vpi-chevron-right caret-icon"},null,-1)])],32)):h("",!0)],16,Co)):h("",!0),e.item.items&&e.item.items.length?(o(),u("div",Eo,[e.depth<5?(o(!0),u(S,{key:0},x(e.item.items,M=>(o(),p(T,{key:M.text,item:M,depth:e.depth+1},null,8,["item","depth"]))),128)):h("",!0)])):h("",!0)]),_:1},8,["class"])}}}),Oo=_(Fo,[["__scopeId","data-v-d81de50c"]]),Do=m({__name:"VPSidebarGroup",props:{items:{}},setup(e){const t=V(!0);let n=null;return R(()=>{n=setTimeout(()=>{n=null,t.value=!1},300)}),tt(()=>{n!=null&&(clearTimeout(n),n=null)}),(a,s)=>(o(!0),u(S,null,x(e.items,r=>(o(),u("div",{key:r.text,class:P(["group",{"no-transition":t.value}])},[g(Oo,{item:r,depth:0},null,8,["item"])],2))),128))}}),Go=_(Do,[["__scopeId","data-v-8d50c081"]]),Ko={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},Ro=m({__name:"VPSidebar",props:{open:{type:Boolean}},setup(e){const{sidebarGroups:t,hasSidebar:n}=G(),a=e,s=V(null),r=xe(Y?document.body:null);D([a,s],()=>{a.open?(r.value=!0,s.value?.focus()):r.value=!1},{immediate:!0,flush:"post"});const d=V(0);return D(t,()=>{d.value+=1},{deep:!0}),(l,b)=>i(n)?(o(),u("aside",{key:0,class:P(["VPSidebar",{open:e.open}]),ref_key:"navEl",ref:s,onClick:b[0]||(b[0]=nt(()=>{},["stop"]))},[b[2]||(b[2]=v("div",{class:"curtain"},null,-1)),v("nav",Ko,[b[1]||(b[1]=v("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),c(l.$slots,"sidebar-nav-before",{},void 0,!0),(o(),p(Go,{items:i(t),key:d.value},null,8,["items"])),c(l.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):h("",!0)}}),Uo=_(Ro,[["__scopeId","data-v-af661f50"]]),jo={href:"#VPContent",class:"VPSkipLink visually-hidden"},Wo=m({__name:"VPSkipLink",setup(e){const{theme:t}=$(),n=ne(),a=V();return D(()=>n.path,()=>a.value.focus()),(s,r)=>(o(),u(S,null,[v("span",{ref_key:"backToTop",ref:a,tabindex:"-1"},null,512),v("a",jo,L(i(t).skipToContentLabel||"Skip to content"),1)],64))}}),zo=_(Wo,[["__scopeId","data-v-331ec75c"]]),qo=m({__name:"Layout",setup(e){const{isOpen:t,open:n,close:a}=wt();xt({closeSidebar:a});const{frontmatter:s}=$(),r=at(),d=k(()=>!!r["home-hero-image"]);return Ae(Be,{heroImageSlotExists:d}),(l,b)=>{const y=j("Content");return i(s).layout!==!1?(o(),u("div",{key:0,class:P(["Layout",i(s).pageClass])},[c(l.$slots,"layout-top",{},void 0,!0),g(zo),g(rt,{class:"backdrop",show:i(t),onClick:i(a)},null,8,["show","onClick"]),g(Bo,null,{"nav-bar-title-before":f(()=>[c(l.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":f(()=>[c(l.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":f(()=>[c(l.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":f(()=>[c(l.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":f(()=>[c(l.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":f(()=>[c(l.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),g(_a,{open:i(t),onOpenMenu:i(n)},null,8,["open","onOpenMenu"]),g(Uo,{open:i(t)},{"sidebar-nav-before":f(()=>[c(l.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":f(()=>[c(l.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),g(sa,null,{"page-top":f(()=>[c(l.$slots,"page-top",{},void 0,!0)]),"page-bottom":f(()=>[c(l.$slots,"page-bottom",{},void 0,!0)]),"not-found":f(()=>[c(l.$slots,"not-found",{},void 0,!0)]),"home-hero-before":f(()=>[c(l.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":f(()=>[c(l.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":f(()=>[c(l.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":f(()=>[c(l.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":f(()=>[c(l.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":f(()=>[c(l.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":f(()=>[c(l.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":f(()=>[c(l.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":f(()=>[c(l.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":f(()=>[c(l.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":f(()=>[c(l.$slots,"doc-before",{},void 0,!0)]),"doc-after":f(()=>[c(l.$slots,"doc-after",{},void 0,!0)]),"doc-top":f(()=>[c(l.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":f(()=>[c(l.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":f(()=>[c(l.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":f(()=>[c(l.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":f(()=>[c(l.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":f(()=>[c(l.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":f(()=>[c(l.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":f(()=>[c(l.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),g(ca),c(l.$slots,"layout-bottom",{},void 0,!0)],2)):(o(),p(y,{key:1}))}}}),Jo=_(qo,[["__scopeId","data-v-1df9f90f"]]),Xo={Layout:Jo,enhanceApp:({app:e})=>{e.component("Badge",st)}};export{Xo as t,$ as u}; diff --git a/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.js b/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.js deleted file mode 100644 index 92137b9..0000000 --- a/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.js +++ /dev/null @@ -1,43 +0,0 @@ -import{_ as s,c as a,o as t,ak as e}from"./chunks/framework.DyoGesZS.js";const E=JSON.parse('{"title":"Adding an Exploration","description":"","frontmatter":{},"headers":[],"relativePath":"contributing/adding-an-exploration.md","filePath":"contributing/adding-an-exploration.md"}'),n={name:"contributing/adding-an-exploration.md"};function l(h,i,p,r,d,k){return t(),a("div",null,[...i[0]||(i[0]=[e(`

Adding an Exploration

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Overview

Each exploration lives in its own folder under src/explorations/ with two files: info.ts (metadata) and MyC.vue (interactive widget). This guide walks you through adding a new one.

Step 1: Create the Exploration Folder

Create a new folder in src/explorations/ named after your exploration's ID. The ID should be lowercase and hyphen-separated (e.g. eip-XXXX, erc-XXXX, or any descriptive identifier):

bash
mkdir src/explorations/eip-XXXX

Step 2: Create info.ts

Create src/explorations/eip-XXXX/info.ts with your exploration's metadata:

typescript
import type { Exploration } from '../REGISTRY'
-
-export const INFO: Exploration = {
-  id: 'eip-XXXX',
-  path: '/eip-XXXX-short-description',
-  title: 'Human-Readable Title',
-  infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX',
-  topics: ['fusaka'],
-  image: 'fusaka.webp',
-  introText:
-    '<b>What does this change?</b> ' +
-    'A brief introduction to the protocol change.',
-  usageText:
-    'Instructions on how to use the interactive widget below.',
-  poweredBy: [
-    { name: 'EthereumJS', href: 'https://github.com/ethereumjs/ethereumjs-monorepo' },
-  ],
-}

Fields:

FieldRequiredDescription
idYesUnique identifier, matches the folder name
pathYesURL path for the exploration page
titleYesDisplay title
infoURLYesLink to the specification or reference material
topicsNoArray of topic IDs this exploration belongs to
imageNoImage filename from src/assets/ (e.g. fusaka.webp)
introTextNoHTML-formatted introduction paragraph
usageTextNoHTML-formatted usage instructions
poweredByNoArray of { name, href } for library credits

Step 3: Create MyC.vue

Create src/explorations/eip-XXXX/MyC.vue with your interactive widget. Use ExplorationC as the wrapper component for consistent layout:

vue
<script setup lang="ts">
-import ExplorationC from '../ExplorationC.vue'
-import PoweredByC from '../PoweredByC.vue'
-import { INFO } from './info'
-
-const exploration = INFO
-const poweredBy = exploration.poweredBy ?? []
-</script>
-
-<template>
-  <div>
-    <ExplorationC :explorationId="exploration.id" :exploration="exploration">
-      <template v-slot:content>
-        <!-- Your interactive widget here -->
-        <p>Widget content goes here.</p>
-      </template>
-    </ExplorationC>
-    <PoweredByC :poweredBy="poweredBy" />
-  </div>
-</template>

The ExplorationC wrapper automatically renders the title, info link, intro text and usage text from your info.ts. You only need to provide the interactive content via the content slot.

For shared UI components (hex inputs, result displays, etc.), import from ../../components/ui/ and ../../components/precompiles/. Browse existing explorations for examples.

Step 4: Register in the Registry

Add one import line to src/explorations/REGISTRY.ts:

typescript
import { INFO as eipXXXX } from './eip-XXXX/info'
-
-export const EXPLORATIONS: Explorations = {
-  // ... existing explorations
-  [eipXXXX.id]: eipXXXX,
-}

That's it for registration. The router reads from EXPLORATIONS and automatically creates the route — no manual route configuration needed.

Step 5: Add Library Dependencies

If your widget needs an Ethereum library (or a custom fork), install it and import it only in your MyC.vue file:

bash
npm install some-library

If you need a library fork, see the Library Forks guide. Key rule: only import fork-specific libraries in your MyC.vue, never in shared code. This keeps each exploration's dependencies isolated via Vite's code splitting.

Step 6: Add Tests

Add a Cypress E2E test in cypress/e2e/ to verify basic widget functionality. Name it after your exploration ID (e.g. eip-XXXX.cy.ts).

Step 7: Build and Verify

bash
npm run build        # verify production build works
-npm run test:e2e     # run E2E tests

Quick Checklist

  • [ ] Created src/explorations/<id>/info.ts with metadata
  • [ ] Created src/explorations/<id>/MyC.vue with interactive widget
  • [ ] Added import and entry in src/explorations/REGISTRY.ts
  • [ ] Added library dependencies (if needed)
  • [ ] Added E2E test
  • [ ] Build passes (npm run build)
`,31)])])}const c=s(n,[["render",l]]);export{E as __pageData,c as default}; diff --git a/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.lean.js b/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.lean.js deleted file mode 100644 index a44bb20..0000000 --- a/dist/docs/assets/contributing_adding-an-exploration.md.Bx_AiHel.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,c as a,o as t,ak as e}from"./chunks/framework.DyoGesZS.js";const E=JSON.parse('{"title":"Adding an Exploration","description":"","frontmatter":{},"headers":[],"relativePath":"contributing/adding-an-exploration.md","filePath":"contributing/adding-an-exploration.md"}'),n={name:"contributing/adding-an-exploration.md"};function l(h,i,p,r,d,k){return t(),a("div",null,[...i[0]||(i[0]=[e("",31)])])}const c=s(n,[["render",l]]);export{E as __pageData,c as default}; diff --git a/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.js b/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.js deleted file mode 100644 index e1c9a23..0000000 --- a/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.js +++ /dev/null @@ -1,3 +0,0 @@ -import{_ as t,c as e,o as s,ak as a}from"./chunks/framework.DyoGesZS.js";const k=JSON.parse('{"title":"How to Contribute","description":"","frontmatter":{},"headers":[],"relativePath":"contributing/how-to-contribute.md","filePath":"contributing/how-to-contribute.md"}'),n={name:"contributing/how-to-contribute.md"};function o(l,i,r,h,d,p){return s(),e("div",null,[...i[0]||(i[0]=[a(`

How to Contribute

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Ways to Contribute

  • Add a new exploration — the most impactful contribution (see Adding an Exploration)
  • Improve an existing exploration — better examples, UI improvements, bug fixes
  • Improve documentation — fix typos, add guides, clarify explanations
  • Report issues — found a bug or have a suggestion? Open an issue

Development Workflow

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run linting and tests:
bash
npm run lf          # format + lint
-npm run test:unit   # unit tests
-npm run test:e2e    # E2E tests
  1. Submit a pull request

Code Style

  • Formatting: Prettier (auto-formatted with npm run format)
  • Linting: ESLint (auto-fixed with npm run lint)
  • Run both: npm run lf

Documentation

This documentation is built with VitePress. Docs live in the docs/ folder as standard markdown files.

To preview docs changes locally:

bash
npm run docs:dev
`,14)])])}const u=t(n,[["render",o]]);export{k as __pageData,u as default}; diff --git a/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.lean.js b/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.lean.js deleted file mode 100644 index 584258c..0000000 --- a/dist/docs/assets/contributing_how-to-contribute.md.C-N0YY81.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as t,c as e,o as s,ak as a}from"./chunks/framework.DyoGesZS.js";const k=JSON.parse('{"title":"How to Contribute","description":"","frontmatter":{},"headers":[],"relativePath":"contributing/how-to-contribute.md","filePath":"contributing/how-to-contribute.md"}'),n={name:"contributing/how-to-contribute.md"};function o(l,i,r,h,d,p){return s(),e("div",null,[...i[0]||(i[0]=[a("",14)])])}const u=t(n,[["render",o]]);export{k as __pageData,u as default}; diff --git a/dist/docs/assets/guide_architecture.md.qxw0U9Or.js b/dist/docs/assets/guide_architecture.md.qxw0U9Or.js deleted file mode 100644 index 9970494..0000000 --- a/dist/docs/assets/guide_architecture.md.qxw0U9Or.js +++ /dev/null @@ -1,31 +0,0 @@ -import{_ as i,c as a,o as e,ak as t}from"./chunks/framework.DyoGesZS.js";const k=JSON.parse('{"title":"Architecture","description":"","frontmatter":{},"headers":[],"relativePath":"guide/architecture.md","filePath":"guide/architecture.md"}'),n={name:"guide/architecture.md"};function r(l,s,o,p,h,d){return e(),a("div",null,[...s[0]||(s[0]=[t(`

Architecture

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Overview

Feel Your Protocol is a Vue 3 application built with Vite. The core idea is simple: each Ethereum protocol change gets its own interactive widget that runs real library code in the browser.

Tech Stack

Content Structure

Content is organized around two concepts:

  • Explorations — the core unit. Each exploration represents an interactive widget for a protocol change (EIP, ERC, or other research topic). Explorations live in src/explorations/.
  • Topics — group explorations by theme (e.g. "Fusaka" for an upcoming hardfork). Each exploration can belong to zero or more topics via the topics array. Topics are defined in src/explorations/TOPICS.ts.

The src/explorations/ Folder

This is the heart of the project. Each exploration is a self-contained folder:

src/explorations/
-├── REGISTRY.ts            # Assembles all explorations, exports EXPLORATIONS dict
-├── TOPICS.ts              # Topic definitions
-├── ExplorationC.vue       # Shared wrapper component (title, description, buttons)
-├── PoweredByC.vue         # Shared "powered by" footer
-├── eip-7594/
-│   ├── info.ts            # Metadata: id, title, path, infoURL, introText, usageText, …
-│   └── MyC.vue            # The interactive widget
-├── eip-7883/
-│   ├── info.ts
-│   └── MyC.vue
-└── eip-7951/
-    ├── info.ts
-    └── MyC.vue

Each info.ts exports a const INFO object typed as Exploration. The REGISTRY.ts imports all INFO constants and assembles them into the EXPLORATIONS dictionary. The router reads from this to automatically create routes — no manual route registration needed.

Exploration Metadata (info.ts)

Each exploration's info.ts contains all its metadata as a flat object:

typescript
import type { Exploration } from '../REGISTRY'
-
-export const INFO: Exploration = {
-  id: 'eip-XXXX',
-  path: '/eip-XXXX-short-description',
-  title: 'Human-Readable Title',
-  infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX',
-  topics: ['fusaka'],
-  image: 'fusaka.webp',
-  introText: 'HTML-formatted introduction.',
-  usageText: 'HTML-formatted usage instructions.',
-  poweredBy: [
-    { name: 'Library Name', href: 'https://github.com/...' },
-  ],
-}

Key Design Decisions

Folder-per-Exploration

Each exploration is fully self-contained in its own folder with info.ts (metadata) and MyC.vue (widget). This means:

  • Contributors can focus on a single folder
  • Adding a new exploration is a matter of creating a folder and adding one import to REGISTRY.ts
  • Each exploration's dependencies are isolated

Dynamic Views

There are no static per-exploration or per-topic view files. Instead:

  • ExplorationView.vue dynamically loads the correct MyC.vue using import.meta.glob() and defineAsyncComponent() based on the route name
  • TopicView.vue dynamically lists all explorations belonging to a topic

This keeps the views lean and avoids boilerplate when adding new content.

Route-Level Code Splitting

The ExplorationView.vue uses import.meta.glob() for lazy loading:

typescript
const componentModules = import.meta.glob('../explorations/*/MyC.vue')
-const ExplorationComponent = defineAsyncComponent(
-  componentModules[\`../explorations/\${explorationId}/MyC.vue\`]
-)

Each exploration is a separate chunk that's loaded on demand. Users only download the libraries needed for the page they visit.

Library Forks

Some exploration widgets require custom forks of Ethereum libraries (see Library Forks). The architecture ensures that different forks of the same library can coexist without conflicts, each isolated to its specific exploration route.

`,30)])])}const E=i(n,[["render",r]]);export{k as __pageData,E as default}; diff --git a/dist/docs/assets/guide_architecture.md.qxw0U9Or.lean.js b/dist/docs/assets/guide_architecture.md.qxw0U9Or.lean.js deleted file mode 100644 index 25c5664..0000000 --- a/dist/docs/assets/guide_architecture.md.qxw0U9Or.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,c as a,o as e,ak as t}from"./chunks/framework.DyoGesZS.js";const k=JSON.parse('{"title":"Architecture","description":"","frontmatter":{},"headers":[],"relativePath":"guide/architecture.md","filePath":"guide/architecture.md"}'),n={name:"guide/architecture.md"};function r(l,s,o,p,h,d){return e(),a("div",null,[...s[0]||(s[0]=[t("",30)])])}const E=i(n,[["render",r]]);export{k as __pageData,E as default}; diff --git a/dist/docs/assets/guide_getting-started.md.BHVwHBbn.js b/dist/docs/assets/guide_getting-started.md.BHVwHBbn.js deleted file mode 100644 index 2e895a8..0000000 --- a/dist/docs/assets/guide_getting-started.md.BHVwHBbn.js +++ /dev/null @@ -1,34 +0,0 @@ -import{_ as a,c as e,o as n,ak as i}from"./chunks/framework.DyoGesZS.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),t={name:"guide/getting-started.md"};function p(l,s,r,o,h,c){return n(),e("div",null,[...s[0]||(s[0]=[i(`

Getting Started

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

What is Feel Your Protocol?

Feel Your Protocol is an interactive website that lets you explore Ethereum protocol changes hands on. Instead of just reading specifications, you can interact with real Ethereum library code running directly in the browser.

Each protocol change — called an Exploration — gets its own page with a dedicated interactive widget. For example, the EIP-7883 page lets you experiment with ModExp gas cost changes interactively.

Explorations are grouped into Topics (e.g. "Fusaka" for the upcoming hardfork), making it easy to discover related protocol changes.

Prerequisites

  • Node.js v20.19+ or v22.12+
  • npm (comes with Node.js)

Setup

Clone the repository and install dependencies:

bash
git clone https://github.com/feelyourprotocol/website.git
-cd website
-npm install

Development

Start the website dev server:

bash
npm run dev

Start the docs dev server:

bash
npm run docs:dev

Building

Build both the website and documentation:

bash
npm run build          # website → dist/website
-npm run docs:build     # docs → dist/docs

Project Structure

website/
-├── src/
-│   ├── explorations/          # Explorations (the core content)
-│   │   ├── REGISTRY.ts        # Exploration registry, types, helper functions
-│   │   ├── TOPICS.ts          # Topic definitions and types
-│   │   ├── ExplorationC.vue   # Shared exploration wrapper component
-│   │   ├── PoweredByC.vue     # Shared "powered by" component
-│   │   ├── eip-7594/          # One folder per exploration
-│   │   │   ├── info.ts        #   Metadata (title, description, links, …)
-│   │   │   └── MyC.vue        #   Interactive widget component
-│   │   ├── eip-7883/
-│   │   │   ├── info.ts
-│   │   │   └── MyC.vue
-│   │   └── eip-7951/
-│   │       ├── info.ts
-│   │       └── MyC.vue
-│   ├── components/            # Shared UI and utility components
-│   │   ├── ui/                # Generic UI components
-│   │   ├── precompiles/       # Precompile-related shared components
-│   │   └── lib/               # Shared logic and utilities
-│   ├── views/                 # Route views
-│   │   ├── HomeView.vue
-│   │   ├── TopicView.vue
-│   │   ├── ExplorationView.vue
-│   │   └── TopicIntroView.vue
-│   └── router/                # Vue Router config
-├── docs/                      # Documentation (VitePress)
-├── cypress/                   # E2E tests
-└── dist/                      # Build output
-    ├── website/
-    └── docs/
`,21)])])}const k=a(t,[["render",p]]);export{g as __pageData,k as default}; diff --git a/dist/docs/assets/guide_getting-started.md.BHVwHBbn.lean.js b/dist/docs/assets/guide_getting-started.md.BHVwHBbn.lean.js deleted file mode 100644 index b33f90e..0000000 --- a/dist/docs/assets/guide_getting-started.md.BHVwHBbn.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as a,c as e,o as n,ak as i}from"./chunks/framework.DyoGesZS.js";const g=JSON.parse('{"title":"Getting Started","description":"","frontmatter":{},"headers":[],"relativePath":"guide/getting-started.md","filePath":"guide/getting-started.md"}'),t={name:"guide/getting-started.md"};function p(l,s,r,o,h,c){return n(),e("div",null,[...s[0]||(s[0]=[i("",21)])])}const k=a(t,[["render",p]]);export{g as __pageData,k as default}; diff --git a/dist/docs/assets/guide_library-forks.md.C-_4bMc7.js b/dist/docs/assets/guide_library-forks.md.C-_4bMc7.js deleted file mode 100644 index e94b960..0000000 --- a/dist/docs/assets/guide_library-forks.md.C-_4bMc7.js +++ /dev/null @@ -1,7 +0,0 @@ -import{_ as i,c as a,o as s,ak as r}from"./chunks/framework.DyoGesZS.js";const c=JSON.parse('{"title":"Library Forks","description":"","frontmatter":{},"headers":[],"relativePath":"guide/library-forks.md","filePath":"guide/library-forks.md"}'),n={name:"guide/library-forks.md"};function t(o,e,l,h,p,k){return s(),a("div",null,[...e[0]||(e[0]=[r(`

Library Forks

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Why Forks?

Feel Your Protocol runs real Ethereum library code in the browser. Some exploration widgets need a modified version of an existing library — for example, a version with a new precompile implementation, or experimental gas calculation changes that haven't been released yet.

Rather than waiting for upstream releases, we maintain targeted forks that are used by specific exploration pages.

How Forks Work

npm Package Aliases

Multiple versions of the same library coexist in package.json using npm aliases:

json
{
-  "dependencies": {
-    "@ethereumjs/evm": "^10.1.1-nightly.1",
-    "@ethereumjs/evm-experimental": "npm:@ethereumjs/evm@^11.0.0-fork.1"
-  }
-}

Each alias is a fully independent install. In code, you import the specific version you need:

typescript
import { EVM } from '@ethereumjs/evm'                    // standard
-import { EVM as EVMExp } from '@ethereumjs/evm-experimental'  // fork

Monorepo Libraries

For libraries from monorepos (like EthereumJS), where the target package has several intra-monorepo dependencies, we use pre-bundled ESM builds. The fork is bundled on the monorepo side with all internal dependencies resolved, producing a single ESM file with no internal wiring issues.

Per-Route Isolation

Each fork is only imported in its specific exploration's MyC.vue. Thanks to Vite's code splitting, the fork's code is only loaded when the user visits that exploration's page. Other pages are unaffected.

Adding a New Fork

  1. Fork the upstream library (or create a branch if you have access)
  2. Make your changes
  3. For single-package repos: add an npm alias pointing to your fork's git URL
  4. For monorepo packages: create a bundled build, then reference it
  5. Import the fork only in your exploration's MyC.vue file — never in shared code
  6. Document the fork in this page
`,17)])])}const g=i(n,[["render",t]]);export{c as __pageData,g as default}; diff --git a/dist/docs/assets/guide_library-forks.md.C-_4bMc7.lean.js b/dist/docs/assets/guide_library-forks.md.C-_4bMc7.lean.js deleted file mode 100644 index 52576bf..0000000 --- a/dist/docs/assets/guide_library-forks.md.C-_4bMc7.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,c as a,o as s,ak as r}from"./chunks/framework.DyoGesZS.js";const c=JSON.parse('{"title":"Library Forks","description":"","frontmatter":{},"headers":[],"relativePath":"guide/library-forks.md","filePath":"guide/library-forks.md"}'),n={name:"guide/library-forks.md"};function t(o,e,l,h,p,k){return s(),a("div",null,[...e[0]||(e[0]=[r("",17)])])}const g=i(n,[["render",t]]);export{c as __pageData,g as default}; diff --git a/dist/docs/assets/index.md.BJXo33p3.js b/dist/docs/assets/index.md.BJXo33p3.js deleted file mode 100644 index ed1d652..0000000 --- a/dist/docs/assets/index.md.BJXo33p3.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,c as n,o as a,j as e}from"./chunks/framework.DyoGesZS.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"Feel Your Protocol","text":"Interactive Ethereum Protocol Explorations","tagline":"Explore, visualize and understand Ethereum protocol changes — hands on.","actions":[{"theme":"brand","text":"Get Started","link":"/guide/getting-started"},{"theme":"alt","text":"How to Contribute","link":"/contributing/how-to-contribute"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/feelyourprotocol/website"}]},"features":[{"title":"Interactive Explorations","details":"Each exploration gets its own self-contained folder with an interactive component that lets you explore the protocol change hands on."},{"title":"Powered by Real Libraries","details":"Widgets run actual Ethereum library code in the browser — no mocks, no simplifications."},{"title":"Collaborative & Open Source","details":"Designed for community contributions. The modular folder-per-exploration structure makes it easy to add new explorations or improve existing ones."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),r={name:"index.md"};function i(l,t,s,c,d,u){return a(),n("div",null,[...t[0]||(t[0]=[e("div",{class:"warning custom-block"},[e("p",{class:"custom-block-title"},"Under Active Development"),e("p",null,"Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. Contributions and feedback are very welcome!")],-1)])])}const h=o(r,[["render",i]]);export{p as __pageData,h as default}; diff --git a/dist/docs/assets/index.md.BJXo33p3.lean.js b/dist/docs/assets/index.md.BJXo33p3.lean.js deleted file mode 100644 index ed1d652..0000000 --- a/dist/docs/assets/index.md.BJXo33p3.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o,c as n,o as a,j as e}from"./chunks/framework.DyoGesZS.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"layout":"home","hero":{"name":"Feel Your Protocol","text":"Interactive Ethereum Protocol Explorations","tagline":"Explore, visualize and understand Ethereum protocol changes — hands on.","actions":[{"theme":"brand","text":"Get Started","link":"/guide/getting-started"},{"theme":"alt","text":"How to Contribute","link":"/contributing/how-to-contribute"},{"theme":"alt","text":"View on GitHub","link":"https://github.com/feelyourprotocol/website"}]},"features":[{"title":"Interactive Explorations","details":"Each exploration gets its own self-contained folder with an interactive component that lets you explore the protocol change hands on."},{"title":"Powered by Real Libraries","details":"Widgets run actual Ethereum library code in the browser — no mocks, no simplifications."},{"title":"Collaborative & Open Source","details":"Designed for community contributions. The modular folder-per-exploration structure makes it easy to add new explorations or improve existing ones."}]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),r={name:"index.md"};function i(l,t,s,c,d,u){return a(),n("div",null,[...t[0]||(t[0]=[e("div",{class:"warning custom-block"},[e("p",{class:"custom-block-title"},"Under Active Development"),e("p",null,"Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. Contributions and feedback are very welcome!")],-1)])])}const h=o(r,[["render",i]]);export{p as __pageData,h as default}; diff --git a/dist/docs/contributing/adding-an-exploration.html b/dist/docs/contributing/adding-an-exploration.html index ca12b6e..d8459c0 100644 --- a/dist/docs/contributing/adding-an-exploration.html +++ b/dist/docs/contributing/adding-an-exploration.html @@ -9,24 +9,27 @@ - + - + - + -
Skip to content

Adding an Exploration

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Overview

Each exploration lives in its own folder under src/explorations/ with two files: info.ts (metadata) and MyC.vue (interactive widget). This guide walks you through adding a new one.

Step 1: Create the Exploration Folder

Create a new folder in src/explorations/ named after your exploration's ID. The ID should be lowercase and hyphen-separated (e.g. eip-XXXX, erc-XXXX, or any descriptive identifier):

bash
mkdir src/explorations/eip-XXXX

Step 2: Create info.ts

Create src/explorations/eip-XXXX/info.ts with your exploration's metadata:

typescript
import type { Exploration } from '../REGISTRY'
+    
Skip to content

Adding an Exploration

Each exploration lives in its own folder under src/explorations/ with a few files. This guide walks you through adding a new one.

Quick Overview

An exploration folder looks like this:

src/explorations/eip-XXXX/
+├── info.ts         # Metadata (required)
+├── MyC.vue         # Interactive widget (required)
+├── examples.ts     # Example presets (recommended)
+└── data/           # Optional data files

Step 1: Create the Folder

Create a new folder in src/explorations/ named after your exploration's ID. Use lowercase, hyphen-separated names:

bash
mkdir src/explorations/eip-XXXX

The ID can be eip-XXXX, erc-XXXX, or any descriptive identifier for research topics.

Step 2: Create info.ts

This file defines all the metadata for your exploration:

typescript
import type { Exploration } from '@/explorations/REGISTRY'
 
 export const INFO: Exploration = {
   id: 'eip-XXXX',
   path: '/eip-XXXX-short-description',
   title: 'Human-Readable Title',
   infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX',
-  topics: ['fusaka'],
-  image: 'fusaka.webp',
+  topic: 'fusaka',
   introText:
     '<b>What does this change?</b> ' +
     'A brief introduction to the protocol change.',
@@ -35,33 +38,97 @@
   poweredBy: [
     { name: 'EthereumJS', href: 'https://github.com/ethereumjs/ethereumjs-monorepo' },
   ],
-}

Fields:

FieldRequiredDescription
idYesUnique identifier, matches the folder name
pathYesURL path for the exploration page
titleYesDisplay title
infoURLYesLink to the specification or reference material
topicsNoArray of topic IDs this exploration belongs to
imageNoImage filename from src/assets/ (e.g. fusaka.webp)
introTextNoHTML-formatted introduction paragraph
usageTextNoHTML-formatted usage instructions
poweredByNoArray of { name, href } for library credits

Step 3: Create MyC.vue

Create src/explorations/eip-XXXX/MyC.vue with your interactive widget. Use ExplorationC as the wrapper component for consistent layout:

vue
<script setup lang="ts">
-import ExplorationC from '../ExplorationC.vue'
-import PoweredByC from '../PoweredByC.vue'
-import { INFO } from './info'
+}

Field Reference

FieldRequiredDescription
idYesUnique identifier, matches the folder name
pathYesURL path for the exploration page
titleYesDisplay title
infoURLYesLink to the specification or reference material
topicYesTopic ID this exploration belongs to (e.g. 'fusaka')
imageNoImported image for topic overview display
introTextNoHTML-formatted introduction paragraph
usageTextNoHTML-formatted usage instructions
poweredByYesArray of { name, href } for library credits

Step 3: Create examples.ts

Define example presets that users can select from a dropdown:

typescript
import type { Examples } from '@/components/lib/general'
 
-const exploration = INFO
-const poweredBy = exploration.poweredBy ?? []
+export const examples: Examples = {
+  basic: {
+    title: 'Basic Example',
+    values: ['deadbeef'],
+  },
+  advanced: {
+    title: 'Advanced Example',
+    values: ['cafebabe', '0102030405'],
+  },
+}

Each example has a title (displayed in the dropdown) and a values array (the preset input values).

Step 4: Create MyC.vue

This is your interactive widget. The approach depends on what you are building.

Option A: Custom Widget

For explorations with unique behavior, build the widget from scratch using shared UI components:

vue
<script setup lang="ts">
+import { ref } from 'vue'
+
+import { PP_BOX_LAYOUT } from '@/components/lib/layout'
+import ExamplesC from '@/components/ui/ExamplesC.vue'
+import HexDataInputC from '@/components/ui/HexDataInputC.vue'
+import PPBoxC from '@/components/ui/PPBoxC.vue'
+import ExplorationC from '@/explorations/ExplorationC.vue'
+import PoweredByC from '@/explorations/PoweredByC.vue'
+import { TOPICS } from '@/explorations/TOPICS'
+
+import { examples } from './examples'
+import { INFO as exploration } from './info'
+
+const topic = TOPICS[exploration.topic]
+const data = ref('')
+const example = ref('')
+
+async function selectExample() {
+  if (example.value === '') return
+  data.value = examples[example.value]!.values[0]
+}
+
+async function onDataInputFormChange() {
+  example.value = ''
+}
+
+async function init() {
+  example.value = 'basic'
+  await selectExample()
+}
+
+await init()
+</script>
+
+<template>
+  <ExplorationC explorationId="eip-XXXX" :exploration="exploration" :topic="topic">
+    <template #content>
+      <div>
+        <ExamplesC v-model="example" :examples="examples" :change="selectExample" />
+        <HexDataInputC v-model="data" rows="6" :formChange="onDataInputFormChange" />
+        <!-- Your result display here -->
+        <PoweredByC :poweredBy="exploration.poweredBy" :topic="topic" />
+      </div>
+    </template>
+  </ExplorationC>
+</template>

The ExplorationC wrapper renders the title, info link, intro text, and usage text from your info.ts. You provide the interactive content via the #content slot.

Option B: Precompile Interface E-Component

If your exploration is about a precompile, you can use the Precompile Interface E-Component and get a full-featured widget in ~30 lines:

vue
<script setup lang="ts">
+import { Hardfork } from '@ethereumjs/common'
+
+import PrecompileInterfaceEC from '@/eComponents/precompileInterfaceEC/PrecompileInterfaceEC.vue'
+import type { PrecompileConfig } from '@/eComponents/precompileInterfaceEC/types'
+
+import { examples } from './examples'
+import { INFO as exploration } from './info'
+
+const config: PrecompileConfig = {
+  explorationId: 'eip-XXXX',
+  precompileAddress: '0a',
+  preHardfork: Hardfork.Prague,
+  postHardfork: Hardfork.Osaka,
+  defaultExample: 'basic',
+  values: [
+    { title: 'Input A', urlParam: 'a', expectedLen: 32n },
+    { title: 'Input B', urlParam: 'b', expectedLen: 32n },
+  ],
+}
 </script>
 
 <template>
-  <div>
-    <ExplorationC :explorationId="exploration.id" :exploration="exploration">
-      <template v-slot:content>
-        <!-- Your interactive widget here -->
-        <p>Widget content goes here.</p>
-      </template>
-    </ExplorationC>
-    <PoweredByC :poweredBy="poweredBy" />
-  </div>
-</template>

The ExplorationC wrapper automatically renders the title, info link, intro text and usage text from your info.ts. You only need to provide the interactive content via the content slot.

For shared UI components (hex inputs, result displays, etc.), import from ../../components/ui/ and ../../components/precompiles/. Browse existing explorations for examples.

Step 4: Register in the Registry

Add one import line to src/explorations/REGISTRY.ts:

typescript
import { INFO as eipXXXX } from './eip-XXXX/info'
+  <PrecompileInterfaceEC :config="config" :examples="examples" :exploration="exploration" />
+</template>

See Using E-Components for the full API reference.

Step 5: Register in the Registry

Add one import line to src/explorations/REGISTRY.ts:

typescript
import { INFO as eipXXXX } from './eip-XXXX/info'
 
 export const EXPLORATIONS: Explorations = {
   // ... existing explorations
   [eipXXXX.id]: eipXXXX,
-}

That's it for registration. The router reads from EXPLORATIONS and automatically creates the route — no manual route configuration needed.

Step 5: Add Library Dependencies

If your widget needs an Ethereum library (or a custom fork), install it and import it only in your MyC.vue file:

bash
npm install some-library

If you need a library fork, see the Library Forks guide. Key rule: only import fork-specific libraries in your MyC.vue, never in shared code. This keeps each exploration's dependencies isolated via Vite's code splitting.

Step 6: Add Tests

Add a Cypress E2E test in cypress/e2e/ to verify basic widget functionality. Name it after your exploration ID (e.g. eip-XXXX.cy.ts).

Step 7: Build and Verify

bash
npm run build        # verify production build works
-npm run test:e2e     # run E2E tests

Quick Checklist

  • [ ] Created src/explorations/<id>/info.ts with metadata
  • [ ] Created src/explorations/<id>/MyC.vue with interactive widget
  • [ ] Added import and entry in src/explorations/REGISTRY.ts
  • [ ] Added library dependencies (if needed)
  • [ ] Added E2E test
  • [ ] Build passes (npm run build)

This project and its documentation are under active development.

- +}

The router reads from EXPLORATIONS and automatically creates the route — no manual route configuration needed.

Step 6: Install Dependencies

If your widget needs additional libraries, install them:

bash
npm install some-library

Import libraries only in your MyC.vue — never in shared code. This keeps each exploration's dependencies isolated via Vite's code splitting.

If you need a custom library fork (e.g. with experimental features), see Library Forks.

Step 7: Verify

bash
npm run dev          # check your exploration locally
+npm run lf           # format + lint
+npm run type-check   # TypeScript check
+npm run build        # verify production build

Checklist

  • [ ] Created src/explorations/<id>/info.ts with metadata
  • [ ] Created src/explorations/<id>/MyC.vue with interactive widget
  • [ ] Created src/explorations/<id>/examples.ts with example presets
  • [ ] Added import and entry in src/explorations/REGISTRY.ts
  • [ ] Installed library dependencies (if needed)
  • [ ] Linting and type checking pass
  • [ ] Production build succeeds

This project and its documentation are under active development.

+ \ No newline at end of file diff --git a/dist/docs/contributing/how-to-contribute.html b/dist/docs/contributing/how-to-contribute.html index 7d671b0..33e3c9c 100644 --- a/dist/docs/contributing/how-to-contribute.html +++ b/dist/docs/contributing/how-to-contribute.html @@ -9,19 +9,22 @@ - + - + - + -
Skip to content

How to Contribute

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Ways to Contribute

  • Add a new exploration — the most impactful contribution (see Adding an Exploration)
  • Improve an existing exploration — better examples, UI improvements, bug fixes
  • Improve documentation — fix typos, add guides, clarify explanations
  • Report issues — found a bug or have a suggestion? Open an issue

Development Workflow

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run linting and tests:
bash
npm run lf          # format + lint
-npm run test:unit   # unit tests
-npm run test:e2e    # E2E tests
  1. Submit a pull request

Code Style

  • Formatting: Prettier (auto-formatted with npm run format)
  • Linting: ESLint (auto-fixed with npm run lint)
  • Run both: npm run lf

Documentation

This documentation is built with VitePress. Docs live in the docs/ folder as standard markdown files.

To preview docs changes locally:

bash
npm run docs:dev

This project and its documentation are under active development.

- +
Skip to content

How to Contribute

Contributions are what make Feel Your Protocol useful. Whether you are adding a brand-new exploration, polishing an existing one, or improving the shared components — every contribution helps the Ethereum community understand protocol changes better.

Ways to Contribute

Add a New Exploration

This is the most impactful contribution. Each exploration is a self-contained folder with metadata and an interactive widget. The Adding an Exploration guide walks you through it step by step.

Improve an Existing Exploration

  • Better examples and presets
  • UI/UX improvements
  • Bug fixes
  • More informative intro and usage texts

Build or Improve E-Components

E-Components are reusable Ethereum-specific components (e.g. a precompile interface). If you spot a pattern shared across explorations, it might be a candidate for a new E-Component.

Improve Documentation

Fix typos, add guides, clarify explanations. Documentation lives in the docs/ folder as standard markdown files. Preview locally with:

bash
npm run docs:dev

Report Issues

Found a bug or have a suggestion? Open an issue on GitHub.

Development Workflow

1. Setup

bash
git clone https://github.com/feelyourprotocol/website.git
+cd website
+npm install

2. Develop

bash
npm run dev          # start dev server

3. Verify

Before submitting a PR, run all quality checks:

bash
npm run lf           # format + lint (auto-fix)
+npm run type-check   # TypeScript type checking
+npx vitest run       # unit tests (single run)
+npm run test:e2e     # E2E tests

4. Submit

  • Fork the repository and create a feature branch
  • Make your changes
  • Ensure all checks pass
  • Submit a pull request with a clear description of what you changed and why

What Goes Where

What you are working onWhere it lives
A new explorationsrc/explorations/<id>/
Exploration metadatasrc/explorations/<id>/info.ts
Interactive widgetsrc/explorations/<id>/MyC.vue
Example presetssrc/explorations/<id>/examples.ts
Exploration registrysrc/explorations/REGISTRY.ts
E-Componentssrc/eComponents/<name>EC/
Shared UI componentssrc/components/ui/
Shared utilitiessrc/components/lib/
Unit testssrc/views/__tests__/ (or co-located __tests__/)
E2E testscypress/e2e/
Documentationdocs/

Further Reading

This project and its documentation are under active development.

+ \ No newline at end of file diff --git a/dist/docs/guide/architecture.html b/dist/docs/guide/architecture.html index 6d34a92..5fb4013 100644 --- a/dist/docs/guide/architecture.html +++ b/dist/docs/guide/architecture.html @@ -9,47 +9,29 @@ - + - + - + -
Skip to content

Architecture

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Overview

Feel Your Protocol is a Vue 3 application built with Vite. The core idea is simple: each Ethereum protocol change gets its own interactive widget that runs real library code in the browser.

Tech Stack

Content Structure

Content is organized around two concepts:

  • Explorations — the core unit. Each exploration represents an interactive widget for a protocol change (EIP, ERC, or other research topic). Explorations live in src/explorations/.
  • Topics — group explorations by theme (e.g. "Fusaka" for an upcoming hardfork). Each exploration can belong to zero or more topics via the topics array. Topics are defined in src/explorations/TOPICS.ts.

The src/explorations/ Folder

This is the heart of the project. Each exploration is a self-contained folder:

src/explorations/
-├── REGISTRY.ts            # Assembles all explorations, exports EXPLORATIONS dict
-├── TOPICS.ts              # Topic definitions
-├── ExplorationC.vue       # Shared wrapper component (title, description, buttons)
-├── PoweredByC.vue         # Shared "powered by" footer
-├── eip-7594/
-│   ├── info.ts            # Metadata: id, title, path, infoURL, introText, usageText, …
-│   └── MyC.vue            # The interactive widget
-├── eip-7883/
-│   ├── info.ts
-│   └── MyC.vue
-└── eip-7951/
-    ├── info.ts
-    └── MyC.vue

Each info.ts exports a const INFO object typed as Exploration. The REGISTRY.ts imports all INFO constants and assembles them into the EXPLORATIONS dictionary. The router reads from this to automatically create routes — no manual route registration needed.

Exploration Metadata (info.ts)

Each exploration's info.ts contains all its metadata as a flat object:

typescript
import type { Exploration } from '../REGISTRY'
-
-export const INFO: Exploration = {
-  id: 'eip-XXXX',
-  path: '/eip-XXXX-short-description',
-  title: 'Human-Readable Title',
-  infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX',
-  topics: ['fusaka'],
-  image: 'fusaka.webp',
-  introText: 'HTML-formatted introduction.',
-  usageText: 'HTML-formatted usage instructions.',
-  poweredBy: [
-    { name: 'Library Name', href: 'https://github.com/...' },
-  ],
-}

Key Design Decisions

Folder-per-Exploration

Each exploration is fully self-contained in its own folder with info.ts (metadata) and MyC.vue (widget). This means:

  • Contributors can focus on a single folder
  • Adding a new exploration is a matter of creating a folder and adding one import to REGISTRY.ts
  • Each exploration's dependencies are isolated

Dynamic Views

There are no static per-exploration or per-topic view files. Instead:

  • ExplorationView.vue dynamically loads the correct MyC.vue using import.meta.glob() and defineAsyncComponent() based on the route name
  • TopicView.vue dynamically lists all explorations belonging to a topic

This keeps the views lean and avoids boilerplate when adding new content.

Route-Level Code Splitting

The ExplorationView.vue uses import.meta.glob() for lazy loading:

typescript
const componentModules = import.meta.glob('../explorations/*/MyC.vue')
+    
Skip to content

Architecture

Overview

Feel Your Protocol is a Vue 3 application built with Vite. The core idea is simple: each Ethereum protocol change gets its own interactive widget that runs real library code in the browser.

Tech Stack

Content Model

Content is organized around two concepts:

Explorations

The core content unit. Each exploration represents an interactive widget for a protocol change — EIPs, ERCs, or research topics. Explorations live in src/explorations/, one folder per exploration:

src/explorations/eip-7883/
+├── info.ts         # Metadata: id, title, path, topic, introText, poweredBy, …
+├── MyC.vue         # The interactive widget
+└── examples.ts     # Example presets for the widget

Each info.ts exports a const INFO object typed as Exploration. The REGISTRY.ts imports all INFO constants and assembles them into the EXPLORATIONS dictionary. The router reads from this dictionary to automatically create routes — no manual route registration needed.

Topics

Topics group explorations by theme (e.g. "Fusaka" for an upcoming hardfork). Each exploration belongs to exactly one topic via the topic field in its info.ts. Topics are defined in src/explorations/TOPICS.ts.

E-Components

E-Components are reusable Ethereum-specific components that encapsulate common patterns across explorations. They live in src/eComponents/ and follow a naming convention: folder and component names are post-fixed with EC.

The first E-Component is precompileInterfaceEC, which provides a complete precompile exploration interface — input parsing, hardfork comparison, result display — as a single component backed by a composable:

src/eComponents/precompileInterfaceEC/
+├── PrecompileInterfaceEC.vue      # Full-featured precompile exploration template
+├── PrecompileInterfaceResultEC.vue # Result display (pre/post hardfork comparison)
+├── PrecompileValueInputEC.vue     # Value input with byte length validation
+├── usePrecompileState.ts          # Composable: all state and logic
+├── types.ts                       # PrecompileConfig and PrecompileValueDef interfaces
+└── run.ts                         # EVM precompile execution utility

Using the Precompile Interface E-Component, a precompile exploration widget can be as short as 30 lines — just a config object and a single component tag. See Using E-Components for details.

UI Components

Generic UI components live in src/components/ui/ and are used across explorations:

ComponentPurpose
ExamplesCExample selector dropdown
HexDataInputCHex data input textarea with validation
PPBoxCResult display box with title
PPBoxErrorTextError message display
PPBoxInfoTextInformational message display
ActionButtonCAction button with tooltip
ButtonCIcon button with tooltip
HeadlineButtonCHeadline with action button
TooltipCGeneric tooltip wrapper

Shared utilities in src/components/lib/:

ModulePurpose
byteFormUtils.tsHex/byte conversion and validation
general.tsShared types (e.g. Examples)
layout.tsTailwind CSS class constants for consistent layouts

Key Design Decisions

Folder-per-Exploration

Each exploration is fully self-contained in its own folder. This means:

  • Contributors can focus on a single folder
  • Adding a new exploration requires creating a folder and adding one import to REGISTRY.ts
  • Each exploration's dependencies are isolated

Dynamic Views

There are no static per-exploration or per-topic view files. Instead:

  • ExplorationView.vue dynamically loads the correct MyC.vue using import.meta.glob() and defineAsyncComponent() based on the route name
  • TopicView.vue dynamically lists all explorations belonging to a topic
  • HomeView.vue dynamically renders all topics defined in TOPICS.ts

Route-Level Code Splitting

Each exploration is a separate chunk that is loaded on demand. Users only download the libraries needed for the page they visit. This is achieved via import.meta.glob() for lazy loading:

typescript
const componentModules = import.meta.glob('../explorations/*/MyC.vue')
 const ExplorationComponent = defineAsyncComponent(
   componentModules[`../explorations/${explorationId}/MyC.vue`]
-)

Each exploration is a separate chunk that's loaded on demand. Users only download the libraries needed for the page they visit.

Library Forks

Some exploration widgets require custom forks of Ethereum libraries (see Library Forks). The architecture ensures that different forks of the same library can coexist without conflicts, each isolated to its specific exploration route.

This project and its documentation are under active development.

- +)

Testing Strategy

The project uses a hybrid testing approach:

  • Unit tests (Vitest + Vue Test Utils) for component rendering, content verification, and UI logic — fast and focused
  • E2E tests (Cypress) as lean smoke tests for critical navigation flows and page-level integration

Unit tests live alongside their components in __tests__/ folders. E2E tests are consolidated in cypress/e2e/.

This project and its documentation are under active development.

+ \ No newline at end of file diff --git a/dist/docs/guide/getting-started.html b/dist/docs/guide/getting-started.html index 05047c0..40a88f8 100644 --- a/dist/docs/guide/getting-started.html +++ b/dist/docs/guide/getting-started.html @@ -9,50 +9,57 @@ - + - + - + -
Skip to content

Getting Started

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

What is Feel Your Protocol?

Feel Your Protocol is an interactive website that lets you explore Ethereum protocol changes hands on. Instead of just reading specifications, you can interact with real Ethereum library code running directly in the browser.

Each protocol change — called an Exploration — gets its own page with a dedicated interactive widget. For example, the EIP-7883 page lets you experiment with ModExp gas cost changes interactively.

Explorations are grouped into Topics (e.g. "Fusaka" for the upcoming hardfork), making it easy to discover related protocol changes.

Prerequisites

  • Node.js v20.19+ or v22.12+
  • npm (comes with Node.js)

Setup

Clone the repository and install dependencies:

bash
git clone https://github.com/feelyourprotocol/website.git
+    
Skip to content

Getting Started

What is Feel Your Protocol?

Feel Your Protocol is an interactive website that lets you explore Ethereum protocol changes hands on. Instead of just reading specifications, you can interact with real Ethereum library code running directly in the browser.

Each protocol change — called an Exploration — gets its own page with a dedicated interactive widget. Explorations cover EIPs, ERCs, and other research topics. They are grouped into Topics (e.g. "Fusaka" for the upcoming hardfork), making it easy to discover related protocol changes.

Prerequisites

  • Node.js v20.19+ or v22.12+
  • npm (comes with Node.js)

Setup

Clone the repository and install dependencies:

bash
git clone https://github.com/feelyourprotocol/website.git
 cd website
-npm install

Development

Start the website dev server:

bash
npm run dev

Start the docs dev server:

bash
npm run docs:dev

Building

Build both the website and documentation:

bash
npm run build          # website → dist/website
+npm install

Development

Start the website dev server:

bash
npm run dev

Start the docs dev server:

bash
npm run docs:dev

Quality Checks

bash
npm run lf           # format + lint (auto-fix)
+npm run lf:ci        # lint + format check (CI mode, no auto-fix)
+npm run type-check   # TypeScript type checking (vue-tsc)

Testing

bash
npx vitest run       # unit tests (single run)
+npm run test:unit    # unit tests (watch mode)
+npm run test:e2e     # E2E tests (Cypress, requires build first)

Building

bash
npm run build          # website → dist/website
 npm run docs:build     # docs → dist/docs

Project Structure

website/
 ├── src/
-│   ├── explorations/          # Explorations (the core content)
-│   │   ├── REGISTRY.ts        # Exploration registry, types, helper functions
-│   │   ├── TOPICS.ts          # Topic definitions and types
-│   │   ├── ExplorationC.vue   # Shared exploration wrapper component
-│   │   ├── PoweredByC.vue     # Shared "powered by" component
-│   │   ├── eip-7594/          # One folder per exploration
-│   │   │   ├── info.ts        #   Metadata (title, description, links, …)
-│   │   │   └── MyC.vue        #   Interactive widget component
+│   ├── explorations/              # Explorations (the core content)
+│   │   ├── REGISTRY.ts            # Exploration registry and types
+│   │   ├── TOPICS.ts              # Topic definitions and types
+│   │   ├── ExplorationC.vue       # Shared exploration wrapper component
+│   │   ├── PoweredByC.vue         # Shared "powered by" component
+│   │   ├── eip-7594/              # One folder per exploration
+│   │   │   ├── info.ts            #   Metadata (title, description, links, …)
+│   │   │   ├── MyC.vue            #   Interactive widget component
+│   │   │   ├── examples.ts        #   Example presets
+│   │   │   └── data/              #   Optional data files
 │   │   ├── eip-7883/
-│   │   │   ├── info.ts
-│   │   │   └── MyC.vue
 │   │   └── eip-7951/
-│   │       ├── info.ts
-│   │       └── MyC.vue
-│   ├── components/            # Shared UI and utility components
-│   │   ├── ui/                # Generic UI components
-│   │   ├── precompiles/       # Precompile-related shared components
-│   │   └── lib/               # Shared logic and utilities
-│   ├── views/                 # Route views
+│   ├── eComponents/               # Reusable Ethereum-specific components (E-Components)
+│   │   └── precompileInterfaceEC/ # Precompile interface E-Component
+│   │       ├── PrecompileInterfaceEC.vue
+│   │       ├── PrecompileInterfaceResultEC.vue
+│   │       ├── PrecompileValueInputEC.vue
+│   │       ├── usePrecompileState.ts
+│   │       ├── types.ts
+│   │       └── run.ts
+│   ├── components/                # Shared UI components and utilities
+│   │   ├── ui/                    # Generic UI components
+│   │   └── lib/                   # Shared logic and utilities
+│   ├── views/                     # Route views
 │   │   ├── HomeView.vue
 │   │   ├── TopicView.vue
 │   │   ├── ExplorationView.vue
-│   │   └── TopicIntroView.vue
-│   └── router/                # Vue Router config
-├── docs/                      # Documentation (VitePress)
-├── cypress/                   # E2E tests
-└── dist/                      # Build output
-    ├── website/
-    └── docs/

This project and its documentation are under active development.

- +│ │ └── __tests__/ # Unit tests +│ └── router/ # Vue Router config +├── docs/ # Documentation (VitePress) +├── cypress/ # E2E tests +└── .github/workflows/ # CI workflows

This project and its documentation are under active development.

+ \ No newline at end of file diff --git a/dist/docs/guide/library-forks.html b/dist/docs/guide/library-forks.html deleted file mode 100644 index 56f4968..0000000 --- a/dist/docs/guide/library-forks.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Library Forks | Feel Your Protocol - - - - - - - - - - - - - - -
Skip to content

Library Forks

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently.

Why Forks?

Feel Your Protocol runs real Ethereum library code in the browser. Some exploration widgets need a modified version of an existing library — for example, a version with a new precompile implementation, or experimental gas calculation changes that haven't been released yet.

Rather than waiting for upstream releases, we maintain targeted forks that are used by specific exploration pages.

How Forks Work

npm Package Aliases

Multiple versions of the same library coexist in package.json using npm aliases:

json
{
-  "dependencies": {
-    "@ethereumjs/evm": "^10.1.1-nightly.1",
-    "@ethereumjs/evm-experimental": "npm:@ethereumjs/evm@^11.0.0-fork.1"
-  }
-}

Each alias is a fully independent install. In code, you import the specific version you need:

typescript
import { EVM } from '@ethereumjs/evm'                    // standard
-import { EVM as EVMExp } from '@ethereumjs/evm-experimental'  // fork

Monorepo Libraries

For libraries from monorepos (like EthereumJS), where the target package has several intra-monorepo dependencies, we use pre-bundled ESM builds. The fork is bundled on the monorepo side with all internal dependencies resolved, producing a single ESM file with no internal wiring issues.

Per-Route Isolation

Each fork is only imported in its specific exploration's MyC.vue. Thanks to Vite's code splitting, the fork's code is only loaded when the user visits that exploration's page. Other pages are unaffected.

Adding a New Fork

  1. Fork the upstream library (or create a branch if you have access)
  2. Make your changes
  3. For single-package repos: add an npm alias pointing to your fork's git URL
  4. For monorepo packages: create a bundled build, then reference it
  5. Import the fork only in your exploration's MyC.vue file — never in shared code
  6. Document the fork in this page

This project and its documentation are under active development.

- - - - \ No newline at end of file diff --git a/dist/docs/hashmap.json b/dist/docs/hashmap.json index ac3712c..3a30be5 100644 --- a/dist/docs/hashmap.json +++ b/dist/docs/hashmap.json @@ -1 +1 @@ -{"contributing_adding-an-exploration.md":"Bx_AiHel","contributing_how-to-contribute.md":"C-N0YY81","guide_architecture.md":"qxw0U9Or","guide_getting-started.md":"BHVwHBbn","guide_library-forks.md":"C-_4bMc7","index.md":"BJXo33p3"} +{"contributing_adding-an-exploration.md":"Ahsvo_Ol","contributing_available-e-components.md":"CJ3WaJq0","contributing_code-conventions.md":"q8SsjSd8","contributing_e-components.md":"EgChjAzs","contributing_how-to-contribute.md":"XKvLYLkk","contributing_library-forks.md":"DYk1JQhp","contributing_styling.md":"BxYm3jg7","guide_architecture.md":"BSBE4DKA","guide_getting-started.md":"B-wiDt-S","index.md":"DpQvSWpN"} diff --git a/dist/docs/index.html b/dist/docs/index.html index 20494e3..4ac9ff6 100644 --- a/dist/docs/index.html +++ b/dist/docs/index.html @@ -9,17 +9,17 @@ - + - + - + -
Skip to content

Feel Your ProtocolInteractive Ethereum Protocol Explorations

Explore, visualize and understand Ethereum protocol changes — hands on.

Under Active Development

Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. Contributions and feedback are very welcome!

This project and its documentation are under active development.

- +
Skip to content

Feel Your ProtocolInteractive Ethereum Protocol Explorations

Explore, visualize and understand Ethereum protocol changes — hands on.

Want to contribute?

The fastest way to get started is to add a new exploration. Each one lives in a single folder with just two files — metadata and your interactive widget.

This project and its documentation are under active development.

+ \ No newline at end of file diff --git a/dist/website/index.html b/dist/website/index.html index 653d525..1ba5325 100644 --- a/dist/website/index.html +++ b/dist/website/index.html @@ -5,8 +5,8 @@ Feel Your Protocol - - + +
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 4a07619..d77dd90 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -16,7 +16,6 @@ export default defineConfig({ items: [ { text: 'Getting Started', link: '/guide/getting-started' }, { text: 'Architecture', link: '/guide/architecture' }, - { text: 'Library Forks', link: '/guide/library-forks' }, ], }, { @@ -24,6 +23,12 @@ export default defineConfig({ items: [ { text: 'How to Contribute', link: '/contributing/how-to-contribute' }, { text: 'Adding an Exploration', link: '/contributing/adding-an-exploration' }, + { text: 'UI Components', link: '/contributing/ui-components' }, + { text: 'E-Components', link: '/contributing/e-components' }, + { text: 'Available E-Components', link: '/contributing/available-e-components' }, + { text: 'Styling & Design', link: '/contributing/styling' }, + { text: 'Code Conventions', link: '/contributing/code-conventions' }, + { text: 'Library Forks', link: '/contributing/library-forks' }, ], }, ], diff --git a/docs/contributing/adding-an-exploration.md b/docs/contributing/adding-an-exploration.md index 285957f..7177b4f 100644 --- a/docs/contributing/adding-an-exploration.md +++ b/docs/contributing/adding-an-exploration.md @@ -1,35 +1,42 @@ # Adding an Exploration -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. -::: +Each exploration lives in its own folder under `src/explorations/` with a few files. This guide walks you through adding a new one. -## Overview +## Quick Overview -Each exploration lives in its own folder under `src/explorations/` with two files: `info.ts` (metadata) and `MyC.vue` (interactive widget). This guide walks you through adding a new one. +An exploration folder looks like this: -## Step 1: Create the Exploration Folder +``` +src/explorations/eip-XXXX/ +├── info.ts # Metadata (required) +├── MyC.vue # Interactive widget (required) +├── examples.ts # Example presets (recommended) +└── data/ # Optional data files +``` -Create a new folder in `src/explorations/` named after your exploration's ID. The ID should be lowercase and hyphen-separated (e.g. `eip-XXXX`, `erc-XXXX`, or any descriptive identifier): +## Step 1: Create the Folder + +Create a new folder in `src/explorations/` named after your exploration's ID. Use lowercase, hyphen-separated names: ```bash mkdir src/explorations/eip-XXXX ``` +The ID can be `eip-XXXX`, `erc-XXXX`, or any descriptive identifier for research topics. + ## Step 2: Create `info.ts` -Create `src/explorations/eip-XXXX/info.ts` with your exploration's metadata: +This file defines all the metadata for your exploration: ```typescript -import type { Exploration } from '../REGISTRY' +import type { Exploration } from '@/explorations/REGISTRY' export const INFO: Exploration = { id: 'eip-XXXX', path: '/eip-XXXX-short-description', title: 'Human-Readable Title', infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX', - topics: ['fusaka'], - image: 'fusaka.webp', + topic: 'fusaka', introText: 'What does this change? ' + 'A brief introduction to the protocol change.', @@ -41,7 +48,7 @@ export const INFO: Exploration = { } ``` -**Fields:** +### Field Reference | Field | Required | Description | |-------|----------|-------------| @@ -49,44 +56,128 @@ export const INFO: Exploration = { | `path` | Yes | URL path for the exploration page | | `title` | Yes | Display title | | `infoURL` | Yes | Link to the specification or reference material | -| `topics` | No | Array of topic IDs this exploration belongs to | -| `image` | No | Image filename from `src/assets/` (e.g. `fusaka.webp`) | +| `topic` | Yes | Topic ID this exploration belongs to (e.g. `'fusaka'`) | +| `image` | No | Imported image for topic overview display | | `introText` | No | HTML-formatted introduction paragraph | | `usageText` | No | HTML-formatted usage instructions | -| `poweredBy` | No | Array of `{ name, href }` for library credits | +| `poweredBy` | Yes | Array of `{ name, href }` for library credits | + +## Step 3: Create `examples.ts` + +Define example presets that users can select from a dropdown: + +```typescript +import type { Examples } from '@/components/lib/general' + +export const examples: Examples = { + basic: { + title: 'Basic Example', + values: ['deadbeef'], + }, + advanced: { + title: 'Advanced Example', + values: ['cafebabe', '0102030405'], + }, +} +``` + +Each example has a `title` (displayed in the dropdown) and a `values` array (the preset input values). + +## Step 4: Create `MyC.vue` -## Step 3: Create `MyC.vue` +This is your interactive widget. The approach depends on what you are building. -Create `src/explorations/eip-XXXX/MyC.vue` with your interactive widget. Use `ExplorationC` as the wrapper component for consistent layout: +### Option A: Custom Widget + +For explorations with unique behavior, build the widget from scratch using shared UI components: ```vue ``` -The `ExplorationC` wrapper automatically renders the title, info link, intro text and usage text from your `info.ts`. You only need to provide the interactive content via the `content` slot. +The `ExplorationC` wrapper renders the title, info link, intro text, and usage text from your `info.ts`. You provide the interactive content via the `#content` slot. -For shared UI components (hex inputs, result displays, etc.), import from `../../components/ui/` and `../../components/precompiles/`. Browse existing explorations for examples. +### Option B: Precompile Interface E-Component -## Step 4: Register in the Registry +If your exploration is about a precompile, you can use the Precompile Interface E-Component and get a full-featured widget in ~30 lines: + +```vue + + + +``` + +See [Using E-Components](/contributing/e-components) for the full API reference. + +## Step 5: Register in the Registry Add one import line to `src/explorations/REGISTRY.ts`: @@ -99,34 +190,35 @@ export const EXPLORATIONS: Explorations = { } ``` -That's it for registration. The router reads from `EXPLORATIONS` and automatically creates the route — no manual route configuration needed. +The router reads from `EXPLORATIONS` and automatically creates the route — no manual route configuration needed. -## Step 5: Add Library Dependencies +## Step 6: Install Dependencies -If your widget needs an Ethereum library (or a custom fork), install it and import it only in your `MyC.vue` file: +If your widget needs additional libraries, install them: ```bash npm install some-library ``` -If you need a library fork, see the [Library Forks](../guide/library-forks.md) guide. Key rule: **only import fork-specific libraries in your `MyC.vue`**, never in shared code. This keeps each exploration's dependencies isolated via Vite's code splitting. - -## Step 6: Add Tests +Import libraries only in your `MyC.vue` — never in shared code. This keeps each exploration's dependencies isolated via Vite's code splitting. -Add a Cypress E2E test in `cypress/e2e/` to verify basic widget functionality. Name it after your exploration ID (e.g. `eip-XXXX.cy.ts`). +If you need a custom library fork (e.g. with experimental features), see [Library Forks](/contributing/library-forks). -## Step 7: Build and Verify +## Step 7: Verify ```bash -npm run build # verify production build works -npm run test:e2e # run E2E tests +npm run dev # check your exploration locally +npm run lf # format + lint +npm run type-check # TypeScript check +npm run build # verify production build ``` -## Quick Checklist +## Checklist - [ ] Created `src/explorations//info.ts` with metadata - [ ] Created `src/explorations//MyC.vue` with interactive widget +- [ ] Created `src/explorations//examples.ts` with example presets - [ ] Added import and entry in `src/explorations/REGISTRY.ts` -- [ ] Added library dependencies (if needed) -- [ ] Added E2E test -- [ ] Build passes (`npm run build`) +- [ ] Installed library dependencies (if needed) +- [ ] Linting and type checking pass +- [ ] Production build succeeds diff --git a/docs/contributing/available-e-components.md b/docs/contributing/available-e-components.md new file mode 100644 index 0000000..fe57124 --- /dev/null +++ b/docs/contributing/available-e-components.md @@ -0,0 +1,139 @@ +# Available E-Components + +This page lists all E-Components that are ready to use in your explorations. For background on how E-Components work and how to create new ones, see [E-Components](/contributing/e-components). + +## Precompile Interface (`precompileInterfaceEC`) + +A complete interface for exploring EVM precompiles. It handles: + +- Example selection and URL sharing +- Hex data input with parsing and validation +- Individual value inputs with byte length tracking +- Side-by-side pre/post hardfork comparison (running the precompile on two different EVM versions) +- Result display with gas cost and output data + +**Files:** + +``` +src/eComponents/precompileInterfaceEC/ +├── PrecompileInterfaceEC.vue # Main component +├── PrecompileInterfaceResultEC.vue # Result display (pre/post comparison) +├── PrecompileValueInputEC.vue # Value input with byte length validation +├── usePrecompileState.ts # Composable: all state and logic +├── types.ts # PrecompileConfig and PrecompileValueDef +└── run.ts # EVM precompile execution utility +``` + +**Used by:** [EIP-7951](https://github.com/feelyourprotocol/website/blob/main/src/explorations/eip-7951/MyC.vue) (secp256r1), [EIP-7883](https://github.com/feelyourprotocol/website/blob/main/src/explorations/eip-7883/MyC.vue) (ModExp gas cost) + +### Basic Usage + +A precompile exploration needs just a config object and a single component tag: + +```vue + + + +``` + +### `PrecompileConfig` Reference + +```typescript +interface PrecompileConfig { + explorationId: string + precompileAddress: string + preHardfork: Hardfork + postHardfork: Hardfork + defaultExample: string + showBigInt?: boolean + values: PrecompileValueDef[] + assembleData?: (hexVals: string[], byteLengths: bigint[]) => string + parseData?: (data: string, byteLengths: bigint[]) => void +} +``` + +| Field | Required | Description | +|-------|----------|-------------| +| `explorationId` | Yes | Matches the exploration's `id` in `info.ts` | +| `precompileAddress` | Yes | Hex address of the precompile (e.g. `'05'` for ModExp) | +| `preHardfork` | Yes | Hardfork for the "before" comparison | +| `postHardfork` | Yes | Hardfork for the "after" comparison | +| `defaultExample` | Yes | Key from `examples.ts` to load on initialization | +| `showBigInt` | No | Show BigInt representations for values (default: per-value setting) | +| `values` | Yes | Array of value definitions (see below) | +| `assembleData` | No | Custom function to assemble raw hex data from individual values | +| `parseData` | No | Custom function to parse raw hex data into individual values | + +### `PrecompileValueDef` Reference + +```typescript +interface PrecompileValueDef { + title: string + urlParam?: string + expectedLen?: bigint + initialHex?: string + showBigInt?: boolean + showInput?: boolean +} +``` + +| Field | Required | Description | +|-------|----------|-------------| +| `title` | Yes | Display label for this value | +| `urlParam` | No | URL query parameter name for sharing (omit for computed values) | +| `expectedLen` | No | Expected byte length for validation | +| `initialHex` | No | Initial hex value (used for length-prefix fields) | +| `showBigInt` | No | Show BigInt representation for this value | +| `showInput` | No | Whether to show the input field (default: `true`, set `false` for computed fields) | + +### Custom Data Assembly + +By default, individual values are simply concatenated to form the raw hex data. For precompiles with non-trivial data formats (like ModExp, which has length prefixes), provide custom `assembleData` and `parseData` functions: + +```typescript +const config: PrecompileConfig = { + // ... + values: [ + { title: 'Blen', expectedLen: 32n, initialHex: '00'.repeat(32), showInput: false }, + { title: 'Elen', expectedLen: 32n, initialHex: '00'.repeat(32), showInput: false }, + { title: 'Mlen', expectedLen: 32n, initialHex: '00'.repeat(32), showInput: false }, + { title: 'B', urlParam: 'b' }, + { title: 'E', urlParam: 'e' }, + { title: 'M', urlParam: 'm' }, + ], + assembleData: (hexVals, byteLengths) => + toHex(byteLengths[3], 32 * 2) + + toHex(byteLengths[4], 32 * 2) + + toHex(byteLengths[5], 32 * 2) + + padHex(hexVals[3]) + + padHex(hexVals[4]) + + padHex(hexVals[5]), + parseData: (data, byteLengths) => { + byteLengths[3] = hexToBigInt(`0x${data.substring(0, 64)}`) + byteLengths[4] = hexToBigInt(`0x${data.substring(64, 128)}`) + byteLengths[5] = hexToBigInt(`0x${data.substring(128, 192)}`) + }, +} +``` diff --git a/docs/contributing/code-conventions.md b/docs/contributing/code-conventions.md new file mode 100644 index 0000000..4b168f7 --- /dev/null +++ b/docs/contributing/code-conventions.md @@ -0,0 +1,151 @@ +# Code Conventions + +This page documents the coding conventions used throughout the project. Following these ensures consistency and makes PRs easier to review. + +## Imports + +### Path Style + +Use `@/` path aliases for cross-module imports and relative paths (`./`, `../`) for local/sibling imports within the same module: + +```typescript +// External packages first +import { Hardfork } from '@ethereumjs/common' + +// Internal @/ alias imports +import ResultBoxUIC from '@/eComponents/ui/resultBox/ResultBoxUIC.vue' +import ExplorationC from '@/explorations/ExplorationC.vue' + +// Local relative imports last +import { examples } from './examples' +import { INFO as exploration } from './info' +``` + +The `@/` alias maps to `src/`. It is configured in both `vite.config.ts` and `tsconfig.app.json`. + +### Import Sorting + +Imports are automatically sorted and grouped by ESLint using `eslint-plugin-simple-import-sort`. The enforced order is: + +1. **External packages** — anything not starting with `.` or `@/` (e.g. `vue`, `@ethereumjs/common`) +2. **Internal `@/` alias imports** — cross-module imports via the `@/` alias +3. **Local relative imports** — `./` and `../` paths + +Run `npm run lint` to auto-fix import order. This is enforced in CI. + +## Naming + +### E-Components + +E-Component folders and files use the `EC` postfix: + +- Folder: `src/eComponents/precompileInterfaceEC/` +- Component: `PrecompileInterfaceEC.vue` +- Composable: `usePrecompileState.ts` + +### Explorations + +- Folder: lowercase, hyphen-separated ID (`eip-7883`, `erc-XXXX`) +- Widget: always `MyC.vue` +- Metadata: always `info.ts` +- Examples: always `examples.ts` + +### UI Components + +Standard Vue component naming in `src/eComponents/ui/`: +- UI components end with `UIC` (e.g. `ResultBoxUIC.vue`, `ExamplesUIC.vue`), E-Components with `EC`, other structure components with `C` + +## Vue Patterns + +### Composition API + +All components use ` + + +``` + +### Strict Equality + +Always use `===` and `!==`, never `==` or `!=`. + +### No Console Logging + +Remove all `console.log` statements before submitting. Use error handling with `errorMsg` refs for user-facing messages. + +## Linting & Formatting + +The project uses ESLint 9 (flat config) and Prettier: + +```bash +npm run lf # format with Prettier, then lint with ESLint (auto-fix) +npm run lf:ci # check only, no auto-fix (used in CI) +npm run lint # ESLint auto-fix only +npm run format # Prettier only +npm run type-check # vue-tsc type checking +``` + +The ESLint config (`eslint.config.ts`) includes: +- Vue essential rules +- TypeScript recommended rules +- Import sorting (`eslint-plugin-simple-import-sort`) +- Vitest rules for test files +- Cypress rules for E2E files + +## Testing + +### Unit Tests + +Unit tests use [Vitest](https://vitest.dev/) with [Vue Test Utils](https://test-utils.vuejs.org/) and a JSDOM environment. They live in `__tests__/` folders alongside their source: + +``` +src/views/__tests__/ +├── HomeView.spec.ts +├── TopicView.spec.ts +├── ImprintView.spec.ts +└── AppLayout.spec.ts +``` + +Run unit tests: + +```bash +npx vitest run # single run +npm run test:unit # watch mode +``` + +### E2E Tests + +E2E tests use [Cypress](https://www.cypress.io/) and are kept as lean smoke tests for critical navigation and integration flows. They live in `cypress/e2e/`: + +```bash +npm run test:e2e # headless run (requires build) +npm run test:e2e:dev # interactive mode with dev server +``` + +### What to Test Where + +| Test type | Tool | What to test | +|-----------|------|-------------| +| Unit | Vitest | Component rendering, content, props, computed values | +| E2E | Cypress | Page loading, navigation flows, critical user journeys | diff --git a/docs/contributing/e-components.md b/docs/contributing/e-components.md new file mode 100644 index 0000000..be4bf75 --- /dev/null +++ b/docs/contributing/e-components.md @@ -0,0 +1,73 @@ +# E-Components + +**E-Components** are reusable, Ethereum-specific components that encapsulate common patterns across explorations. They live in `src/eComponents/` and are designed to let you build new explorations quickly without duplicating logic. + +While the shared [UI components](/contributing/ui-components) (`src/eComponents/ui/`) provide generic building blocks like buttons and input fields, E-Components go a level higher: they package complete Ethereum-domain patterns — like a full precompile testing interface — into a single component with a clean configuration API. + +For the list of ready-to-use E-Components, see [Available E-Components](/contributing/available-e-components). + +## How E-Components Work + +Each E-Component is a self-contained folder in `src/eComponents/` with a consistent internal structure: + +``` +src/eComponents/EC/ +├── EC.vue # Main component (the primary entry point) +├── types.ts # Configuration interfaces +├── use.ts # Composable: shared state and logic +├── EC.vue # Optional sub-components +└── .ts # Optional utility modules +``` + +### Naming Convention + +E-Component folders and files are post-fixed with **`EC`** (for E-Component): + +- Folder: `src/eComponents/precompileInterfaceEC/` +- Main component: `PrecompileInterfaceEC.vue` +- Sub-components: `PrecompileInterfaceResultEC.vue`, `PrecompileValueInputEC.vue` +- Composable: `usePrecompileState.ts` +- Types: `types.ts` + +### Using an E-Component in Your Exploration + +The typical pattern is: + +1. Import the E-Component and its config type +2. Define a config object describing your specific use case +3. Render the component in your template with the config, examples, and exploration metadata + +This keeps your `MyC.vue` short and declarative — often under 30 lines. + +## Creating a New E-Component + +If you spot a pattern shared across two or more explorations, it is a strong candidate for a new E-Component. Contributions of new E-Components are very welcome — they directly help other contributors build explorations faster. + +### Steps + +1. Create a folder in `src/eComponents/` following the naming convention: `EC/` +2. Define a `types.ts` with a configuration interface that captures the variation between use cases +3. Extract shared logic into a composable (`use.ts`) +4. Build the component(s) with a clean, props-based API +5. Document usage on the [Available E-Components](/contributing/available-e-components) page + +### Design Principles + +A good E-Component should: + +- **Accept a config object** rather than many individual props — this keeps the consumer API minimal and easy to extend +- **Provide sensible defaults** where possible, so simple use cases stay simple +- **Allow customization hooks** (callback functions in the config) for cases that need non-standard behavior +- **Keep the consumer's `MyC.vue` short and declarative** — ideally just a config + a single component tag +- **Encapsulate all state and logic** in a composable, keeping the Vue component focused on rendering + +### Ideas for Future E-Components + +Some patterns that might become E-Components as the project grows: + +- **Transaction builder** — interactive transaction construction and signing +- **Gas calculator** — compare gas costs across hardforks for different operations +- **Storage layout** — visualize contract storage slot changes +- **Opcode explorer** — step through EVM bytecode execution + +If you are interested in building any of these (or have other ideas), feel free to [open an issue](https://github.com/feelyourprotocol/website/issues) to discuss the design. diff --git a/docs/contributing/how-to-contribute.md b/docs/contributing/how-to-contribute.md index 1770e16..c60e5ff 100644 --- a/docs/contributing/how-to-contribute.md +++ b/docs/contributing/how-to-contribute.md @@ -1,43 +1,88 @@ # How to Contribute -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. -::: +Contributions are what make Feel Your Protocol useful. Whether you are adding a brand-new exploration, polishing an existing one, or improving the shared components — every contribution helps the Ethereum community understand protocol changes better. ## Ways to Contribute -- **Add a new exploration** — the most impactful contribution (see [Adding an Exploration](./adding-an-exploration.md)) -- **Improve an existing exploration** — better examples, UI improvements, bug fixes -- **Improve documentation** — fix typos, add guides, clarify explanations -- **Report issues** — found a bug or have a suggestion? Open an issue +### Add a New Exploration -## Development Workflow +This is the most impactful contribution. Each exploration is a self-contained folder with metadata and an interactive widget. The [Adding an Exploration](/contributing/adding-an-exploration) guide walks you through it step by step. + +### Improve an Existing Exploration + +- Better examples and presets +- UI/UX improvements +- Bug fixes +- More informative intro and usage texts + +### Build or Improve E-Components + +[E-Components](/contributing/e-components) are reusable Ethereum-specific components (e.g. a precompile interface). If you spot a pattern shared across explorations, it might be a candidate for a new E-Component. + +### Improve Documentation -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Run linting and tests: +Fix typos, add guides, clarify explanations. Documentation lives in the `docs/` folder as standard markdown files. Preview locally with: ```bash -npm run lf # format + lint -npm run test:unit # unit tests -npm run test:e2e # E2E tests +npm run docs:dev ``` -5. Submit a pull request +### Report Issues -## Code Style +Found a bug or have a suggestion? [Open an issue](https://github.com/feelyourprotocol/website/issues) on GitHub. -- **Formatting**: Prettier (auto-formatted with `npm run format`) -- **Linting**: ESLint (auto-fixed with `npm run lint`) -- **Run both**: `npm run lf` +## Development Workflow -## Documentation +### 1. Setup -This documentation is built with [VitePress](https://vitepress.dev/). Docs live in the `docs/` folder as standard markdown files. +```bash +git clone https://github.com/feelyourprotocol/website.git +cd website +npm install +``` -To preview docs changes locally: +### 2. Develop ```bash -npm run docs:dev +npm run dev # start dev server ``` + +### 3. Verify + +Before submitting a PR, run all quality checks: + +```bash +npm run lf # format + lint (auto-fix) +npm run type-check # TypeScript type checking +npx vitest run # unit tests (single run) +npm run test:e2e # E2E tests +``` + +### 4. Submit + +- Fork the repository and create a feature branch +- Make your changes +- Ensure all checks pass +- Submit a pull request with a clear description of what you changed and why + +## What Goes Where + +| What you are working on | Where it lives | +|------------------------|----------------| +| A new exploration | `src/explorations//` | +| Exploration metadata | `src/explorations//info.ts` | +| Interactive widget | `src/explorations//MyC.vue` | +| Example presets | `src/explorations//examples.ts` | +| Exploration registry | `src/explorations/REGISTRY.ts` | +| E-Components | `src/eComponents/EC/` | +| Shared UI components | `src/eComponents/ui/` | +| Unit tests | `src/views/__tests__/` (or co-located `__tests__/`) | +| E2E tests | `cypress/e2e/` | +| Documentation | `docs/` | + +## Further Reading + +- [Adding an Exploration](/contributing/adding-an-exploration) — step-by-step guide +- [Using E-Components](/contributing/e-components) — reusable Ethereum-specific components +- [Code Conventions](/contributing/code-conventions) — imports, naming, linting, testing +- [Library Forks](/contributing/library-forks) — when you need a custom library build diff --git a/docs/contributing/library-forks.md b/docs/contributing/library-forks.md new file mode 100644 index 0000000..7f3f145 --- /dev/null +++ b/docs/contributing/library-forks.md @@ -0,0 +1,46 @@ +# Library Forks + +Feel Your Protocol runs real Ethereum library code in the browser. Some explorations need a modified version of a library — for example, a version with a new precompile implementation or experimental gas calculation changes that have not been released yet. + +Rather than waiting for upstream releases, the project maintains targeted forks used by specific exploration pages. + +## How Forks Work + +### npm Package Aliases + +Multiple versions of the same library coexist in `package.json` using npm aliases: + +```json +{ + "dependencies": { + "@ethereumjs/evm": "^10.1.1-nightly.1", + "@ethereumjs/evm-experimental": "npm:@ethereumjs/evm@^11.0.0-fork.1" + } +} +``` + +Each alias is a fully independent install. In code, you import the specific version you need: + +```typescript +import { EVM } from '@ethereumjs/evm' +import { EVM as EVMExp } from '@ethereumjs/evm-experimental' +``` + +### Monorepo Libraries + +For libraries from monorepos (like EthereumJS), where the target package has several intra-monorepo dependencies, use **pre-bundled ESM builds**. The fork is bundled on the monorepo side with all internal dependencies resolved, producing a single ESM file with no internal wiring issues. + +### Per-Route Isolation + +Each fork is only imported in its specific exploration's `MyC.vue`. Thanks to Vite's code splitting, the fork's code is only loaded when the user visits that exploration's page. Other pages are unaffected. + +**Key rule:** Only import fork-specific libraries in your `MyC.vue` — never in shared code or E-Components. + +## Adding a New Fork + +1. Fork the upstream library (or create a branch if you have access) +2. Make your changes +3. For single-package repos: add an npm alias in `package.json` pointing to your fork +4. For monorepo packages: create a bundled build, then reference it +5. Import the fork only in your exploration's `MyC.vue` +6. Document the fork in your PR description diff --git a/docs/contributing/styling.md b/docs/contributing/styling.md new file mode 100644 index 0000000..3a5fb13 --- /dev/null +++ b/docs/contributing/styling.md @@ -0,0 +1,180 @@ +# Styling & Design + +The project uses [Tailwind CSS v4](https://tailwindcss.com/) for styling with a design system built on **CSS custom properties** that automatically adapt to the topic color of each exploration. This means contributors can focus on building their widget logic — colors, fonts, and spacing are handled by the shared design system. + +## How Topic Colors Work + +Every exploration belongs to a **topic** (e.g. "Fusaka"), and each topic has a color (blue, yellow, green, red). The `ExplorationC` wrapper component sets CSS custom properties on its root element based on the topic color. All child components — UI components, E-Components, and your widget — automatically inherit these colors. + +``` +Topic (e.g. Fusaka = blue) + → ExplorationC sets --e-* CSS variables + → All children inherit colors automatically +``` + +You never need to think about colors in your exploration widget. Every UI component and CSS class picks up the right color from the topic. + +### Available CSS Variables + +These variables are set by `ExplorationC` and available to all child elements: + +| Variable | Purpose | Blue topic example | +|----------|---------|-------------------| +| `--e-text` | Primary text color | `blue-900` | +| `--e-bg` | Exploration wrapper background | `blue-200` | +| `--e-bg-light` | Input backgrounds | `blue-50` | +| `--e-bg-medium` | Button backgrounds | `blue-100` | +| `--e-bg-dark` | Result box backgrounds | `blue-900` | +| `--e-border` | Light borders | `blue-200` | +| `--e-border-dark` | Dark borders | `blue-900` | + +If you need the topic color for custom elements, use the `.e-text` utility class or reference the variables directly: + +```html + +

This text uses the topic color.

+ + +
Custom accent
+``` + +## CSS Classes + +The design system provides semantic CSS classes defined in `src/main.css`. Use these instead of assembling Tailwind utilities manually — they're topic-color-aware and consistent across all explorations. + +### Result Boxes + +Result boxes are the dark panels used to display computation output (e.g. gas costs, hex results): + +| Class | Purpose | +|-------|---------| +| `e-result-box` | Dark result panel container | +| `e-result-title` | Title label inside a result box | +| `e-result-text-lg` | Large text (e.g. gas numbers) | +| `e-result-text-md` | Medium text (e.g. status messages) | +| `e-result-text-sm` | Small monospace text (e.g. hex output) | + +Usage with the `ResultBoxUIC` component: + +```vue +
+ +

21000 Gas

+

Result: 0xdeadbeef...

+
+ +

42000 Gas

+

Result: 0xcafebabe...

+
+
+``` + +### Grid Layouts + +| Class | Purpose | +|-------|---------| +| `e-grid-single` | Single-column grid for result panels | +| `e-grid-double` | Two-column grid for side-by-side comparison | + +### Form Inputs + +These classes are already applied inside the shared UI components (`HexDataInputUIC`, `ExamplesUIC`). You typically don't need them directly, but they're available: + +| Class | Purpose | +|-------|---------| +| `e-textarea` | Hex data input textarea | +| `e-select` | Dropdown select (examples selector) | +| `e-input` | Single-line input field | + +### Buttons + +| Class | Purpose | +|-------|---------| +| `e-action-button` | Primary action button (requires `group` class alongside for tooltip support) | +| `e-button-icon` | Small icon button | + +### Text + +| Class | Purpose | +|-------|---------| +| `e-text` | Applies the topic text color | + +## UI Components + +Most styling is encapsulated in reusable UI components so you don't need to apply classes manually. Use these in your `MyC.vue`: + +| Component | What it does | +|-----------|-------------| +| `ExamplesUIC` | Example selector dropdown — already styled with `e-select` | +| `HexDataInputUIC` | Hex data input textarea — already styled with `e-textarea` | +| `ResultBoxUIC` | Result display box with title, info text, and error text — uses `e-result-box` and `e-result-title` | +| `ActionButtonUIC` | Action button with loading state and tooltip | +| `ButtonUIC` | Small icon button with tooltip | + +Import them from `@/eComponents/ui/`: + +```typescript +import ResultBoxUIC from '@/eComponents/ui/resultBox/ResultBoxUIC.vue' +import HexDataInputUIC from '@/eComponents/ui/HexDataInputUIC.vue' +import ActionButtonUIC from '@/eComponents/ui/ActionButtonUIC.vue' +``` + +## Custom Styling + +While the design system handles the common cases, you can always add custom Tailwind classes for exploration-specific elements. A few guidelines: + +### Do + +- Use the `e-*` CSS classes for result boxes, grids, inputs, and buttons +- Use `e-text` when you need the topic color on custom text +- Use standard Tailwind utilities for spacing, layout, and sizing (`mt-4`, `grid`, `text-sm`, etc.) +- Reference `--e-*` CSS variables for custom color needs + +### Don't + +- Hardcode color values like `text-blue-900` or `bg-blue-50` — use the CSS variables or `e-text` instead, so your exploration works with any topic color +- Duplicate styling that's already in a UI component — use the component instead +- Override `e-*` classes unless you have a specific reason + +### Example: Mixing Design System with Custom Layout + +```vue + +``` + +## Where Styles Live + +| What | Where | +|------|-------| +| Global design system (`e-*` classes, CSS variables) | `src/main.css` | +| Topic color definitions | `src/explorations/TOPICS.ts` | +| CSS variable propagation | `src/explorations/ExplorationC.vue` | +| E-Component-specific classes | `src/main.css` (namespaced section) | +| Component-level Tailwind classes | Inline in each `.vue` template | diff --git a/docs/contributing/ui-components.md b/docs/contributing/ui-components.md new file mode 100644 index 0000000..e0f8982 --- /dev/null +++ b/docs/contributing/ui-components.md @@ -0,0 +1,143 @@ +# UI Components + +The project provides a set of generic, reusable UI components that you can use in any exploration or E-Component. They live in `src/eComponents/ui/` and are already styled with the exploration design system — colors adapt automatically to the current topic. + +## Where UI Components Live + +UI components are organized by scope: + +``` +src/eComponents/ +├── ui/ # Shared across all E-Components and explorations +│ ├── resultBox/ # Result display components +│ │ └── ResultBoxUIC.vue +│ ├── ExamplesUIC.vue +│ ├── HexDataInputUIC.vue +│ ├── ActionButtonUIC.vue +│ ├── ButtonUIC.vue +│ └── TooltipUIC.vue +└── precompileInterfaceEC/ + └── ui/ # (future) Components specific to this E-Component +``` + +The placement rules: + +| Scope | Location | Example | +|-------|----------|---------| +| Used across multiple E-Components or explorations | `src/eComponents/ui/` | `ResultBoxUIC`, `ExamplesUIC` | +| Specific to one E-Component | `src/eComponents//ui/` | (none yet) | +| Specific to one exploration | `src/explorations//custom/ui/` | (none yet) | + +## Available Components + +### ExamplesUIC + +Example selector dropdown built on [Headless UI Listbox](https://headlessui.dev/v1/vue/listbox). Provides an accessible, keyboard-navigable dropdown with pre-defined example presets. + +```vue + +``` + +| Prop | Type | Description | +|------|------|-------------| +| `v-model` | `string` | Selected example key | +| `examples` | `Examples` | Object mapping keys to `{ title, values }` | +| `change` | `() => void` | Called when selection changes | + +### HexDataInputUIC + +Hex data input textarea for raw byte input. + +```vue + +``` + +| Prop | Type | Description | +|------|------|-------------| +| `v-model` | `string` | The hex data string | +| `rows` | `number` | Textarea row count | +| `formChange` | `() => void` | Called on input change | + +### ResultBoxUIC + +Result display box with a title label. Used for showing computation output. Has built-in support for placeholder info text and error messages via optional props. + +```vue + +

21000 Gas

+
+``` + +| Prop | Type | Description | +|------|------|-------------| +| `title` | `string` | Title label shown in the box | +| `left` | `boolean` | Alignment: `true` for left, `false` for right | +| `infoText` | `string?` | Placeholder text shown when no content is available | +| `errorText` | `string?` | Error message (red) with a "Report on GitHub" hint | + +`errorText` takes precedence over `infoText`. Both render below the slot content, so conditionally pass them only when the slot is empty: + +```vue + + ...
+
+``` + +Use with `e-grid-single` or `e-grid-double` for layout: + +```vue +
+ ... + ... +
+``` + +### ActionButtonUIC + +Async action button with loading state and tooltip. Disables itself and shows "Loading..." while the async handler runs. + +```vue + +``` + +| Prop | Type | Description | +|------|------|-------------| +| `text` | `string` | Button label | +| `tooltip` | `string` | Tooltip text on hover | +| `onClick` | `() => Promise` | Async click handler | + +### ButtonUIC + +Small icon button with tooltip. Used internally by `ExplorationC` for share and external-link icons. + +```vue + +``` + +### TooltipUIC + +CSS tooltip wrapper. Used internally by `ButtonUIC` and `ActionButtonUIC`. You typically don't need this directly — use `ActionButtonUIC` or `ButtonUIC` instead. + +## Underlying Libraries + +Some UI components use [Headless UI](https://headlessui.dev/) (`@headlessui/vue`) — a set of completely unstyled, accessible UI primitives designed for Tailwind CSS. Headless UI handles keyboard navigation, focus management, and ARIA attributes while we control all styling via the exploration design system. + +Currently used by: +- `ExamplesUIC` — uses the Headless UI **Listbox** component + +As a contributor you don't need to interact with Headless UI directly — just use the UIC components as documented above. + +## Importing + +All shared UI components use the `@/eComponents/ui/` path: + +```typescript +import ExamplesUIC from '@/eComponents/ui/ExamplesUIC.vue' +import ResultBoxUIC from '@/eComponents/ui/resultBox/ResultBoxUIC.vue' +import ActionButtonUIC from '@/eComponents/ui/ActionButtonUIC.vue' +``` diff --git a/docs/guide/architecture.md b/docs/guide/architecture.md index f0eea0b..eea5731 100644 --- a/docs/guide/architecture.md +++ b/docs/guide/architecture.md @@ -1,9 +1,5 @@ # Architecture -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. -::: - ## Overview Feel Your Protocol is a Vue 3 application built with Vite. The core idea is simple: each Ethereum protocol change gets its own interactive widget that runs real library code in the browser. @@ -15,69 +11,77 @@ Feel Your Protocol is a Vue 3 application built with Vite. The core idea is simp - **[Tailwind CSS v4](https://tailwindcss.com/)** for styling - **[Vue Router](https://router.vuejs.org/)** with route-level code splitting - **[VitePress](https://vitepress.dev/)** for documentation +- **[Vitest](https://vitest.dev/)** + **[Vue Test Utils](https://test-utils.vuejs.org/)** for unit testing - **[Cypress](https://www.cypress.io/)** for E2E testing - **[Heroicons](https://heroicons.com/)** for icons (`@heroicons/vue`) -## Content Structure +## Content Model Content is organized around two concepts: -- **Explorations** — the core unit. Each exploration represents an interactive widget for a protocol change (EIP, ERC, or other research topic). Explorations live in `src/explorations/`. -- **Topics** — group explorations by theme (e.g. "Fusaka" for an upcoming hardfork). Each exploration can belong to zero or more topics via the `topics` array. Topics are defined in `src/explorations/TOPICS.ts`. +### Explorations + +The core content unit. Each exploration represents an interactive widget for a protocol change — EIPs, ERCs, or research topics. Explorations live in `src/explorations/`, one folder per exploration: + +``` +src/explorations/eip-7883/ +├── info.ts # Metadata: id, title, path, topic, introText, poweredBy, … +├── MyC.vue # The interactive widget +└── examples.ts # Example presets for the widget +``` + +Each `info.ts` exports a `const INFO` object typed as `Exploration`. The `REGISTRY.ts` imports all `INFO` constants and assembles them into the `EXPLORATIONS` dictionary. The router reads from this dictionary to automatically create routes — no manual route registration needed. + +### Topics -### The `src/explorations/` Folder +Topics group explorations by theme (e.g. "Fusaka" for an upcoming hardfork). Each exploration belongs to exactly one topic via the `topic` field in its `info.ts`. Topics are defined in `src/explorations/TOPICS.ts`. -This is the heart of the project. Each exploration is a self-contained folder: +## E-Components + +**E-Components** are reusable Ethereum-specific components that encapsulate common patterns across explorations. They live in `src/eComponents/` and follow a naming convention: folder and component names are post-fixed with `EC`. + +The first E-Component is `precompileInterfaceEC`, which provides a complete precompile exploration interface — input parsing, hardfork comparison, result display — as a single component backed by a composable: ``` -src/explorations/ -├── REGISTRY.ts # Assembles all explorations, exports EXPLORATIONS dict -├── TOPICS.ts # Topic definitions -├── ExplorationC.vue # Shared wrapper component (title, description, buttons) -├── PoweredByC.vue # Shared "powered by" footer -├── eip-7594/ -│ ├── info.ts # Metadata: id, title, path, infoURL, introText, usageText, … -│ └── MyC.vue # The interactive widget -├── eip-7883/ -│ ├── info.ts -│ └── MyC.vue -└── eip-7951/ - ├── info.ts - └── MyC.vue +src/eComponents/precompileInterfaceEC/ +├── PrecompileInterfaceEC.vue # Full-featured precompile exploration template +├── PrecompileInterfaceResultEC.vue # Result display (pre/post hardfork comparison) +├── PrecompileValueInputEC.vue # Value input with byte length validation +├── usePrecompileState.ts # Composable: all state and logic +├── types.ts # PrecompileConfig and PrecompileValueDef interfaces +└── run.ts # EVM precompile execution utility ``` -Each `info.ts` exports a `const INFO` object typed as `Exploration`. The `REGISTRY.ts` imports all `INFO` constants and assembles them into the `EXPLORATIONS` dictionary. The router reads from this to automatically create routes — no manual route registration needed. +Using the Precompile Interface E-Component, a precompile exploration widget can be as short as 30 lines — just a config object and a single component tag. See [Using E-Components](/contributing/e-components) for details. + +## UI Components -### Exploration Metadata (`info.ts`) +Generic UI components live in `src/eComponents/ui/` alongside the E-Components they serve. These are reusable building blocks available for any exploration or E-Component: -Each exploration's `info.ts` contains all its metadata as a flat object: +| Component | Purpose | +|-----------|---------| +| `ExamplesUIC` | Example selector dropdown | +| `HexDataInputUIC` | Hex data input textarea | +| `ResultBoxUIC` | Result display box with title, info text, and error text | +| `ActionButtonUIC` | Async action button with loading state and tooltip | +| `ButtonUIC` | Icon button with tooltip | +| `TooltipUIC` | CSS tooltip wrapper | + +Import them from `@/eComponents/ui/`: ```typescript -import type { Exploration } from '../REGISTRY' - -export const INFO: Exploration = { - id: 'eip-XXXX', - path: '/eip-XXXX-short-description', - title: 'Human-Readable Title', - infoURL: 'https://eips.ethereum.org/EIPS/eip-XXXX', - topics: ['fusaka'], - image: 'fusaka.webp', - introText: 'HTML-formatted introduction.', - usageText: 'HTML-formatted usage instructions.', - poweredBy: [ - { name: 'Library Name', href: 'https://github.com/...' }, - ], -} +import ResultBoxUIC from '@/eComponents/ui/resultBox/ResultBoxUIC.vue' +import ExamplesUIC from '@/eComponents/ui/ExamplesUIC.vue' ``` ## Key Design Decisions ### Folder-per-Exploration -Each exploration is fully self-contained in its own folder with `info.ts` (metadata) and `MyC.vue` (widget). This means: +Each exploration is fully self-contained in its own folder. This means: - Contributors can focus on a single folder -- Adding a new exploration is a matter of creating a folder and adding one import to `REGISTRY.ts` +- Adding a new exploration requires creating a folder and adding one import to `REGISTRY.ts` - Each exploration's dependencies are isolated ### Dynamic Views @@ -86,12 +90,11 @@ There are no static per-exploration or per-topic view files. Instead: - **`ExplorationView.vue`** dynamically loads the correct `MyC.vue` using `import.meta.glob()` and `defineAsyncComponent()` based on the route name - **`TopicView.vue`** dynamically lists all explorations belonging to a topic - -This keeps the views lean and avoids boilerplate when adding new content. +- **`HomeView.vue`** dynamically renders all topics defined in `TOPICS.ts` ### Route-Level Code Splitting -The `ExplorationView.vue` uses `import.meta.glob()` for lazy loading: +Each exploration is a separate chunk that is loaded on demand. Users only download the libraries needed for the page they visit. This is achieved via `import.meta.glob()` for lazy loading: ```typescript const componentModules = import.meta.glob('../explorations/*/MyC.vue') @@ -100,8 +103,11 @@ const ExplorationComponent = defineAsyncComponent( ) ``` -Each exploration is a separate chunk that's loaded on demand. Users only download the libraries needed for the page they visit. +### Testing Strategy + +The project uses a hybrid testing approach: -### Library Forks +- **Unit tests** (Vitest + Vue Test Utils) for component rendering, content verification, and UI logic — fast and focused +- **E2E tests** (Cypress) as lean smoke tests for critical navigation flows and page-level integration -Some exploration widgets require custom forks of Ethereum libraries (see [Library Forks](./library-forks.md)). The architecture ensures that different forks of the same library can coexist without conflicts, each isolated to its specific exploration route. +Unit tests live alongside their components in `__tests__/` folders. E2E tests are consolidated in `cypress/e2e/`. diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index ed15fd7..ac28304 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -1,16 +1,10 @@ # Getting Started -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. -::: - ## What is Feel Your Protocol? Feel Your Protocol is an interactive website that lets you explore Ethereum protocol changes hands on. Instead of just reading specifications, you can interact with real Ethereum library code running directly in the browser. -Each protocol change — called an **Exploration** — gets its own page with a dedicated interactive widget. For example, the [EIP-7883](https://feelyourprotocol.org/eip-7883-modexp-gas-cost-increase) page lets you experiment with ModExp gas cost changes interactively. - -Explorations are grouped into **Topics** (e.g. "Fusaka" for the upcoming hardfork), making it easy to discover related protocol changes. +Each protocol change — called an **Exploration** — gets its own page with a dedicated interactive widget. Explorations cover EIPs, ERCs, and other research topics. They are grouped into **Topics** (e.g. "Fusaka" for the upcoming hardfork), making it easy to discover related protocol changes. ## Prerequisites @@ -41,9 +35,23 @@ Start the docs dev server: npm run docs:dev ``` -## Building +## Quality Checks + +```bash +npm run lf # format + lint (auto-fix) +npm run lf:ci # lint + format check (CI mode, no auto-fix) +npm run type-check # TypeScript type checking (vue-tsc) +``` -Build both the website and documentation: +## Testing + +```bash +npx vitest run # unit tests (single run) +npm run test:unit # unit tests (watch mode) +npm run test:e2e # E2E tests (Cypress, requires build first) +``` + +## Building ```bash npm run build # website → dist/website @@ -55,33 +63,36 @@ npm run docs:build # docs → dist/docs ``` website/ ├── src/ -│ ├── explorations/ # Explorations (the core content) -│ │ ├── REGISTRY.ts # Exploration registry, types, helper functions -│ │ ├── TOPICS.ts # Topic definitions and types -│ │ ├── ExplorationC.vue # Shared exploration wrapper component -│ │ ├── PoweredByC.vue # Shared "powered by" component -│ │ ├── eip-7594/ # One folder per exploration -│ │ │ ├── info.ts # Metadata (title, description, links, …) -│ │ │ └── MyC.vue # Interactive widget component +│ ├── explorations/ # Explorations (the core content) +│ │ ├── REGISTRY.ts # Exploration registry and types +│ │ ├── TOPICS.ts # Topic definitions and types +│ │ ├── ExplorationC.vue # Shared exploration wrapper component +│ │ ├── PoweredByC.vue # Shared "powered by" component +│ │ ├── eip-7594/ # One folder per exploration +│ │ │ ├── info.ts # Metadata (title, description, links, …) +│ │ │ ├── MyC.vue # Interactive widget component +│ │ │ ├── examples.ts # Example presets +│ │ │ └── data/ # Optional data files │ │ ├── eip-7883/ -│ │ │ ├── info.ts -│ │ │ └── MyC.vue │ │ └── eip-7951/ -│ │ ├── info.ts -│ │ └── MyC.vue -│ ├── components/ # Shared UI and utility components -│ │ ├── ui/ # Generic UI components -│ │ ├── precompiles/ # Precompile-related shared components -│ │ └── lib/ # Shared logic and utilities -│ ├── views/ # Route views +│ ├── eComponents/ # Reusable Ethereum-specific components (E-Components) +│ │ └── precompileInterfaceEC/ # Precompile interface E-Component +│ │ ├── PrecompileInterfaceEC.vue +│ │ ├── PrecompileInterfaceResultEC.vue +│ │ ├── PrecompileValueInputEC.vue +│ │ ├── usePrecompileState.ts +│ │ ├── types.ts +│ │ └── run.ts +│ ├── components/ # Shared UI components and utilities +│ │ ├── ui/ # Generic UI components +│ │ └── lib/ # Shared logic and utilities +│ ├── views/ # Route views │ │ ├── HomeView.vue │ │ ├── TopicView.vue │ │ ├── ExplorationView.vue -│ │ └── TopicIntroView.vue -│ └── router/ # Vue Router config -├── docs/ # Documentation (VitePress) -├── cypress/ # E2E tests -└── dist/ # Build output - ├── website/ - └── docs/ +│ │ └── __tests__/ # Unit tests +│ └── router/ # Vue Router config +├── docs/ # Documentation (VitePress) +├── cypress/ # E2E tests +└── .github/workflows/ # CI workflows ``` diff --git a/docs/guide/library-forks.md b/docs/guide/library-forks.md deleted file mode 100644 index 914be73..0000000 --- a/docs/guide/library-forks.md +++ /dev/null @@ -1,50 +0,0 @@ -# Library Forks - -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. -::: - -## Why Forks? - -Feel Your Protocol runs real Ethereum library code in the browser. Some exploration widgets need a modified version of an existing library — for example, a version with a new precompile implementation, or experimental gas calculation changes that haven't been released yet. - -Rather than waiting for upstream releases, we maintain targeted forks that are used by specific exploration pages. - -## How Forks Work - -### npm Package Aliases - -Multiple versions of the same library coexist in `package.json` using npm aliases: - -```json -{ - "dependencies": { - "@ethereumjs/evm": "^10.1.1-nightly.1", - "@ethereumjs/evm-experimental": "npm:@ethereumjs/evm@^11.0.0-fork.1" - } -} -``` - -Each alias is a fully independent install. In code, you import the specific version you need: - -```typescript -import { EVM } from '@ethereumjs/evm' // standard -import { EVM as EVMExp } from '@ethereumjs/evm-experimental' // fork -``` - -### Monorepo Libraries - -For libraries from monorepos (like EthereumJS), where the target package has several intra-monorepo dependencies, we use **pre-bundled ESM builds**. The fork is bundled on the monorepo side with all internal dependencies resolved, producing a single ESM file with no internal wiring issues. - -### Per-Route Isolation - -Each fork is only imported in its specific exploration's `MyC.vue`. Thanks to Vite's code splitting, the fork's code is only loaded when the user visits that exploration's page. Other pages are unaffected. - -## Adding a New Fork - -1. Fork the upstream library (or create a branch if you have access) -2. Make your changes -3. For single-package repos: add an npm alias pointing to your fork's git URL -4. For monorepo packages: create a bundled build, then reference it -5. Import the fork only in your exploration's `MyC.vue` file — never in shared code -6. Document the fork in this page diff --git a/docs/index.md b/docs/index.md index 054aa5c..80b025d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ hero: text: Get Started link: /guide/getting-started - theme: alt - text: How to Contribute + text: Contribute link: /contributing/how-to-contribute - theme: alt text: View on GitHub @@ -18,13 +18,15 @@ hero: features: - title: Interactive Explorations - details: Each exploration gets its own self-contained folder with an interactive component that lets you explore the protocol change hands on. + details: Each exploration is a self-contained folder with an interactive widget that lets you explore a protocol change hands on — EIPs, ERCs, or promising research. - title: Powered by Real Libraries - details: Widgets run actual Ethereum library code in the browser — no mocks, no simplifications. + details: Widgets run actual Ethereum library code in the browser — no mocks, no simplifications. See how protocol changes behave with real inputs. + - title: Reusable E-Components + details: Common Ethereum patterns like precompile interfaces are packaged as reusable E-Components with stable APIs, so you can build new explorations fast. - title: Collaborative & Open Source - details: Designed for community contributions. The modular folder-per-exploration structure makes it easy to add new explorations or improve existing ones. + details: Designed for community contributions. A modular folder-per-exploration structure and shared components make it straightforward to add new explorations. --- -::: warning Under Active Development -Both the Feel Your Protocol project and this documentation are in an early stage and under active development. Things may change frequently. Contributions and feedback are very welcome! +::: tip Want to contribute? +The fastest way to get started is to [add a new exploration](/contributing/adding-an-exploration). Each one lives in a single folder with just two files — metadata and your interactive widget. ::: diff --git a/eslint.config.ts b/eslint.config.ts index 74c0b40..895caf8 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -1,11 +1,12 @@ import { globalIgnores } from 'eslint/config' -import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' -import pluginVue from 'eslint-plugin-vue' -import pluginVitest from '@vitest/eslint-plugin' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import pluginCypress from 'eslint-plugin-cypress' +import simpleImportSort from 'eslint-plugin-simple-import-sort' +import pluginVue from 'eslint-plugin-vue' +import pluginVitest from '@vitest/eslint-plugin' import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' +import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' // To allow more languages other than `ts` in `.vue` files, uncomment the following lines: // import { configureVueProject } from '@vue/eslint-config-typescript' @@ -22,7 +23,25 @@ export default defineConfigWithVueTs( pluginVue.configs['flat/essential'], vueTsConfigs.recommended, - + + { + name: 'app/import-sort', + plugins: { 'simple-import-sort': simpleImportSort }, + rules: { + 'simple-import-sort/imports': ['error', { + groups: [ + // 1) External packages (anything not starting with . or @/) + ['^[^.@]', '^@(?!/)'], + // 2) Internal @/ alias imports + ['^@/'], + // 3) Local ./ and ../ relative imports + ['^\\.'], + ], + }], + 'simple-import-sort/exports': 'error', + }, + }, + { ...pluginVitest.configs.recommended, files: ['src/**/__tests__/*'], diff --git a/package-lock.json b/package-lock.json index 2b8a37c..3294bef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@ethereumjs/common": "^10.1.1-nightly.1", "@ethereumjs/evm": "^10.1.1-nightly.1", "@ethereumjs/util": "^10.1.1-nightly.1", + "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.2.0", "@paulmillr/trusted-setups": "^0.2.0", "@tailwindcss/vite": "^4.1.13", @@ -32,6 +33,7 @@ "cypress": "^14.5.3", "eslint": "^9.31.0", "eslint-plugin-cypress": "^5.1.0", + "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-vue": "~10.3.0", "jiti": "^2.4.2", "jsdom": "^26.1.0", @@ -1488,6 +1490,21 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@headlessui/vue": { + "version": "1.7.23", + "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.23.tgz", + "integrity": "sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==", + "license": "MIT", + "dependencies": { + "@tanstack/vue-virtual": "^3.0.0-beta.60" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/@heroicons/vue": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-2.2.0.tgz", @@ -2494,6 +2511,32 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.19", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.19.tgz", + "integrity": "sha512-/BMP7kNhzKOd7wnDeB8NrIRNLwkf5AhCYCvtfZV2GXWbBieFm/el0n6LOAXlTi6ZwHICSNnQcIxRCWHrLzDY+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-virtual": { + "version": "3.13.19", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.19.tgz", + "integrity": "sha512-07Fp1TYuIziB4zIDA/moeDKHODePy3K1fN4c4VIAGnkxo1+uOvBJP7m54CoxKiQX6Q9a1dZnznrwOg9C86yvvA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, "node_modules/@tsconfig/node22": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.2.tgz", @@ -5122,6 +5165,16 @@ } } }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, "node_modules/eslint-plugin-vue": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.3.0.tgz", diff --git a/package.json b/package.json index 5741f3d..ac00b10 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@ethereumjs/common": "^10.1.1-nightly.1", "@ethereumjs/evm": "^10.1.1-nightly.1", "@ethereumjs/util": "^10.1.1-nightly.1", + "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.2.0", "@paulmillr/trusted-setups": "^0.2.0", "@tailwindcss/vite": "^4.1.13", @@ -49,6 +50,7 @@ "cypress": "^14.5.3", "eslint": "^9.31.0", "eslint-plugin-cypress": "^5.1.0", + "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-vue": "~10.3.0", "jiti": "^2.4.2", "jsdom": "^26.1.0", diff --git a/src/App.vue b/src/App.vue index 89c8162..db78485 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,70 +1,127 @@