diff --git a/.discoveryrc.app.js b/.discoveryrc.app.js index 06646bd..c796b25 100644 --- a/.discoveryrc.app.js +++ b/.discoveryrc.app.js @@ -2,5 +2,6 @@ module.exports = { ...require('./.discoveryrc.js'), + meta: undefined, data: undefined }; diff --git a/.discoveryrc.ghpages.js b/.discoveryrc.ghpages.js new file mode 100644 index 0000000..06646bd --- /dev/null +++ b/.discoveryrc.ghpages.js @@ -0,0 +1,6 @@ +/* eslint-env node */ + +module.exports = { + ...require('./.discoveryrc.js'), + data: undefined +}; diff --git a/.discoveryrc.js b/.discoveryrc.js index 21ee58d..a620d1e 100644 --- a/.discoveryrc.js +++ b/.discoveryrc.js @@ -14,6 +14,10 @@ module.exports = { prepare: './prepare', data: './data', favicon: __dirname + '/app/img/favicon.png', + routers: ['./demo/router.js'], + meta: { + demos: require('./app/demo/index.json') + }, view: { assets: [ './pages/common.css', diff --git a/app/demo/chromium.jsonxl b/app/demo/chromium.jsonxl new file mode 100644 index 0000000..9972e8a Binary files /dev/null and b/app/demo/chromium.jsonxl differ diff --git a/app/demo/deno.jsonxl b/app/demo/deno.jsonxl new file mode 100644 index 0000000..6d4d70d Binary files /dev/null and b/app/demo/deno.jsonxl differ diff --git a/app/demo/edge.jsonxl b/app/demo/edge.jsonxl new file mode 100644 index 0000000..a5cc4f4 Binary files /dev/null and b/app/demo/edge.jsonxl differ diff --git a/app/demo/index.json b/app/demo/index.json new file mode 100644 index 0000000..e650367 --- /dev/null +++ b/app/demo/index.json @@ -0,0 +1,17 @@ +[ + { + "runtime": "deno", + "title": "V8 log (preprocessed)", + "url": "demo/deno.jsonxl" + }, + { + "runtime": "chromium", + "title": "Chromium Profile", + "url": "demo/chromium.jsonxl" + }, + { + "runtime": "edge", + "title": "Edge EPT", + "url": "demo/edge.jsonxl" + } +] diff --git a/app/demo/router.js b/app/demo/router.js new file mode 100644 index 0000000..4fb7064 --- /dev/null +++ b/app/demo/router.js @@ -0,0 +1,15 @@ +/* eslint-env node */ + +module.exports = function repoHealthPath(app) { + const path = require('path'); + const fs = require('fs'); + const files = JSON.parse(fs.readFileSync(path.join(__dirname, './index.json'))); + + for (const demo of files) { + app.get('/' + demo.url, function(req, res) { + const filepath = path.join(__dirname, path.basename(demo.url)); + + fs.createReadStream(filepath).pipe(res); + }); + } +}; diff --git a/app/img/runtime-edge.svg b/app/img/runtime-edge.svg new file mode 100644 index 0000000..b5b7c08 --- /dev/null +++ b/app/img/runtime-edge.svg @@ -0,0 +1,2 @@ + + diff --git a/app/pages/common.css b/app/pages/common.css index eeb6bca..3244d3e 100644 --- a/app/pages/common.css +++ b/app/pages/common.css @@ -1,3 +1,15 @@ +.discovery-root { + --runtime-nodejs: url("../img/runtime-nodejs.png"); + --runtime-deno: url("../img/runtime-deno.svg"); + --runtime-chromium: url("../img/runtime-chromium.svg"); + --runtime-edge: url("../img/runtime-edge.svg"); + --runtime-electron: url("../img/runtime-electron.svg"); + --runtime-unknown: url("../img/runtime-v8.svg"); +} +.discovery-root-darkmode { + --runtime-unknown: url("../img/runtime-v8-outline.svg"); +} + .view-alert-warning .view-link { color: inherit; text-decoration-color: inherit; diff --git a/app/pages/default.css b/app/pages/default.css index e708556..5985564 100644 --- a/app/pages/default.css +++ b/app/pages/default.css @@ -127,7 +127,10 @@ letter-spacing: 1px; padding-bottom: 2px; } -.page-default .view-page-indicators .runtime:is(.nodejs, .deno, .chromium, .electron, .unknown) .value::before { +.page-default .view-page-indicators .runtime .value { + text-align: left; +} +.page-default .view-page-indicators .runtime:is(.nodejs, .deno, .chromium, .edge, .electron, .unknown) .value::before { content: ""; display: inline-block; vertical-align: baseline; @@ -142,25 +145,26 @@ filter: brightness(.85); } .page-default .view-page-indicators .runtime:is(.nodejs) .value::before { - background-image: url("../img/runtime-nodejs.png"); + background-image: var(--runtime-nodejs); } .page-default .view-page-indicators .runtime:is(.deno) .value::before { - background-image: url("../img/runtime-deno.svg"); + background-image: var(--runtime-deno); height: 1.3em; margin-bottom: -.25em; margin-right: 0.6ex; } +.page-default .view-page-indicators .runtime:is(.edge) .value::before { + background-image: var(--runtime-edge); + filter: none; +} .page-default .view-page-indicators .runtime:is(.chromium) .value::before { - background-image: url("../img/runtime-chromium.svg"); + background-image: var(--runtime-chromium); } .page-default .view-page-indicators .runtime:is(.electron) .value::before { - background-image: url("../img/runtime-electron.svg"); + background-image: var(--runtime-electron); } .page-default .view-page-indicators .runtime:is(.unknown) .value::before { - background-image: url("../img/runtime-v8.svg"); -} -.discovery-root-darkmode .page-default .view-page-indicators .runtime:is(.unknown) .value::before { - background-image: url("../img/runtime-v8-outline.svg"); + background-image: var(--runtime-unknown); } .page-default .view-expand.timelines > .header > .header-content { @@ -325,14 +329,67 @@ font-weight: 200; font-size: 13px; } +.page-default .welcome-page .view-page-header__content h1 { + position: relative; + top: -11px; +} +.page-default .welcome-page .view-page-header__content .description { + padding-left: 55px; + margin-top: -17px; + font-size: 12px; + color: #aaa; +} .page-default .welcome-page .view-markdown .view-ul { margin-top: 2px; } .page-default .welcome-page .upload-data { - margin: 2em 0; + margin: -8px -15px 15px; + background-color: #00000014; + border: 1px dashed #555; + padding: 12px; + border-radius: 7px; + max-width: 620px; } .page-default .welcome-page .upload-notes { font-size: 82%; color: #aaa; - padding: 6px 0 0 6px; + padding: 10px 0 0 3px; +} +.page-default .welcome-page .examples { + margin-top: 15px; +} +.page-default .welcome-page .examples .view-button { + margin-top: .35em; + margin-left: 0; + margin-right: .8ex; +} +.page-default .welcome-page .examples .view-button::before { + content: ""; + display: inline-block; + vertical-align: middle; + height: 1.3em; + aspect-ratio: 1/1; + background: center no-repeat; + background-size: 100%; + margin-right: 7px; + margin-left: -.5ex; + margin-top: -4px; +} +.page-default .welcome-page .examples .view-button.nodejs::before { + background-image: var(--runtime-nodejs); +} +.page-default .welcome-page .examples .view-button.deno::before { + background-image: var(--runtime-deno); + filter: brightness(.8); + height: 1.4em; + margin-top: -5px; + margin-bottom: -2px; +} +.page-default .welcome-page .examples .view-button.chromium::before { + background-image: var(--runtime-chromium); + filter: brightness(.85); +} +.page-default .welcome-page .examples .view-button.edge::before { + background-image: var(--runtime-edge); + filter: brightness(.95); } diff --git a/app/pages/default.js b/app/pages/default.js index 79025b4..c8cdbb9 100644 --- a/app/pages/default.js +++ b/app/pages/default.js @@ -2,6 +2,13 @@ const demoDataBase64 = require('../demo/demo-data-base64.js').default; const { supportedFormats } = require('../prepare/index.js'); +function consumeDemos() { + const demos = discovery.context?.model?.meta?.demos; + if (demos) { + discovery.action.define('demos', () => demos); + } +} + discovery.action.define('uploadDemoData', () => discovery.loadDataFromUrl(demoDataBase64)); setTimeout(() => { discovery.nav.primary.append({ @@ -16,7 +23,7 @@ setTimeout(() => { onClick: () => toggleFullPageFlamechart(false) }); discovery.nav.menu.append({ - when: true, + when: '#.actions.unloadData', content: 'text:"Unload cpuprofile"', onClick(_, ctx) { ctx.hide(); @@ -24,7 +31,12 @@ setTimeout(() => { ctx.widget.setPageHash(''); } }); + discovery.nav.render(discovery.dom.nav, discovery.data, discovery.getRenderContext()); + + // FIXME: temporary solution, since context is cleaning up on data load/unload + discovery.on('data', consumeDemos); + consumeDemos(); }, 1); function toggleFullPageFlamechart(fullpageMode) { @@ -394,22 +406,14 @@ const noDataPageContent = { view: 'page-header', content: [ { view: 'block', className: 'logo' }, - 'h1:"cpupro"' - ] - }, - - { - view: 'markdown', - source: [ - 'A viewer for CPU profiles captured in V8 runtimes like Node.js, Deno or Chromium browsers.', - '', - 'Supported formats:', - ...supportedFormats + 'h1:"cpupro"', + { view: 'block', className: 'description', content: 'text:"A viewer for CPU profiles captured in V8 runtimes like Node.js, Deno or Chromium browsers"' } ] }, { view: 'block', + when: '#.actions.uploadFile', className: 'upload-data', content: [ 'preset/upload', @@ -422,9 +426,40 @@ const noDataPageContent = { }, { - view: 'button', - onClick: '=#.actions.uploadDemoData', - content: 'text:"Try demo CPU profile"' + view: 'markdown', + source: [ + 'Supported formats:', + ...supportedFormats + ] + }, + + { + view: 'block', + className: 'examples', + content: [ + 'text:"Try out example:"', + 'html:"
"', + { + view: 'button', + className: 'nodejs', + onClick: '=#.actions.uploadDemoData', + content: 'text:"V8 CPU profile"' + }, + { + view: 'inline-list', + when: '#.actions.demos', + data: '"demos".callAction()', + whenData: true, + item: { + view: 'button', + className: '=runtime', + onClick(_, data) { + discovery.loadDataFromUrl(data.url); + }, + content: 'text:title' + } + } + ] } ] }; @@ -444,7 +479,8 @@ discovery.page.define('default', { view: 'h2', content: [ { view: 'block', className: 'logo' }, - 'text:#.datasets[].resource | type = "file" ? name : "Untitled profile"' + // 'text:#.datasets[].resource | type = "file" ? name : "Untitled profile"' + 'md:"data loaded in **{{#.datasets[].timing.responseTime}}** ms"' ] } ] diff --git a/package.json b/package.json index 63d867d..258e56f 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "build-wasm": "node scripts/wat-compile.js", "build-app": "discovery-build --clean --config .discoveryrc.app.js --single-file --no-data --entry-names \"app\"", "build-report-template": "discovery-build --single-file --no-data --no-model-data-upload --entry-names \"report\"", - "build-gh-pages": "npm run build-wasm && discovery-build --config .discoveryrc.app.js --single-file --no-data -o .gh-pages", + "build-gh-pages": "npm run build-wasm && discovery-build --config .discoveryrc.ghpages.js --single-file --no-data -o .gh-pages && node scripts/gh-pages-files", "prepublishOnly": "npm run build" }, "dependencies": { diff --git a/scripts/gh-pages-files.js b/scripts/gh-pages-files.js new file mode 100644 index 0000000..b70669a --- /dev/null +++ b/scripts/gh-pages-files.js @@ -0,0 +1,18 @@ +/* eslint-env node */ + +const path = require('path'); +const fs = require('fs'); +const demosPath = path.join(__dirname, '../app/demo'); +const ghpagesDemosPath = path.join(__dirname, '../.gh-pages/demo'); + +const indexFile = fs.readFileSync(path.join(demosPath, 'index.json')); +const indexData = JSON.parse(indexFile); + +fs.mkdirSync(ghpagesDemosPath, { recursive: true }); + +for (const demo of indexData) { + fs.cpSync( + path.join(demosPath, path.basename(demo.url)), + path.join(ghpagesDemosPath, path.basename(demo.url)) + ); +}