@vfrey/vfrey 0.0.1 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +33 -0
- package/README.md +78 -3
- package/accounts.example.json +61 -0
- package/accounts.first-run.json +41 -0
- package/bin/vfrey.mjs +36 -0
- package/integration-tools/README.md +43 -0
- package/integration-tools/image/pillow.md +32 -0
- package/integration-tools/image/playwright.md +33 -0
- package/integration-tools/image/resvg.md +36 -0
- package/integration-tools/image/satori.md +36 -0
- package/integration-tools/image/sharp.md +38 -0
- package/integration-tools/video/blender.md +34 -0
- package/integration-tools/video/ffmpeg.md +41 -0
- package/integration-tools/video/manim.md +37 -0
- package/integration-tools/video/motion-canvas.md +37 -0
- package/integration-tools/video/remotion.md +50 -0
- package/integration-tools/video/revideo.md +32 -0
- package/package.json +74 -6
- package/skills/vfrey-orchestrator/SKILL.md +15 -0
- package/skills/vfrey-orchestrator/channel-video/SKILL.md +41 -0
- package/skills/vfrey-product/SKILL.md +63 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-one.svg +14 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-two.svg +22 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/index.html +70 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-one.svg +14 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-two.svg +22 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/index.tsx +49 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/script.json +15 -0
- package/skills/vfrey-product/examples/octopus-facts/index.html +60 -0
- package/skills/vfrey-product/examples/octopus-facts/octopus.svg +8 -0
- package/skills/vfrey-product/examples/octopus-facts/script.json +14 -0
- package/skills/vfrey-product/push/SKILL.md +15 -0
- package/skills/vfrey-product/scenes/NOTICE +5 -0
- package/skills/vfrey-product/scenes/blocks.md +57 -0
- package/skills/vfrey-product/scenes/craft.md +118 -0
- package/skills/vfrey-product/scenes/motion.md +88 -0
- package/src/analytics/dashboard.ts +109 -0
- package/src/analytics/index.ts +260 -0
- package/src/api/client.ts +282 -0
- package/src/api/commands.ts +330 -0
- package/src/api/config.ts +34 -0
- package/src/api/creators.ts +463 -0
- package/src/api/credentials.ts +152 -0
- package/src/api/mail.ts +333 -0
- package/src/api/topics.ts +207 -0
- package/src/api/types.ts +205 -0
- package/src/cli.ts +467 -0
- package/src/cloud/executor.ts +281 -0
- package/src/cloud/job.ts +106 -0
- package/src/cloud/render-job.ts +187 -0
- package/src/config.ts +309 -0
- package/src/dashboard-link.ts +180 -0
- package/src/dashboard-start.ts +103 -0
- package/src/desk/channel.ts +64 -0
- package/src/desk/cli.ts +216 -0
- package/src/desk/creators-route.ts +129 -0
- package/src/desk/fonts/JetBrainsMono-OFL.txt +93 -0
- package/src/desk/fonts/google-sans-flex-latin.woff2 +0 -0
- package/src/desk/fonts/jetbrains-mono-medium.woff2 +0 -0
- package/src/desk/http.ts +460 -0
- package/src/desk/mail-route.ts +103 -0
- package/src/desk/overlay.ts +112 -0
- package/src/desk/preview.ts +422 -0
- package/src/desk/screens.ts +149 -0
- package/src/desk/secrets.ts +143 -0
- package/src/desk/series.ts +127 -0
- package/src/desk/state.ts +77 -0
- package/src/desk/studio-route.ts +119 -0
- package/src/desk/tick-runner.ts +66 -0
- package/src/desk/tick.ts +56 -0
- package/src/desk/topics-route.ts +41 -0
- package/src/desk/ui/app.css +505 -0
- package/src/desk/ui/app.js +185 -0
- package/src/desk/ui/apple-icon.png +0 -0
- package/src/desk/ui/components.js +79 -0
- package/src/desk/ui/core.js +156 -0
- package/src/desk/ui/favicon.ico +0 -0
- package/src/desk/ui/icon.svg +1 -0
- package/src/desk/ui/icons.js +49 -0
- package/src/desk/ui/index.html +41 -0
- package/src/desk/ui/metrics-math.d.ts +22 -0
- package/src/desk/ui/metrics-math.js +135 -0
- package/src/desk/ui/theme.js +5 -0
- package/src/desk/ui/views-creators.js +209 -0
- package/src/desk/ui/views-keys.js +114 -0
- package/src/desk/ui/views-mail.js +188 -0
- package/src/desk/ui/views-misc.js +123 -0
- package/src/desk/ui/views-review.js +119 -0
- package/src/desk/ui/views-runs.js +350 -0
- package/src/desk/ui/views-screens.js +163 -0
- package/src/desk/ui/views-series.js +318 -0
- package/src/desk/ui/views-topics.js +87 -0
- package/src/desk/verbs.ts +361 -0
- package/src/doctor.ts +212 -0
- package/src/drain.ts +14 -0
- package/src/engage/index.ts +217 -0
- package/src/generate/index.ts +110 -0
- package/src/generate/script.ts +152 -0
- package/src/generate/title.ts +78 -0
- package/src/help.ts +233 -0
- package/src/home.ts +45 -0
- package/src/llm/client.ts +177 -0
- package/src/log.ts +7 -0
- package/src/oauth/tokens.ts +189 -0
- package/src/oauth/vault.ts +72 -0
- package/src/pipeline/review.ts +642 -0
- package/src/pipeline/run.ts +443 -0
- package/src/pipeline/series-guard.ts +26 -0
- package/src/pipeline/source-fetch.ts +69 -0
- package/src/pipeline/stages.ts +728 -0
- package/src/pipeline/topics-source.ts +90 -0
- package/src/product/commands.ts +296 -0
- package/src/product/config-file.ts +38 -0
- package/src/product/engine-choice.ts +89 -0
- package/src/product/engines.ts +103 -0
- package/src/product/insights.ts +50 -0
- package/src/product/keys.ts +67 -0
- package/src/product/paths.ts +18 -0
- package/src/product/probes.ts +88 -0
- package/src/product/push-flow.ts +90 -0
- package/src/product/render-video.ts +21 -0
- package/src/product/short.ts +62 -0
- package/src/product/update.ts +32 -0
- package/src/project/canonical.ts +14 -0
- package/src/project/commands.ts +177 -0
- package/src/project/dashboard-status.ts +28 -0
- package/src/project/discover.ts +19 -0
- package/src/project/index-file.ts +73 -0
- package/src/project/marker.ts +87 -0
- package/src/project/migrate.ts +201 -0
- package/src/project/names.ts +27 -0
- package/src/project/notice.ts +16 -0
- package/src/project/open.ts +88 -0
- package/src/project/scaffold.ts +112 -0
- package/src/project/templates.ts +42 -0
- package/src/project/videos.ts +64 -0
- package/src/publish/facebook.ts +91 -0
- package/src/publish/index.ts +12 -0
- package/src/publish/instagram.ts +97 -0
- package/src/publish/rate.ts +62 -0
- package/src/publish/session.ts +242 -0
- package/src/publish/tiktok.ts +202 -0
- package/src/publish/youtube.ts +177 -0
- package/src/queue/desk-ops.ts +1170 -0
- package/src/queue/seal.ts +3 -0
- package/src/queue/store.ts +1006 -0
- package/src/remotion/AiLabel.tsx +26 -0
- package/src/remotion/Root.tsx +73 -0
- package/src/remotion/defaults.ts +26 -0
- package/src/remotion/films/claude/ClaudeFilm.tsx +1014 -0
- package/src/remotion/films/engine/EngineMark.tsx +17 -0
- package/src/remotion/films/rea/ReaShort.tsx +552 -0
- package/src/remotion/films/rea/drawings.tsx +907 -0
- package/src/remotion/films/rea/timing.ts +144 -0
- package/src/remotion/index.ts +4 -0
- package/src/remotion/templates/EditorialSplit.tsx +39 -0
- package/src/remotion/templates/KineticType.tsx +39 -0
- package/src/remotion/templates/SignalBoard.tsx +50 -0
- package/src/remotion/timing.ts +22 -0
- package/src/render/chrome.ts +78 -0
- package/src/render/engine-mark.ts +29 -0
- package/src/render/framey-template.ts +130 -0
- package/src/render/html-engine.ts +149 -0
- package/src/render/node-crypto-stub.ts +11 -0
- package/src/render/probe.ts +29 -0
- package/src/render/props.ts +17 -0
- package/src/render/vertical-short.ts +35 -0
- package/src/render/webpack.ts +33 -0
- package/src/render/worker.ts +65 -0
- package/src/scene/assets/fonts/TitanOne-OFL.txt +94 -0
- package/src/scene/assets/fonts/titan-one.woff2 +0 -0
- package/src/scene/assets/gsap/LICENSE.txt +8 -0
- package/src/scene/assets/gsap/gsap.min.js +11 -0
- package/src/scene/audio.ts +54 -0
- package/src/scene/blocks/LICENSE +190 -0
- package/src/scene/blocks/NOTICE +5 -0
- package/src/scene/blocks/bar-chart-race/bar-chart-race.html +585 -0
- package/src/scene/blocks/beat-freeze-cut/beat-freeze-cut.html +874 -0
- package/src/scene/blocks/camcorder-hud/camcorder-hud.html +164 -0
- package/src/scene/blocks/camera-dolly-zoom/camera-dolly-zoom.html +355 -0
- package/src/scene/blocks/code-diff/code-diff.html +817 -0
- package/src/scene/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html +248 -0
- package/src/scene/blocks/code-typing/code-typing.html +764 -0
- package/src/scene/blocks/data-chart/data-chart.html +452 -0
- package/src/scene/blocks/flowchart-vertical/flowchart-vertical.html +454 -0
- package/src/scene/blocks/hw-scribble-transition/hw-scribble-transition.html +153 -0
- package/src/scene/blocks/index.json +824 -0
- package/src/scene/blocks/lt-accent-underline/lt-accent-underline.html +107 -0
- package/src/scene/blocks/lt-bold-block/lt-bold-block.html +109 -0
- package/src/scene/blocks/lt-clean-bar/lt-clean-bar.html +112 -0
- package/src/scene/blocks/lt-dark-card/lt-dark-card.html +111 -0
- package/src/scene/blocks/lt-kicker-name/lt-kicker-name.html +114 -0
- package/src/scene/blocks/lt-mask-reveal/lt-mask-reveal.html +120 -0
- package/src/scene/blocks/lt-neon-border/lt-neon-border.html +391 -0
- package/src/scene/blocks/lt-stack-bars/lt-stack-bars.html +109 -0
- package/src/scene/blocks/macos-notification/macos-notification.html +194 -0
- package/src/scene/blocks/mk-background/mk-background.html +236 -0
- package/src/scene/blocks/mk-callout-highlight/mk-callout-highlight.html +183 -0
- package/src/scene/blocks/mk-clone-wall-transition/mk-clone-wall-transition.html +226 -0
- package/src/scene/blocks/mk-line-graph/mk-line-graph.html +293 -0
- package/src/scene/blocks/mk-progress-stat/mk-progress-stat.html +186 -0
- package/src/scene/blocks/mk-specs-list/mk-specs-list.html +223 -0
- package/src/scene/blocks/news-ticker/news-ticker.html +409 -0
- package/src/scene/blocks/organic-light-leak-overlay/organic-light-leak-overlay.html +126 -0
- package/src/scene/blocks/oscilloscope-trace/oscilloscope-trace.html +519 -0
- package/src/scene/blocks/snow/snow.html +205 -0
- package/src/scene/blocks/split-flap-board/split-flap-board.html +347 -0
- package/src/scene/blocks/spotify-card/spotify-card.html +294 -0
- package/src/scene/blocks/starfield/starfield.html +188 -0
- package/src/scene/blocks/thread-message-stack/thread-message-stack.html +239 -0
- package/src/scene/blocks/week-in-merges/week-in-merges.html +470 -0
- package/src/scene/blocks/x-post/x-post.html +437 -0
- package/src/scene/blocks/yt-comment-card/yt-comment-card.html +319 -0
- package/src/scene/blocks/yt-lcd-background/yt-lcd-background.html +307 -0
- package/src/scene/blocks/yt-prism-title/yt-prism-title.html +205 -0
- package/src/scene/blocks.ts +229 -0
- package/src/scene/command.ts +272 -0
- package/src/scene/figure.ts +181 -0
- package/src/scene/glyphs.ts +107 -0
- package/src/scene/init.ts +86 -0
- package/src/scene/inspect/css-tree.d.ts +6 -0
- package/src/scene/inspect/page-scripts.ts +272 -0
- package/src/scene/inspect/report.ts +405 -0
- package/src/scene/inspect/run.ts +200 -0
- package/src/scene/inspect/source.ts +236 -0
- package/src/scene/label.ts +131 -0
- package/src/scene/net-guard.ts +74 -0
- package/src/scene/render.ts +292 -0
- package/src/scene/runtime/browser.ts +199 -0
- package/src/scene/runtime/core.ts +181 -0
- package/src/scene/runtime/react.tsx +115 -0
- package/src/scene/runtime/transitions.ts +133 -0
- package/src/scene/sanitize.ts +181 -0
- package/src/scene/scene.ts +152 -0
- package/src/scene/studio-command.ts +48 -0
- package/src/scene/studio-skin.ts +59 -0
- package/src/scene/studio.ts +337 -0
- package/src/scene/supply.ts +111 -0
- package/src/scene/timing.ts +288 -0
- package/src/scene/transitions/LICENSE +190 -0
- package/src/scene/transitions/NOTICE +5 -0
- package/src/scene/transitions/SOURCE.json +24 -0
- package/src/scene/transitions/transitions.global.js +38 -0
- package/src/scene/transitions.ts +26 -0
- package/src/service/all-projects.ts +153 -0
- package/src/service/commands.ts +256 -0
- package/src/service/due.ts +82 -0
- package/src/service/lock.ts +101 -0
- package/src/service/log-file.ts +56 -0
- package/src/service/state.ts +41 -0
- package/src/service/units.ts +90 -0
- package/src/service/worker.ts +51 -0
- package/src/skill-agents.ts +74 -0
- package/src/skill-install.ts +334 -0
- package/src/sync-write.ts +21 -0
- package/src/tick-report.ts +205 -0
- package/src/tts.ts +124 -0
- package/vendor/@vfrey/contracts/dist/account.d.ts +97 -0
- package/vendor/@vfrey/contracts/dist/account.js +27 -0
- package/vendor/@vfrey/contracts/dist/checks.d.ts +8 -0
- package/vendor/@vfrey/contracts/dist/checks.js +1 -0
- package/vendor/@vfrey/contracts/dist/credentials/crypto.d.ts +4 -0
- package/vendor/@vfrey/contracts/dist/credentials/crypto.js +38 -0
- package/vendor/@vfrey/contracts/dist/domain/advice-persona.d.ts +10 -0
- package/vendor/@vfrey/contracts/dist/domain/advice-persona.js +24 -0
- package/vendor/@vfrey/contracts/dist/domain/backoff.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/backoff.js +27 -0
- package/vendor/@vfrey/contracts/dist/domain/cadence.d.ts +29 -0
- package/vendor/@vfrey/contracts/dist/domain/cadence.js +95 -0
- package/vendor/@vfrey/contracts/dist/domain/creators.d.ts +146 -0
- package/vendor/@vfrey/contracts/dist/domain/creators.js +2 -0
- package/vendor/@vfrey/contracts/dist/domain/disclosure.d.ts +127 -0
- package/vendor/@vfrey/contracts/dist/domain/disclosure.js +250 -0
- package/vendor/@vfrey/contracts/dist/domain/errors.d.ts +34 -0
- package/vendor/@vfrey/contracts/dist/domain/errors.js +68 -0
- package/vendor/@vfrey/contracts/dist/domain/redact.d.ts +1 -0
- package/vendor/@vfrey/contracts/dist/domain/redact.js +21 -0
- package/vendor/@vfrey/contracts/dist/domain/revenue.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/revenue.js +16 -0
- package/vendor/@vfrey/contracts/dist/domain/safety.d.ts +20 -0
- package/vendor/@vfrey/contracts/dist/domain/safety.js +61 -0
- package/vendor/@vfrey/contracts/dist/domain/similarity.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/similarity.js +100 -0
- package/vendor/@vfrey/contracts/dist/domain/templates.d.ts +18 -0
- package/vendor/@vfrey/contracts/dist/domain/templates.js +67 -0
- package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.d.ts +19 -0
- package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.js +52 -0
- package/vendor/@vfrey/contracts/dist/domain/topics.d.ts +78 -0
- package/vendor/@vfrey/contracts/dist/domain/topics.js +4 -0
- package/vendor/@vfrey/contracts/dist/domain/types.d.ts +216 -0
- package/vendor/@vfrey/contracts/dist/domain/types.js +1 -0
- package/vendor/@vfrey/contracts/dist/domain/variation.d.ts +57 -0
- package/vendor/@vfrey/contracts/dist/domain/variation.js +278 -0
- package/vendor/@vfrey/contracts/dist/events.d.ts +20 -0
- package/vendor/@vfrey/contracts/dist/events.js +1 -0
- package/vendor/@vfrey/contracts/dist/http.d.ts +11 -0
- package/vendor/@vfrey/contracts/dist/http.js +37 -0
- package/vendor/@vfrey/contracts/dist/index.d.ts +22 -0
- package/vendor/@vfrey/contracts/dist/index.js +22 -0
- package/vendor/@vfrey/contracts/dist/log.d.ts +2 -0
- package/vendor/@vfrey/contracts/dist/log.js +10 -0
- package/vendor/@vfrey/contracts/dist/mail.d.ts +55 -0
- package/vendor/@vfrey/contracts/dist/mail.js +1 -0
- package/vendor/@vfrey/contracts/package.json +18 -0
- package/vendor/@vfrey/ingest/dist/index.d.ts +41 -0
- package/vendor/@vfrey/ingest/dist/index.js +221 -0
- package/vendor/@vfrey/ingest/dist/parse.d.ts +5 -0
- package/vendor/@vfrey/ingest/dist/parse.js +165 -0
- package/vendor/@vfrey/ingest/package.json +21 -0
- package/vendor/framey/LICENSE +190 -0
- package/vendor/framey/NOTICE +15 -0
- package/vendor/framey/SOURCE.json +24 -0
- package/vendor/framey/STUDIO-SOURCE.json +9 -0
- package/vendor/framey/bin/framey-localize-fonts.mjs +12 -0
- package/vendor/framey/bin/framey.mjs +16 -0
- package/vendor/framey/dist/add-VMJD5KBS.js +75 -0
- package/vendor/framey/dist/animatedGifPrep-QPKGJ6PM.js +43 -0
- package/vendor/framey/dist/api.d.ts +211 -0
- package/vendor/framey/dist/api.js +76 -0
- package/vendor/framey/dist/appHistory-WW56FZTT.js +31 -0
- package/vendor/framey/dist/asset-WQKGOELE.js +26 -0
- package/vendor/framey/dist/audio-RSJWVWRA.js +1936 -0
- package/vendor/framey/dist/batchRender-3HPICDCT.js +366 -0
- package/vendor/framey/dist/beats-HL2BDUZI.js +273 -0
- package/vendor/framey/dist/benchmark-RWCWHI5Z.js +230 -0
- package/vendor/framey/dist/browser-ECYET3Q2.js +206 -0
- package/vendor/framey/dist/bundleWithLocalizedFonts-ALYBQEQT.js +52 -0
- package/vendor/framey/dist/captureCompositionFrame-4DA6SEH2.js +94 -0
- package/vendor/framey/dist/catalog-5JVDEUWK.js +979 -0
- package/vendor/framey/dist/catch-up-NEBCL5NO.js +106 -0
- package/vendor/framey/dist/check-F5N544ML.js +111 -0
- package/vendor/framey/dist/checkBrowser-LZ66CMJG.js +110 -0
- package/vendor/framey/dist/chunk-2K7Z2HVP.js +372 -0
- package/vendor/framey/dist/chunk-2UZQHDM7.js +323 -0
- package/vendor/framey/dist/chunk-2X4PQEYM.js +34 -0
- package/vendor/framey/dist/chunk-3422KMTD.js +168 -0
- package/vendor/framey/dist/chunk-3FYD4YEK.js +72 -0
- package/vendor/framey/dist/chunk-3GXQPCJC.js +682 -0
- package/vendor/framey/dist/chunk-3LM6YPBR.js +179 -0
- package/vendor/framey/dist/chunk-442JNXRM.js +20 -0
- package/vendor/framey/dist/chunk-4BNN5FGX.js +182 -0
- package/vendor/framey/dist/chunk-4P7NX5BM.js +11532 -0
- package/vendor/framey/dist/chunk-4UMPIPCQ.js +88 -0
- package/vendor/framey/dist/chunk-4YYJOFKS.js +28 -0
- package/vendor/framey/dist/chunk-54HJ63Q2.js +29 -0
- package/vendor/framey/dist/chunk-5ETJX6WM.js +89 -0
- package/vendor/framey/dist/chunk-5SG54JRS.js +68 -0
- package/vendor/framey/dist/chunk-634HSJ3J.js +58 -0
- package/vendor/framey/dist/chunk-66LREXGS.js +48 -0
- package/vendor/framey/dist/chunk-6BEFEWFB.js +25 -0
- package/vendor/framey/dist/chunk-6BNTGNGD.js +286 -0
- package/vendor/framey/dist/chunk-6DDPOHMK.js +140 -0
- package/vendor/framey/dist/chunk-6GV5XSOR.js +214 -0
- package/vendor/framey/dist/chunk-6JR2XUEE.js +127 -0
- package/vendor/framey/dist/chunk-73QVATIM.js +362 -0
- package/vendor/framey/dist/chunk-7FMUTGOH.js +279 -0
- package/vendor/framey/dist/chunk-7JQFJ6JJ.js +291 -0
- package/vendor/framey/dist/chunk-AHPGAESX.js +113 -0
- package/vendor/framey/dist/chunk-AHX34EFP.js +15 -0
- package/vendor/framey/dist/chunk-AJ5CSAEZ.js +487 -0
- package/vendor/framey/dist/chunk-B7RJV74O.js +5047 -0
- package/vendor/framey/dist/chunk-BL4U6U52.js +254 -0
- package/vendor/framey/dist/chunk-BSQ7XANM.js +210 -0
- package/vendor/framey/dist/chunk-BZ56MTDX.js +6 -0
- package/vendor/framey/dist/chunk-C5BO7PMT.js +282 -0
- package/vendor/framey/dist/chunk-CFUCHO4T.js +358 -0
- package/vendor/framey/dist/chunk-CZGHFATT.js +31 -0
- package/vendor/framey/dist/chunk-DLA44CMS.js +5649 -0
- package/vendor/framey/dist/chunk-DPVXBVLL.js +31292 -0
- package/vendor/framey/dist/chunk-DQBY7FRR.js +397 -0
- package/vendor/framey/dist/chunk-EAMLBAHA.js +154 -0
- package/vendor/framey/dist/chunk-EOVYQ3IG.js +143 -0
- package/vendor/framey/dist/chunk-EVMY7IPL.js +612 -0
- package/vendor/framey/dist/chunk-FG4BBFZI.js +122 -0
- package/vendor/framey/dist/chunk-FI4I7CME.js +56 -0
- package/vendor/framey/dist/chunk-FLREUEHW.js +2081 -0
- package/vendor/framey/dist/chunk-FUFP7A7S.js +1126 -0
- package/vendor/framey/dist/chunk-FV3PIKGH.js +56 -0
- package/vendor/framey/dist/chunk-FXMKHHPO.js +123 -0
- package/vendor/framey/dist/chunk-GGZAM5VH.js +1187 -0
- package/vendor/framey/dist/chunk-GKZEVJIB.js +39 -0
- package/vendor/framey/dist/chunk-GMECXFAN.js +57 -0
- package/vendor/framey/dist/chunk-GNEFDLFB.js +621 -0
- package/vendor/framey/dist/chunk-GPMLSOEG.js +19274 -0
- package/vendor/framey/dist/chunk-GPYVT5EE.js +59 -0
- package/vendor/framey/dist/chunk-H6IEMZEV.js +40 -0
- package/vendor/framey/dist/chunk-HEFQCIXR.js +170 -0
- package/vendor/framey/dist/chunk-HFQ4CWHA.js +118 -0
- package/vendor/framey/dist/chunk-HPDTCOKE.js +304 -0
- package/vendor/framey/dist/chunk-HTUHQKDB.js +548 -0
- package/vendor/framey/dist/chunk-HU2SPFNG.js +103 -0
- package/vendor/framey/dist/chunk-HVW7ABXD.js +19 -0
- package/vendor/framey/dist/chunk-IG7NDKXO.js +26 -0
- package/vendor/framey/dist/chunk-II3J3WXK.js +246 -0
- package/vendor/framey/dist/chunk-IR35BCLK.js +13 -0
- package/vendor/framey/dist/chunk-ISN5GRYI.js +20 -0
- package/vendor/framey/dist/chunk-IUONB55J.js +669 -0
- package/vendor/framey/dist/chunk-IVSUQR2B.js +161 -0
- package/vendor/framey/dist/chunk-IXU6PQSW.js +259 -0
- package/vendor/framey/dist/chunk-IZCW2NKT.js +29 -0
- package/vendor/framey/dist/chunk-JGXJU5PF.js +68 -0
- package/vendor/framey/dist/chunk-JHOCK3DG.js +27 -0
- package/vendor/framey/dist/chunk-JVBRJMOP.js +121 -0
- package/vendor/framey/dist/chunk-K4MJX7SR.js +17 -0
- package/vendor/framey/dist/chunk-K4WKGXRC.js +18 -0
- package/vendor/framey/dist/chunk-KGPLIH7B.js +127 -0
- package/vendor/framey/dist/chunk-KLIKCX6Q.js +63 -0
- package/vendor/framey/dist/chunk-KXTKKBOC.js +61 -0
- package/vendor/framey/dist/chunk-KZI44OPA.js +185 -0
- package/vendor/framey/dist/chunk-LEKZNRKI.js +45 -0
- package/vendor/framey/dist/chunk-LFGJWOVA.js +61 -0
- package/vendor/framey/dist/chunk-LLMSX6V6.js +17 -0
- package/vendor/framey/dist/chunk-LTGTN2TH.js +195 -0
- package/vendor/framey/dist/chunk-M2WBOQ2Q.js +54 -0
- package/vendor/framey/dist/chunk-MI5FTLYK.js +399 -0
- package/vendor/framey/dist/chunk-MLQP2QUF.js +1809 -0
- package/vendor/framey/dist/chunk-MMQ52NIZ.js +956 -0
- package/vendor/framey/dist/chunk-ND46XKK3.js +742 -0
- package/vendor/framey/dist/chunk-NYEXNSHM.js +504 -0
- package/vendor/framey/dist/chunk-O4YRB27L.js +57 -0
- package/vendor/framey/dist/chunk-O7BGSQ5F.js +92 -0
- package/vendor/framey/dist/chunk-OFQWTKPW.js +117 -0
- package/vendor/framey/dist/chunk-P5ZBLXJ6.js +91 -0
- package/vendor/framey/dist/chunk-P6LJ4Y32.js +4662 -0
- package/vendor/framey/dist/chunk-PKZ7RK5N.js +281 -0
- package/vendor/framey/dist/chunk-PURHAXFL.js +15 -0
- package/vendor/framey/dist/chunk-Q5X2B5WT.js +29 -0
- package/vendor/framey/dist/chunk-QAMKOG67.js +248 -0
- package/vendor/framey/dist/chunk-R6YBK4XX.js +514 -0
- package/vendor/framey/dist/chunk-RGLQURIX.js +2997 -0
- package/vendor/framey/dist/chunk-RNV3DM4S.js +3573 -0
- package/vendor/framey/dist/chunk-ROEW726S.js +4638 -0
- package/vendor/framey/dist/chunk-RQVDTPGD.js +6 -0
- package/vendor/framey/dist/chunk-RVZHGOGR.js +26 -0
- package/vendor/framey/dist/chunk-RXATE6FZ.js +385 -0
- package/vendor/framey/dist/chunk-S3S4TGEH.js +424 -0
- package/vendor/framey/dist/chunk-SDYDNHEE.js +11202 -0
- package/vendor/framey/dist/chunk-SSWBU67I.js +173 -0
- package/vendor/framey/dist/chunk-ST35C4K6.js +28 -0
- package/vendor/framey/dist/chunk-SZNTYTIV.js +990 -0
- package/vendor/framey/dist/chunk-T2EUWCJE.js +29 -0
- package/vendor/framey/dist/chunk-T6AGUTZJ.js +444 -0
- package/vendor/framey/dist/chunk-THJALHFJ.js +11131 -0
- package/vendor/framey/dist/chunk-TIQB63MX.js +18128 -0
- package/vendor/framey/dist/chunk-U242RTCT.js +134 -0
- package/vendor/framey/dist/chunk-UJZREAV6.js +3901 -0
- package/vendor/framey/dist/chunk-UMD3V7CU.js +264 -0
- package/vendor/framey/dist/chunk-UOFAE5RF.js +42 -0
- package/vendor/framey/dist/chunk-UTEIUGAZ.js +29 -0
- package/vendor/framey/dist/chunk-VAS6HQX3.js +138 -0
- package/vendor/framey/dist/chunk-VC76R6W2.js +27 -0
- package/vendor/framey/dist/chunk-VFZ7W5W7.js +226 -0
- package/vendor/framey/dist/chunk-VHNLZAE2.js +348 -0
- package/vendor/framey/dist/chunk-VMC4WSH5.js +278 -0
- package/vendor/framey/dist/chunk-VORQIH64.js +108 -0
- package/vendor/framey/dist/chunk-VTXFRIHO.js +186 -0
- package/vendor/framey/dist/chunk-W2LGJSEN.js +182 -0
- package/vendor/framey/dist/chunk-WKBM7NDG.js +32 -0
- package/vendor/framey/dist/chunk-WMQNIQDJ.js +2057 -0
- package/vendor/framey/dist/chunk-WN4XZC5F.js +504 -0
- package/vendor/framey/dist/chunk-WQ6ZXHFQ.js +133 -0
- package/vendor/framey/dist/chunk-X7HBSTCZ.js +53 -0
- package/vendor/framey/dist/chunk-X7MTZ4T2.js +6 -0
- package/vendor/framey/dist/chunk-XPTOJZJF.js +308 -0
- package/vendor/framey/dist/chunk-XT6MXC7A.js +6 -0
- package/vendor/framey/dist/chunk-XT7X6QCP.js +6 -0
- package/vendor/framey/dist/chunk-XTMDWNEC.js +2820 -0
- package/vendor/framey/dist/chunk-XUHOIDKG.js +6 -0
- package/vendor/framey/dist/chunk-XX7LTW5Y.js +138 -0
- package/vendor/framey/dist/chunk-Y3YGTZEG.js +382 -0
- package/vendor/framey/dist/chunk-Y5SBSGN7.js +346 -0
- package/vendor/framey/dist/chunk-YCMVI4MZ.js +625 -0
- package/vendor/framey/dist/chunk-YOR4CTCE.js +261 -0
- package/vendor/framey/dist/chunk-YPRBGITP.js +2748 -0
- package/vendor/framey/dist/chunk-ZKDMAOFU.js +32 -0
- package/vendor/framey/dist/chunk-ZW6DURPF.js +1318 -0
- package/vendor/framey/dist/clean-ZJOGDKDM.js +336 -0
- package/vendor/framey/dist/cli.js +439 -0
- package/vendor/framey/dist/cloudrun-HGJ3B6ZV.js +767 -0
- package/vendor/framey/dist/commands/contrast-audit.browser.js +643 -0
- package/vendor/framey/dist/commands/layout-audit.browser.js +2914 -0
- package/vendor/framey/dist/commands/motion-sample.browser.js +74 -0
- package/vendor/framey/dist/commands/motion-signature.browser.js +475 -0
- package/vendor/framey/dist/compare-W7UY3BAZ.js +463 -0
- package/vendor/framey/dist/compiler-TM3W6GYF.js +136 -0
- package/vendor/framey/dist/component-QF5VGYMU.js +176 -0
- package/vendor/framey/dist/compositions-XT3PMNDF.js +238 -0
- package/vendor/framey/dist/contactSheet-7W6GYHBQ.js +24 -0
- package/vendor/framey/dist/deploy-6FJPWKF2.js +299 -0
- package/vendor/framey/dist/destroy-HE2RIPKZ.js +57 -0
- package/vendor/framey/dist/deterministicFonts-RADD3BSX.js +50 -0
- package/vendor/framey/dist/dist-4S2FGGVY.js +422 -0
- package/vendor/framey/dist/dist-BE4B2EZE.js +32 -0
- package/vendor/framey/dist/dist-EWXJGN63.js +129 -0
- package/vendor/framey/dist/docs-VKIRFEEX.js +131 -0
- package/vendor/framey/dist/doctor-2UNJ2G7U.js +74 -0
- package/vendor/framey/dist/dom-37K6YOH4.js +14 -0
- package/vendor/framey/dist/dotEnv-7PZQTVIM.js +54 -0
- package/vendor/framey/dist/events-ACY3P2PA.js +76 -0
- package/vendor/framey/dist/events-IHB3B2MK.js +69 -0
- package/vendor/framey/dist/ffmpeg-DR5LGGSK.js +63 -0
- package/vendor/framey/dist/figma-BODVUPHB.js +63 -0
- package/vendor/framey/dist/fontData.generated-NXAOI4GH.js +13 -0
- package/vendor/framey/dist/fontLocalizeCli.js +142 -0
- package/vendor/framey/dist/gpuPolicy-XLEXORZY.js +49 -0
- package/vendor/framey/dist/grade-compare-GAXFQ74C.js +661 -0
- package/vendor/framey/dist/gsapParser-IEUSLYDJ.js +2885 -0
- package/vendor/framey/dist/gsapParserAcorn-LZJKCFQI.js +30 -0
- package/vendor/framey/dist/help-VCB5X2HS.js +184 -0
- package/vendor/framey/dist/history-ENGZEVFT.js +471 -0
- package/vendor/framey/dist/hyperframe.manifest.json +22 -0
- package/vendor/framey/dist/hyperframe.runtime.iife.js +1132 -0
- package/vendor/framey/dist/hyperframes-player.global.js +478 -0
- package/vendor/framey/dist/hyperframes-slideshow.global.js +148 -0
- package/vendor/framey/dist/info-WPCJHBMU.js +182 -0
- package/vendor/framey/dist/init-G4WGLYGD.js +1176 -0
- package/vendor/framey/dist/inspect-5IQ6CNEQ.js +106 -0
- package/vendor/framey/dist/installer-ML4HWUV7.js +40 -0
- package/vendor/framey/dist/keyframes-Q6XJO7VW.js +2755 -0
- package/vendor/framey/dist/lambda-A7JC74JF.js +468 -0
- package/vendor/framey/dist/layout-YET6XNXA.js +95 -0
- package/vendor/framey/dist/lint-I7FZN2VJ.js +162 -0
- package/vendor/framey/dist/lintProject-PDB3UN72.js +30 -0
- package/vendor/framey/dist/manager-CD6LJ7ZN.js +45 -0
- package/vendor/framey/dist/manager-WA4F2GST.js +67 -0
- package/vendor/framey/dist/media-treatment-BXYS4WVA.js +1030 -0
- package/vendor/framey/dist/media-use-LWH3MANK.js +113 -0
- package/vendor/framey/dist/models-K363FYM4.js +195 -0
- package/vendor/framey/dist/motionShot-GPAVWXLH.js +640 -0
- package/vendor/framey/dist/normalize-FASIPBUM.js +24 -0
- package/vendor/framey/dist/normalize-audio-VDWYRHN6.js +268 -0
- package/vendor/framey/dist/open-E3TB6P6J.js +128 -0
- package/vendor/framey/dist/parakeetRunner-UP34UOI2.js +58 -0
- package/vendor/framey/dist/pipeline-VXIUYEBU.js +64 -0
- package/vendor/framey/dist/play-BUTDQ4DV.js +353 -0
- package/vendor/framey/dist/policies-OLDYGFK5.js +309 -0
- package/vendor/framey/dist/preflight-2KZ2GWAZ.js +55 -0
- package/vendor/framey/dist/present-GXYSM7NX.js +353 -0
- package/vendor/framey/dist/preview-EKZ33JOJ.js +1910 -0
- package/vendor/framey/dist/producer-SKU5EV3J.js +13 -0
- package/vendor/framey/dist/progress-ZWB4Z4ET.js +76 -0
- package/vendor/framey/dist/projectConfig-3MZAYVTS.js +60 -0
- package/vendor/framey/dist/publication-DTKWNF74.js +33 -0
- package/vendor/framey/dist/remove-background-OY5L7XN2.js +265 -0
- package/vendor/framey/dist/render-CDV4GDFX.js +187 -0
- package/vendor/framey/dist/render-YE5YAMOD.js +2620 -0
- package/vendor/framey/dist/render-batch-ZAMFPCLL.js +274 -0
- package/vendor/framey/dist/renderSetupWorker.js +120 -0
- package/vendor/framey/dist/resolver-D5BIPSEW.js +42 -0
- package/vendor/framey/dist/runtimeVersion.js +18 -0
- package/vendor/framey/dist/sam-WFZXI4F7.js +23 -0
- package/vendor/framey/dist/shaderTransitionWorker.js +44 -0
- package/vendor/framey/dist/sherpa-RQWLV7I3.js +79 -0
- package/vendor/framey/dist/sherpaWorker.js +84 -0
- package/vendor/framey/dist/sites-AMOVABTK.js +54 -0
- package/vendor/framey/dist/skill-ZHVLH7KX.js +15 -0
- package/vendor/framey/dist/slideshow-SJGK4RQS.js +24 -0
- package/vendor/framey/dist/snapshot-TUXL6LBQ.js +1515 -0
- package/vendor/framey/dist/src-JHQGB2S6.js +215 -0
- package/vendor/framey/dist/src-Z5PGEMXB.js +532 -0
- package/vendor/framey/dist/state-B6H3WHCF.js +28 -0
- package/vendor/framey/dist/staticProjectServer-DFVZOUGI.js +63 -0
- package/vendor/framey/dist/studio/assets/hyperframes-player-DLaKjw0L.js +478 -0
- package/vendor/framey/dist/studio/assets/index-BX3KHhGX.js +71 -0
- package/vendor/framey/dist/studio/assets/index-CYbvOVp-.css +1 -0
- package/vendor/framey/dist/studio/assets/index-Dp3QEUGL.js +402 -0
- package/vendor/framey/dist/studio/assets/index-qrTAXbqc.js +531 -0
- package/vendor/framey/dist/studio/favicon.svg +14 -0
- package/vendor/framey/dist/studio/icons/timeline/audio.svg +7 -0
- package/vendor/framey/dist/studio/icons/timeline/captions.svg +5 -0
- package/vendor/framey/dist/studio/icons/timeline/composition.svg +12 -0
- package/vendor/framey/dist/studio/icons/timeline/image.svg +18 -0
- package/vendor/framey/dist/studio/icons/timeline/music.svg +10 -0
- package/vendor/framey/dist/studio/icons/timeline/text.svg +3 -0
- package/vendor/framey/dist/studio/index.html +23 -0
- package/vendor/framey/dist/studio-api-2NECGRMB.js +179 -0
- package/vendor/framey/dist/studioSelectionClient-IUUPKUHZ.js +25 -0
- package/vendor/framey/dist/studioServer-N2OPUMOQ.js +1569 -0
- package/vendor/framey/dist/synthesize-PKTB5ATG.js +183 -0
- package/vendor/framey/dist/system-7F3UEHQT.js +27 -0
- package/vendor/framey/dist/telemetry-DQGPEKM6.js +145 -0
- package/vendor/framey/dist/telemetry-UN55N6U6.js +77 -0
- package/vendor/framey/dist/timeline-LHUUMK63.js +1295 -0
- package/vendor/framey/dist/tokens-OLECWDFK.js +92 -0
- package/vendor/framey/dist/transcribe-O7HQKMIV.js +331 -0
- package/vendor/framey/dist/transcribe-UJESPBT5.js +76 -0
- package/vendor/framey/dist/tts-YCCQHZD4.js +234 -0
- package/vendor/framey/dist/urlDownloader-H2LXLG4U.js +48 -0
- package/vendor/framey/dist/usage-HG4WMM7A.js +2167 -0
- package/vendor/framey/dist/validate-F2XMN6EV.js +87 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/GSAP-LICENSE.txt +8 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/CustomEase.min.js +11 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js +11 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/gsap.min.js +11 -0
- package/vendor/framey/dist/warmCheck.js +220 -0
- package/vendor/framey/dist/warmCheckClient.js +139 -0
- package/vendor/framey/dist/workspaceBuildCheck-XYD2YTFW.js +133 -0
- package/vendor/framey-player/LICENSE +190 -0
- package/vendor/framey-player/NOTICE +16 -0
- package/vendor/framey-player/SOURCE.json +6 -0
- package/vendor/framey-player/hyperframes-player.global.js +478 -0
- package/vendor/framey.pin +1 -0
- package/bin/index.js +0 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Entry: router, poller, shell chrome. Plain ES modules, no build step.
|
|
2
|
+
import { html, icon, setHtml, $, $$, store, read, toast, copyText, clock, dur, ms} from "./core.js";
|
|
3
|
+
import { boardView, runsView, runView } from "./views-runs.js";
|
|
4
|
+
import { seriesListView, seriesDetailView } from "./views-series.js";
|
|
5
|
+
import { keysView } from "./views-keys.js";
|
|
6
|
+
import { topicsView } from "./views-topics.js";
|
|
7
|
+
import { creatorsView } from "./views-creators.js";
|
|
8
|
+
import { mailView } from "./views-mail.js";
|
|
9
|
+
import { reviewView } from "./views-review.js";
|
|
10
|
+
import { publicationsView, sourcesView, engageView, trendsView } from "./views-screens.js";
|
|
11
|
+
import { metricsView, warningsView, settingsView, notFoundView } from "./views-misc.js";
|
|
12
|
+
|
|
13
|
+
const NAV = [
|
|
14
|
+
["/", "Board", "board"], ["/runs", "Runs", "runs"], ["/series", "Series", "series"], ["/topics", "Topics", "topics"], ["/creators", "Creators", "creators"], ["/mail", "Mail", "mail"], ["/publications", "Publications", "publications"], ["/sources", "Sources", "sources"], ["/engage", "Engage", "engage"], ["/trends", "Trends", "trends"], ["/keys", "Keys", "keys"],
|
|
15
|
+
["/review", "Review", "review"],
|
|
16
|
+
["/metrics", "Metrics", "metrics"], ["/warnings", "Warnings", "warnings"], ["/settings", "Settings", "settings"],
|
|
17
|
+
];
|
|
18
|
+
const ROUTES = [
|
|
19
|
+
[/^\/review$/, reviewView],
|
|
20
|
+
[/^\/$/, boardView], [/^\/runs$/, runsView], [/^\/runs\/([A-Za-z0-9-]+)$/, runView, ["id"]],
|
|
21
|
+
[/^\/series$/, seriesListView], [/^\/series\/([^/]+)$/, seriesDetailView, ["id"]], [/^\/topics$/, topicsView], [/^\/creators$/, creatorsView], [/^\/mail$/, mailView],
|
|
22
|
+
[/^\/publications$/, publicationsView], [/^\/sources$/, sourcesView], [/^\/engage$/, engageView], [/^\/trends$/, trendsView],
|
|
23
|
+
[/^\/keys$/, keysView], [/^\/metrics$/, metricsView], [/^\/warnings$/, warningsView], [/^\/settings$/, settingsView],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const main = document.getElementById("main");
|
|
27
|
+
let view = null;
|
|
28
|
+
let timer = null;
|
|
29
|
+
let inFlight = false;
|
|
30
|
+
let prevTickRunning = null;
|
|
31
|
+
|
|
32
|
+
// ---------- chrome ----------
|
|
33
|
+
function drawNav() {
|
|
34
|
+
const path = location.pathname.replace(/\/+$/, "") || "/";
|
|
35
|
+
const unacked = (store.state?.warnings || []).filter((w) => !w.ackedAt).length;
|
|
36
|
+
const waiting = (store.state?.jobs || []).filter((j) => j.status === "awaiting_review").length;
|
|
37
|
+
setHtml(document.getElementById("nav"), html`${NAV.map(([href, label, ic]) => {
|
|
38
|
+
const active = href === "/" ? path === "/" : path === href || path.startsWith(href + "/");
|
|
39
|
+
return html`<a href="${href}" data-link ${active ? html`aria-current="page"` : ""}>${icon(ic)}<span>${label}</span>${href === "/review" ? html`<span class="count" data-review-count ${waiting ? "" : "hidden"} aria-label="${waiting} waiting for review">${waiting}</span>` : ""}${href === "/warnings" ? html`<span class="count" ${unacked ? "" : "hidden"} aria-label="${unacked} unacknowledged">${unacked}</span>` : ""}</a>`;
|
|
40
|
+
})}`);
|
|
41
|
+
}
|
|
42
|
+
function drawLive() {
|
|
43
|
+
const live = document.getElementById("live");
|
|
44
|
+
const mini = document.querySelector("[data-live-mini]");
|
|
45
|
+
live.dataset.live = store.link; if (mini) mini.dataset.live = store.link;
|
|
46
|
+
const t = store.lastGood ? new Date(store.lastGood).toLocaleTimeString([], { hour12: false }) : "";
|
|
47
|
+
const text = store.link === "live" ? "Live" : store.link === "refreshing" ? "Refreshing" : store.link === "offline" ? `Offline${t ? ` · last sync ${t}` : ""}` : "Connecting";
|
|
48
|
+
live.querySelector(".live-text").textContent = text;
|
|
49
|
+
live.title = store.link === "offline" ? "Dashboard server is not reachable" : "Polling the dashboard server";
|
|
50
|
+
const st = store.state;
|
|
51
|
+
const envEl = document.getElementById("env");
|
|
52
|
+
const projectName = st ? (st.project && st.project.name ? st.project.name : "Machine desk") : "";
|
|
53
|
+
for (const el of document.querySelectorAll("[data-project-name]")) { el.textContent = projectName; el.title = projectName; }
|
|
54
|
+
if (!st) { envEl.textContent = ""; return; }
|
|
55
|
+
const scripts = st.mode === "api" ? "Scripts: written by the configured model" : "Scripts: written by your agent";
|
|
56
|
+
const lines = [`Local desk · ${st.homeDisplay || st.home || ""}`, scripts];
|
|
57
|
+
if (st.appEnv === "production") lines.push("Production");
|
|
58
|
+
envEl.replaceChildren(...lines.map((text) => { const d = document.createElement("div"); d.textContent = text; return d; }));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------- routing ----------
|
|
62
|
+
function match(path) {
|
|
63
|
+
for (const [re, factory, names] of ROUTES) {
|
|
64
|
+
const m = re.exec(path);
|
|
65
|
+
if (m) return { factory, params: Object.fromEntries((names || []).map((n, i) => [n, decodeURIComponent(m[i + 1])])) };
|
|
66
|
+
}
|
|
67
|
+
return { factory: notFoundView, params: {} };
|
|
68
|
+
}
|
|
69
|
+
function mountRoute() {
|
|
70
|
+
const path = location.pathname.replace(/(.)\/+$/, "$1");
|
|
71
|
+
const { factory, params } = match(path);
|
|
72
|
+
view = factory(params);
|
|
73
|
+
const el = document.createElement("div");
|
|
74
|
+
el.className = "view";
|
|
75
|
+
main.replaceChildren(el);
|
|
76
|
+
view.mount(el, params, ctx);
|
|
77
|
+
document.title = `${view.title} · vfrey desk`;
|
|
78
|
+
drawNav();
|
|
79
|
+
if (store.state) view.update(store.state);
|
|
80
|
+
document.body.classList.remove("menu-open");
|
|
81
|
+
document.getElementById("menu-btn").setAttribute("aria-expanded", "false");
|
|
82
|
+
}
|
|
83
|
+
export function navigate(path) {
|
|
84
|
+
if (path === location.pathname + location.search) return;
|
|
85
|
+
history.pushState({}, "", path);
|
|
86
|
+
mountRoute();
|
|
87
|
+
window.scrollTo(0, 0);
|
|
88
|
+
main.focus({ preventScroll: true });
|
|
89
|
+
}
|
|
90
|
+
const ctx = { navigate };
|
|
91
|
+
|
|
92
|
+
document.addEventListener("click", (e) => {
|
|
93
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
94
|
+
const copy = e.target.closest("[data-copy]");
|
|
95
|
+
if (copy) {
|
|
96
|
+
copyText(copy.dataset.copy).then((ok) => {
|
|
97
|
+
const old = copy.innerHTML; copy.innerHTML = icon("check").toString();
|
|
98
|
+
toast(ok ? "Copied" : "Copy failed", { kind: ok ? "info" : "error", ms: 1800 });
|
|
99
|
+
setTimeout(() => { copy.innerHTML = old; }, 1200);
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const a = e.target.closest("a[data-link]");
|
|
104
|
+
if (!a || a.target === "_blank") return;
|
|
105
|
+
const url = new URL(a.href, location.href);
|
|
106
|
+
if (url.origin !== location.origin) return;
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
navigate(url.pathname + url.search);
|
|
109
|
+
});
|
|
110
|
+
window.addEventListener("popstate", mountRoute);
|
|
111
|
+
|
|
112
|
+
document.getElementById("menu-btn").innerHTML = icon("menu").toString();
|
|
113
|
+
document.getElementById("menu-btn").addEventListener("click", () => {
|
|
114
|
+
const open = document.body.classList.toggle("menu-open");
|
|
115
|
+
document.getElementById("menu-btn").setAttribute("aria-expanded", String(open));
|
|
116
|
+
});
|
|
117
|
+
document.getElementById("scrim").addEventListener("click", () => { document.body.classList.remove("menu-open"); document.getElementById("menu-btn").setAttribute("aria-expanded", "false"); });
|
|
118
|
+
document.addEventListener("keydown", (e) => { if (e.key === "Escape") document.body.classList.remove("menu-open"); });
|
|
119
|
+
|
|
120
|
+
// ---------- theme ----------
|
|
121
|
+
const themeBtn = document.getElementById("theme-btn");
|
|
122
|
+
const systemDark = () => matchMedia("(prefers-color-scheme: dark)").matches;
|
|
123
|
+
const currentTheme = () => document.documentElement.dataset.theme || (systemDark() ? "dark" : "light");
|
|
124
|
+
function drawTheme() {
|
|
125
|
+
const dark = currentTheme() === "dark";
|
|
126
|
+
themeBtn.querySelector(".theme-ic").innerHTML = icon(dark ? "moon" : "sun").toString();
|
|
127
|
+
themeBtn.querySelector(".theme-text").textContent = dark ? "Dark" : "Light";
|
|
128
|
+
}
|
|
129
|
+
themeBtn.addEventListener("click", () => {
|
|
130
|
+
const next = currentTheme() === "dark" ? "light" : "dark";
|
|
131
|
+
document.documentElement.dataset.theme = next;
|
|
132
|
+
try { localStorage.setItem("vfrey-desk-theme", next); } catch { /* ignore */ }
|
|
133
|
+
drawTheme();
|
|
134
|
+
});
|
|
135
|
+
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", drawTheme);
|
|
136
|
+
drawTheme();
|
|
137
|
+
|
|
138
|
+
// ---------- polling ----------
|
|
139
|
+
const busyJobs = (s) => s?.tick?.running || (s?.jobs || []).some((j) => j.status === "leased");
|
|
140
|
+
async function poll() {
|
|
141
|
+
if (inFlight) return;
|
|
142
|
+
inFlight = true;
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
if (store.state) { store.link = "refreshing"; drawLive(); }
|
|
145
|
+
try {
|
|
146
|
+
const s = await read("/api/state");
|
|
147
|
+
if (!s.ok) throw new Error("state");
|
|
148
|
+
store.state = s; store.lastGood = Date.now(); store.link = "live";
|
|
149
|
+
afterState(s);
|
|
150
|
+
} catch {
|
|
151
|
+
store.link = "offline";
|
|
152
|
+
}
|
|
153
|
+
inFlight = false;
|
|
154
|
+
drawLive();
|
|
155
|
+
schedule();
|
|
156
|
+
}
|
|
157
|
+
function afterState(s) {
|
|
158
|
+
const running = !!s.tick?.running;
|
|
159
|
+
if (prevTickRunning === true && !running) {
|
|
160
|
+
if (s.tick.error) toast("Tick failed", { kind: "error", detail: s.tick.error });
|
|
161
|
+
else toast(`Tick finished`, { detail: `${s.tick.processed ?? 0} job${s.tick.processed === 1 ? "" : "s"} processed` });
|
|
162
|
+
}
|
|
163
|
+
prevTickRunning = running;
|
|
164
|
+
drawNav();
|
|
165
|
+
view?.update(s);
|
|
166
|
+
}
|
|
167
|
+
function schedule() {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
if (document.hidden) return;
|
|
170
|
+
timer = setTimeout(poll, busyJobs(store.state) ? 2000 : 5000);
|
|
171
|
+
}
|
|
172
|
+
document.addEventListener("visibilitychange", () => { if (document.hidden) clearTimeout(timer); else poll(); });
|
|
173
|
+
window.addEventListener("focus", () => { if (!inFlight) poll(); });
|
|
174
|
+
store.refresh = () => { clearTimeout(timer); return poll(); };
|
|
175
|
+
|
|
176
|
+
// 1 s ticker for elapsed labels that sit between polls
|
|
177
|
+
setInterval(() => {
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
for (const el of $$(document, "[data-tick-since]")) el.textContent = clock(now - ms(el.dataset.tickSince));
|
|
180
|
+
for (const el of $$(document, "[data-dur-since]")) el.textContent = dur(now - ms(el.dataset.durSince));
|
|
181
|
+
}, 1000);
|
|
182
|
+
|
|
183
|
+
drawNav(); drawLive();
|
|
184
|
+
mountRoute();
|
|
185
|
+
poll();
|
|
Binary file
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Reusable fragments. Everything returns html`` (escaped) values.
|
|
2
|
+
import { html, raw, icon, STATUS, STAGES, stageInfo, cap, ms, rel, abs } from "./core.js";
|
|
3
|
+
|
|
4
|
+
export const PLATFORMS = { youtube: "YouTube", tiktok: "TikTok", instagram: "Instagram", facebook: "Facebook" };
|
|
5
|
+
export const platformName = (p) => PLATFORMS[p] || cap(p);
|
|
6
|
+
|
|
7
|
+
export function statusPill(status, { iconic = false } = {}) {
|
|
8
|
+
const s = STATUS[status] || { label: status, tone: "wait", ic: "clock" };
|
|
9
|
+
const live = status === "leased" ? " live" : "";
|
|
10
|
+
if (iconic) return html`<span class="pill tone-${s.tone}${live}">${s.ic === "spinner" ? icon("spinner", "spin") : icon(s.ic)}${s.label}</span>`;
|
|
11
|
+
return html`<span class="pill tone-${s.tone}${live}">${s.label}</span>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function stageTone(status) {
|
|
15
|
+
return { succeeded: "ok", running: "run", skipped: "skip", retrying: "retry", rejected: "rej", blocked: "block", dead: "dead", pending: "wait", warning: "retry", awaiting_review: "retry", approved: "ok" }[status] || "wait";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Five-step rail: done, active, retry, failed, skipped, or idle (not reached).
|
|
19
|
+
export function stageRail(job) {
|
|
20
|
+
const cells = STAGES.map((stage) => {
|
|
21
|
+
const info = stageInfo(job, stage);
|
|
22
|
+
let state = "idle", note = "not reached";
|
|
23
|
+
if (info) {
|
|
24
|
+
note = info.status;
|
|
25
|
+
if (info.status === "succeeded") state = "done";
|
|
26
|
+
else if (info.status === "running") state = "active";
|
|
27
|
+
else if (info.status === "skipped") state = "skipped";
|
|
28
|
+
else if (info.status === "retrying" || info.status === "awaiting_review") state = "retry";
|
|
29
|
+
else if (["rejected", "blocked", "dead"].includes(info.status)) state = "failed";
|
|
30
|
+
else state = "idle";
|
|
31
|
+
}
|
|
32
|
+
return html`<div class="step ${state}" title="${cap(stage)}: ${note}"><i></i><span>${stage}</span></div>`;
|
|
33
|
+
});
|
|
34
|
+
return html`<div class="rail" role="img" aria-label="Stage progress">${cells}</div>`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Small cell illustrations: one per kind of empty state, drawn in mono cells like the landing.
|
|
38
|
+
const CELLS = {
|
|
39
|
+
film: ["□ ■ ■ □", "■ ▣ ▣ ■", "□ ■ ■ □"],
|
|
40
|
+
clock: ["□ ■ □", "■ ▣ ■", "□ ■ □"],
|
|
41
|
+
done: ["□ □ □ ■", "■ □ ■ □", "□ ▣ □ □"],
|
|
42
|
+
alert: ["□ ▣ □", "□ ■ □", "□ ▣ □"],
|
|
43
|
+
metrics: ["□ □ □ ■", "□ □ ■ ■", "□ ■ ■ ■"],
|
|
44
|
+
series: ["■ ■ ■", "■ □ □", "■ □ □"],
|
|
45
|
+
};
|
|
46
|
+
const cellArt = (name) => {
|
|
47
|
+
const rows = CELLS[name] || CELLS.clock;
|
|
48
|
+
return html`<pre class="cells" aria-hidden="true">${rows.map((r) => raw(r.replace(/([■▣])/g, "<b>$1</b>").replace(/□/g, '<span class="d">□</span>') + "\n"))}</pre>`;
|
|
49
|
+
};
|
|
50
|
+
export function emptyState(iconName, title, text, action = "") {
|
|
51
|
+
return html`<div class="empty">${cellArt(iconName)}<p class="t">${title}</p><p>${text}</p>${action}</div>`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const when = (iso) => html`<span title="${abs(iso)}" data-since="${iso}">${rel(iso)}</span>`;
|
|
55
|
+
|
|
56
|
+
export function copyBtn(text, label = "Copy") {
|
|
57
|
+
return html`<button type="button" class="icon-btn" data-copy="${text}" aria-label="${label}" title="${label}">${icon("copy")}</button>`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function cadenceSummary(s) {
|
|
61
|
+
const perDay = s.maxPostsPerDay === 1 ? "1 a day" : `${s.maxPostsPerDay} a day`;
|
|
62
|
+
return `${perDay} · ${s.minHoursBetweenPosts} h apart · ${s.timezone}`;
|
|
63
|
+
}
|
|
64
|
+
export const CADENCE = { on_track: ["On track", "ok"], behind: ["Behind", "warn"], ahead: ["Ahead", ""] };
|
|
65
|
+
|
|
66
|
+
export function platformChips(state, seriesId) {
|
|
67
|
+
const rows = (state.platforms || []).filter((p) => p.seriesId === seriesId);
|
|
68
|
+
const chips = ["youtube", "tiktok", "instagram", "facebook"].map((pl) => {
|
|
69
|
+
const row = rows.find((r) => r.platform === pl);
|
|
70
|
+
return row?.enabled ? html`<span class="chip">${platformName(pl)}</span>` : "";
|
|
71
|
+
});
|
|
72
|
+
return chips.some(Boolean) ? html`<div class="chips">${chips}</div>` : html`<span class="muted">No platform on</span>`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function latestRunStatus(state, series) {
|
|
76
|
+
const jobs = (state.jobs || []).filter((j) => (j.seriesId ?? j.accountId) === series.id);
|
|
77
|
+
return jobs[0] ? statusPill(jobs[0].status) : html`<span class="muted">No runs yet</span>`;
|
|
78
|
+
}
|
|
79
|
+
export { ms };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Shared plumbing: escaping html tag, state store, api() helper, toasts, formatting.
|
|
2
|
+
import { icons } from "./icons.js";
|
|
3
|
+
|
|
4
|
+
const ESC = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
5
|
+
export const esc = (v) => String(v ?? "").replace(/[&<>"']/g, (c) => ESC[c]);
|
|
6
|
+
|
|
7
|
+
class Safe { constructor(s) { this.s = s; } toString() { return this.s; } }
|
|
8
|
+
export const raw = (s) => new Safe(String(s));
|
|
9
|
+
const part = (v) => (v instanceof Safe ? v.s : Array.isArray(v) ? v.map(part).join("") : v == null || v === false ? "" : esc(v));
|
|
10
|
+
// html`...` escapes every interpolation unless it is raw() or a nested html`` result.
|
|
11
|
+
export const html = (strings, ...vals) => new Safe(strings.reduce((out, s, i) => out + s + (i < vals.length ? part(vals[i]) : ""), ""));
|
|
12
|
+
export const icon = (name, cls = "") => {
|
|
13
|
+
const svg = icons[name];
|
|
14
|
+
if (!cls) return raw(svg);
|
|
15
|
+
return raw(svg.includes(' class="') ? svg.replace(' class="', ` class="${cls} `) : svg.replace("<svg ", `<svg class="${cls}" `));
|
|
16
|
+
};
|
|
17
|
+
// An identical render is skipped, so a poll never replaces the node under a click that is in flight.
|
|
18
|
+
export const setHtml = (el, safe) => {
|
|
19
|
+
const next = safe.toString();
|
|
20
|
+
if (el.__html === next && el.isConnected) return;
|
|
21
|
+
el.__html = next;
|
|
22
|
+
el.innerHTML = next;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const $ = (root, sel) => root.querySelector(sel);
|
|
26
|
+
export const $$ = (root, sel) => [...root.querySelectorAll(sel)];
|
|
27
|
+
|
|
28
|
+
// ---------- state ----------
|
|
29
|
+
export const store = { state: null, lastGood: null, link: "connecting", listeners: new Set() };
|
|
30
|
+
export const onState = (fn) => { store.listeners.add(fn); return () => store.listeners.delete(fn); };
|
|
31
|
+
|
|
32
|
+
// ---------- toasts ----------
|
|
33
|
+
export function toast(message, { kind = "info", detail = "", ms } = {}) {
|
|
34
|
+
const host = document.getElementById("toasts");
|
|
35
|
+
const el = document.createElement("div");
|
|
36
|
+
el.className = `toast ${kind}`;
|
|
37
|
+
el.setAttribute("role", kind === "error" ? "alert" : "status");
|
|
38
|
+
el.innerHTML = html`<span class="grow"><b>${message}</b>${detail ? html`<small>${detail}</small>` : ""}</span><button type="button" aria-label="Dismiss">${icon("x")}</button>`.toString();
|
|
39
|
+
const close = () => el.remove();
|
|
40
|
+
el.querySelector("button").addEventListener("click", close);
|
|
41
|
+
host.append(el);
|
|
42
|
+
while (host.children.length > 4) host.firstChild.remove();
|
|
43
|
+
setTimeout(close, ms ?? (kind === "error" ? 9000 : 4500));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------- api ----------
|
|
47
|
+
// One write path. Returns the parsed body ({ ok: true, ... }) or a refusal ({ ok: false, refusal, fix }).
|
|
48
|
+
export async function api(path, body = {}) {
|
|
49
|
+
let res;
|
|
50
|
+
try {
|
|
51
|
+
res = await fetch(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
|
|
52
|
+
} catch {
|
|
53
|
+
return { ok: false, refusal: "Dashboard server is not reachable", fix: "Check that npx vfrey dashboard is still running, then try again." };
|
|
54
|
+
}
|
|
55
|
+
let data = null;
|
|
56
|
+
try { data = await res.json(); } catch { /* non-JSON body */ }
|
|
57
|
+
if (!data) return { ok: false, refusal: `Unexpected response (${res.status})`, fix: "Reload the page and try again." };
|
|
58
|
+
if (!res.ok && data.ok !== false) data.ok = false;
|
|
59
|
+
data.status = res.status;
|
|
60
|
+
return data;
|
|
61
|
+
}
|
|
62
|
+
export async function read(path) {
|
|
63
|
+
const res = await fetch(path, { headers: { accept: "application/json" } });
|
|
64
|
+
const data = await res.json().catch(() => null);
|
|
65
|
+
if (!data) throw new Error(`Bad response ${res.status}`);
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
export const refusalToast = (r) => toast(r.refusal || "Request failed", { kind: "error", detail: r.fix || "" });
|
|
69
|
+
|
|
70
|
+
// ---------- time ----------
|
|
71
|
+
const T = (iso) => (iso ? Date.parse(iso) : NaN);
|
|
72
|
+
export const ms = T;
|
|
73
|
+
export function dur(n) {
|
|
74
|
+
if (n == null || !isFinite(n) || n < 0) return "—";
|
|
75
|
+
if (n < 1000) return `${Math.round(n)} ms`;
|
|
76
|
+
if (n < 60000) return `${(n / 1000).toFixed(1)} s`;
|
|
77
|
+
const s = Math.round(n / 1000);
|
|
78
|
+
if (s < 3600) return `${Math.floor(s / 60)} m ${s % 60} s`;
|
|
79
|
+
return `${Math.floor(s / 3600)} h ${Math.floor((s % 3600) / 60)} m`;
|
|
80
|
+
}
|
|
81
|
+
export function rel(iso, now = Date.now()) {
|
|
82
|
+
const t = T(iso);
|
|
83
|
+
if (!isFinite(t)) return "—";
|
|
84
|
+
const d = Math.round((now - t) / 1000);
|
|
85
|
+
const a = Math.abs(d);
|
|
86
|
+
const u = a < 5 ? "just now" : a < 60 ? `${a} s` : a < 3600 ? `${Math.floor(a / 60)} min` : a < 86400 ? `${Math.floor(a / 3600)} h` : `${Math.floor(a / 86400)} d`;
|
|
87
|
+
if (u === "just now") return u;
|
|
88
|
+
return d >= 0 ? `${u} ago` : `in ${u}`;
|
|
89
|
+
}
|
|
90
|
+
export const abs = (iso) => { const t = T(iso); return isFinite(t) ? new Date(t).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "medium" }) : ""; };
|
|
91
|
+
export const clock = (n) => { const s = Math.max(0, Math.floor(n / 1000)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; };
|
|
92
|
+
export const short = (id) => String(id ?? "").slice(0, 8);
|
|
93
|
+
export const num = (n) => (n == null ? "—" : Number(n).toLocaleString());
|
|
94
|
+
|
|
95
|
+
// ---------- job helpers ----------
|
|
96
|
+
export const STAGES = ["source", "generate", "render", "publish", "engage"];
|
|
97
|
+
export const TERMINAL = new Set(["succeeded", "rejected", "blocked", "dead"]);
|
|
98
|
+
export const GROUPS = {
|
|
99
|
+
waiting: { label: "Waiting", statuses: ["pending", "awaiting_review"] },
|
|
100
|
+
running: { label: "Running", statuses: ["leased"] },
|
|
101
|
+
finished: { label: "Finished", statuses: ["succeeded"] },
|
|
102
|
+
failed: { label: "Failed", statuses: ["rejected", "blocked", "dead"] },
|
|
103
|
+
};
|
|
104
|
+
export const STATUS = {
|
|
105
|
+
pending: { label: "Waiting", tone: "wait", ic: "clock" },
|
|
106
|
+
awaiting_review: { label: "Needs review", tone: "retry", ic: "alert" },
|
|
107
|
+
leased: { label: "Running", tone: "run", ic: "spinner" },
|
|
108
|
+
succeeded: { label: "Finished", tone: "ok", ic: "done" },
|
|
109
|
+
rejected: { label: "Rejected · quality", tone: "rej", ic: "alert" },
|
|
110
|
+
blocked: { label: "Blocked · compliance", tone: "block", ic: "failed" },
|
|
111
|
+
dead: { label: "Dead · retries used", tone: "dead", ic: "failed" },
|
|
112
|
+
};
|
|
113
|
+
export const groupOf = (status) => Object.keys(GROUPS).find((g) => GROUPS[g].statuses.includes(status)) || "waiting";
|
|
114
|
+
|
|
115
|
+
export function jobTimes(job, now = Date.now()) {
|
|
116
|
+
const ev = job.events || [];
|
|
117
|
+
const start = ev.length ? ms(ev[0].at) : ms(job.createdAt);
|
|
118
|
+
let end;
|
|
119
|
+
if (TERMINAL.has(job.status)) end = ev.length ? Math.max(...ev.map((e) => ms(e.at))) : ms(job.updatedAt);
|
|
120
|
+
else if (job.status === "leased") end = now;
|
|
121
|
+
else end = NaN;
|
|
122
|
+
const started = ev.length || job.status !== "pending";
|
|
123
|
+
return { start: started ? start : NaN, end, total: started && isFinite(end) ? Math.max(0, end - start) : NaN, running: job.status === "leased" };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Per-stage status for the rail and timeline.
|
|
127
|
+
export function stageInfo(job, stage) {
|
|
128
|
+
const evs = (job.events || []).filter((e) => e.stage === stage);
|
|
129
|
+
if (!evs.length) return null;
|
|
130
|
+
// A warning is a note on the stage, not a state of it.
|
|
131
|
+
const core = evs.filter((e) => e.status !== "warning");
|
|
132
|
+
if (!core.length) return null;
|
|
133
|
+
const last = core[core.length - 1];
|
|
134
|
+
const runs = core.filter((e) => e.status === "running");
|
|
135
|
+
const terms = core.filter((e) => e.status !== "running");
|
|
136
|
+
const lastTerm = terms[terms.length - 1];
|
|
137
|
+
const open = last.status === "running";
|
|
138
|
+
const startAt = runs.length ? ms(runs[0].at) : lastTerm ? ms(lastTerm.at) - (lastTerm.durationMs || 0) : ms(last.at);
|
|
139
|
+
const endAt = open ? null : ms(lastTerm.at);
|
|
140
|
+
return { stage, events: evs, status: last.status, open, startAt, endAt, durationMs: open ? null : lastTerm.durationMs ?? (endAt - startAt) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// The series a job belongs to: jobs carry seriesId (accountId holds the same value).
|
|
144
|
+
export const seriesFor = (state, job) => job.seriesId ?? job.accountId;
|
|
145
|
+
|
|
146
|
+
export function currentStage(job) {
|
|
147
|
+
if (job.status === "succeeded") return "done";
|
|
148
|
+
return job.stage;
|
|
149
|
+
}
|
|
150
|
+
export const cap = (s) => String(s ?? "").charAt(0).toUpperCase() + String(s ?? "").slice(1);
|
|
151
|
+
export async function copyText(text) {
|
|
152
|
+
try { await navigator.clipboard.writeText(text); return true; } catch {
|
|
153
|
+
const t = document.createElement("textarea"); t.value = text; t.style.position = "fixed"; t.style.opacity = "0";
|
|
154
|
+
document.body.append(t); t.select(); let ok = false; try { ok = document.execCommand("copy"); } catch { /* ignore */ } t.remove(); return ok;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="#2196F3"/></svg>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// vfrey's own square icons. Drawn on a 16x16 grid from rectangles and straight lines only:
|
|
2
|
+
// square caps, miter joins, 1.5 stroke, currentColor. No third-party icon packs.
|
|
3
|
+
const A = ' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="miter" aria-hidden="true"';
|
|
4
|
+
const line = (body) => `<svg${A} width="16" height="16">${body}</svg>`; // small actions, 16px
|
|
5
|
+
const big = (body) => `<svg${A} class="sq">${body}</svg>`; // nav, status, empty states, sized by CSS
|
|
6
|
+
|
|
7
|
+
export const icons = {
|
|
8
|
+
// nav / larger glyphs
|
|
9
|
+
board: big('<rect x="2" y="2" width="5" height="5"/><rect x="9" y="2" width="5" height="5"/><rect x="2" y="9" width="5" height="5"/><rect x="9" y="9" width="5" height="5"/>'),
|
|
10
|
+
runs: big('<rect x="2" y="2.5" width="3" height="3" fill="currentColor"/><rect x="2" y="10.5" width="3" height="3" fill="currentColor"/><path d="M7.5 4h6.5M7.5 8h6.5M7.5 12h6.5M2 8h3"/>'),
|
|
11
|
+
series: big('<rect x="2" y="2" width="9" height="9"/><path d="M13.5 5v8.5H5"/>'),
|
|
12
|
+
creators: big('<rect x="5.5" y="2" width="5" height="5"/><path d="M2.5 14v-2.5h11V14"/>'),
|
|
13
|
+
mail: big('<rect x="2" y="3.5" width="12" height="9"/><path d="M2 4.5l6 4.5 6-4.5"/>'),
|
|
14
|
+
topics: big('<path d="M2 13.5h12"/><path d="M3 13.5V9M7 13.5V5.5M11 13.5V2.5"/><rect x="1.5" y="1.5" width="3" height="3" fill="currentColor"/>'),
|
|
15
|
+
metrics: big('<path d="M2 2v12h12"/><path d="M5.5 11V8M8.5 11V5M11.5 11V7.5"/>'),
|
|
16
|
+
warnings: big('<path d="M4 12V6.5L6 3h4l2 3.5V12M2.5 12h11M6.5 14.25h3"/>'),
|
|
17
|
+
settings: big('<path d="M2 4.5h12M2 11.5h12"/><rect x="9" y="2.5" width="3" height="4" fill="currentColor"/><rect x="4" y="9.5" width="3" height="4" fill="currentColor"/>'),
|
|
18
|
+
review: big('<rect x="2" y="2" width="12" height="12"/><path d="M5 6h6M5 8.5h6M5 11h3"/>'),
|
|
19
|
+
keys: big('<rect x="2" y="2" width="5" height="5"/><path d="M7 4.5h7M11 4.5v3M13.5 4.5v2M4.5 7v7"/>'),
|
|
20
|
+
alert: big('<path d="M8 1.75 14.75 14H1.25z"/><path d="M8 6v4M8 11.5v1.5"/>'),
|
|
21
|
+
done: big('<rect x="2" y="2" width="12" height="12"/><path d="M5 8.25 7.25 10.5 11 6"/>'),
|
|
22
|
+
failed: big('<rect x="2" y="2" width="12" height="12"/><path d="M5.5 5.5l5 5M10.5 5.5l-5 5"/>'),
|
|
23
|
+
clock: big('<rect x="2" y="2" width="12" height="12"/><path d="M8 4.5V8h3"/>'),
|
|
24
|
+
film: big('<rect x="2" y="3" width="12" height="10"/><path d="M5 3v10M11 3v10M2 6.5h3M2 9.5h3M11 6.5h3M11 9.5h3"/>'),
|
|
25
|
+
moon: big('<path d="M8.5 2.5h-6v11h11v-6h-3v3h-5v-5h3z"/>'),
|
|
26
|
+
sun: big('<rect x="5" y="5" width="6" height="6" fill="currentColor"/><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.5 3.5l1 1M11.5 11.5l1 1M12.5 3.5l-1 1M4.5 11.5l-1 1"/>'),
|
|
27
|
+
publications: big('<rect x="2" y="2" width="12" height="12"/><path d="M5 6h6M5 8.5h6M5 11h3.5"/>'),
|
|
28
|
+
sources: big('<rect x="2" y="2" width="4" height="4"/><rect x="2" y="10" width="4" height="4"/><path d="M6 4h4.5v8H6M10.5 8H14"/>'),
|
|
29
|
+
engage: big('<path d="M2 2.5h12v8H7l-3 3v-3H2z"/>'),
|
|
30
|
+
trends: big('<path d="M2 13.5h12"/><path d="M2.5 11 6 7.5l2.5 2L13.5 4"/><path d="M10.5 4h3v3"/>'),
|
|
31
|
+
// small actions
|
|
32
|
+
copy: line('<rect x="5.5" y="5.5" width="8" height="8"/><path d="M10.5 3.25v-.75h-8v8h.75"/>'),
|
|
33
|
+
check: line('<path d="M3 8.5 6.5 12 13 4.5"/>'),
|
|
34
|
+
x: line('<path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/>'),
|
|
35
|
+
retry: line('<path d="M13 8v4.5H3v-9h8"/><path d="M9 1.75l2.25 1.75L9 5.25"/>'),
|
|
36
|
+
cancel: line('<rect x="3.5" y="3.5" width="9" height="9" fill="currentColor"/>'),
|
|
37
|
+
play: line('<path d="M4.5 2.75v10.5L12.5 8z" fill="currentColor"/>'),
|
|
38
|
+
refresh: line('<path d="M3 6.5V3.5h9"/><path d="M10 1.5l2.25 2L10 5.5"/><path d="M13 9.5v3H4"/><path d="M6 10.5l-2.25 2L6 14.5"/>'),
|
|
39
|
+
chevronRight: line('<path d="M6 3l5 5-5 5"/>'),
|
|
40
|
+
chevronDown: line('<path d="M3 6l5 5 5-5"/>'),
|
|
41
|
+
external: line('<path d="M7 3H2.75v10.25H13V9"/><path d="M9.5 2.75h3.75v3.75M13 3 7.5 8.5"/>'),
|
|
42
|
+
menu: line('<path d="M2.5 4h11M2.5 8h11M2.5 12h11"/>'),
|
|
43
|
+
plus: line('<path d="M8 2.5v11M2.5 8h11"/>'),
|
|
44
|
+
trash: line('<path d="M2.5 4h11M6 4V2.25h4V4"/><path d="M3.75 4v9.75h8.5V4M6.5 6.5v5M9.5 6.5v5"/>'),
|
|
45
|
+
spinner: line('<path d="M8 2v3M8 11v3M2 8h3M11 8h3" opacity="1"/><path d="M3.75 3.75l1.5 1.5M10.75 10.75l1.5 1.5M12.25 3.75l-1.5 1.5M5.25 10.75l-1.5 1.5" opacity=".45"/>'),
|
|
46
|
+
search: line('<rect x="2.5" y="2.5" width="8" height="8"/><path d="M10.5 10.5l3 3"/>'),
|
|
47
|
+
info: line('<rect x="2.5" y="2.5" width="11" height="11"/><path d="M8 7v4M8 4.75v.75"/>'),
|
|
48
|
+
pause: line('<rect x="3.5" y="3" width="3" height="10" fill="currentColor"/><rect x="9.5" y="3" width="3" height="10" fill="currentColor"/>'),
|
|
49
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="color-scheme" content="light dark">
|
|
7
|
+
<title>vfrey desk</title>
|
|
8
|
+
<link rel="icon" href="/assets/icon.svg" type="image/svg+xml">
|
|
9
|
+
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32 48x48">
|
|
10
|
+
<link rel="apple-touch-icon" href="/assets/apple-icon.png">
|
|
11
|
+
<script src="/assets/theme.js"></script>
|
|
12
|
+
<link rel="preload" href="/fonts/google-sans-flex-latin.woff2" as="font" type="font/woff2" crossorigin>
|
|
13
|
+
<link rel="preload" href="/fonts/jetbrains-mono-medium.woff2" as="font" type="font/woff2" crossorigin>
|
|
14
|
+
<link rel="stylesheet" href="/assets/app.css">
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<header class="topbar">
|
|
18
|
+
<button class="icon-btn" id="menu-btn" type="button" aria-label="Open menu" aria-expanded="false" aria-controls="sidebar"></button>
|
|
19
|
+
<a href="/" class="wordmark" data-link>vfrey</a>
|
|
20
|
+
<span class="project-name" data-project-name></span>
|
|
21
|
+
<span class="live-dot" data-live-mini></span>
|
|
22
|
+
</header>
|
|
23
|
+
<div class="scrim" id="scrim"></div>
|
|
24
|
+
<div class="shell">
|
|
25
|
+
<aside class="sidebar" id="sidebar" aria-label="Primary">
|
|
26
|
+
<a href="/" class="wordmark side-word" data-link>vfrey</a>
|
|
27
|
+
<p class="project-name" data-project-name></p>
|
|
28
|
+
<p class="side-section">Desk</p>
|
|
29
|
+
<nav id="nav"></nav>
|
|
30
|
+
<div class="side-foot">
|
|
31
|
+
<div class="live" id="live" role="status"><span class="live-dot"></span><span class="live-text">Connecting</span></div>
|
|
32
|
+
<p class="env" id="env"></p>
|
|
33
|
+
<button type="button" class="theme-btn" id="theme-btn" aria-label="Switch color theme"><span class="theme-ic"></span><span class="theme-text"></span></button>
|
|
34
|
+
</div>
|
|
35
|
+
</aside>
|
|
36
|
+
<main id="main" tabindex="-1"></main>
|
|
37
|
+
</div>
|
|
38
|
+
<div id="toasts" aria-live="polite"></div>
|
|
39
|
+
<script type="module" src="/assets/app.js"></script>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const DASH: string;
|
|
2
|
+
export const DRY_RUN_ID: string;
|
|
3
|
+
export interface MetricRow { platform: string; externalId: string; capturedAt?: string; views: number | null; watchTimeSec: number | null; retention: number | null; estimatedRevenue: number | null; revenueStatus: string; [k: string]: unknown }
|
|
4
|
+
export interface PostRow { kind: "measured" | "unmeasured" | "dry_run"; platform: string; externalId: string | null; metric: MetricRow | null; reason: string }
|
|
5
|
+
export interface Known { total: number | null; known: number; of: number }
|
|
6
|
+
export interface KnownMean { mean: number | null; known: number; of: number }
|
|
7
|
+
export function platformLabel(p: string): string;
|
|
8
|
+
export function exposure(platform: string, field: string): "returned" | "not_exposed";
|
|
9
|
+
export function gapLabel(m: Pick<MetricRow, "platform" | "revenueStatus" | "estimatedRevenue"> & Record<string, unknown>, field: string): string;
|
|
10
|
+
export function sumKnown(values: Array<number | null | undefined>): Known;
|
|
11
|
+
export function meanKnown(values: Array<number | null | undefined>): KnownMean;
|
|
12
|
+
export function compact(n: number | null | undefined): string;
|
|
13
|
+
export function percent(r: number | null | undefined): string;
|
|
14
|
+
export function isDryRun(externalId: string | null | undefined): boolean;
|
|
15
|
+
export function buildRows(metrics: MetricRow[] | undefined, publications: Array<{ id: string; platform: string; externalId: string | null }> | undefined): PostRow[];
|
|
16
|
+
export function summarize(rows: PostRow[]): { posts: number; dryRuns: number; measured: number; views: Known; watch: KnownMean; retention: KnownMean; asOf: string | null };
|
|
17
|
+
export function describeSum(s: Known, noun: string, posts?: string): string;
|
|
18
|
+
export interface Gained { gained: number | null; from: string | null; to: string | null; reason: "ok" | "one_snapshot" | "no_snapshot" | "unknown_views" }
|
|
19
|
+
export function viewsGained(snapshots: Array<{ capturedAt: string; views: number | null }> | undefined): Gained;
|
|
20
|
+
export interface GainSummary { gained: number | null; known: number; of: number; asOf: string | null; since: string | null; reason: "ok" | "not_measured" | "one_snapshot" | "unknown_views" }
|
|
21
|
+
export function summarizeGains(results: Gained[]): GainSummary;
|
|
22
|
+
export const TREND_REASONS: Record<string, string>;
|