@heytherevibin/skillforge 0.2.1
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/CHANGELOG.md +16 -0
- package/CODE_OF_CONDUCT.md +34 -0
- package/CONTRIBUTING.md +38 -0
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/RELEASING.md +93 -0
- package/SECURITY.md +31 -0
- package/STRATEGY.md +26 -0
- package/bin/cli.js +547 -0
- package/lib/packs.js +184 -0
- package/package.json +38 -0
- package/python/app/__init__.py +0 -0
- package/python/app/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/app/__pycache__/auth.cpython-312.pyc +0 -0
- package/python/app/__pycache__/main.cpython-312.pyc +0 -0
- package/python/app/auth.py +63 -0
- package/python/app/cli.py +78 -0
- package/python/app/db_paths.py +26 -0
- package/python/app/events_cli.py +175 -0
- package/python/app/main.py +647 -0
- package/python/app/materialize.py +138 -0
- package/python/app/mcp_server.py +610 -0
- package/python/app/route_cli.py +117 -0
- package/python/requirements-dev.txt +1 -0
- package/python/requirements.txt +7 -0
- package/python/tests/test_db_paths.py +41 -0
- package/skills/accessibility/SKILL.md +145 -0
- package/skills/agent-architecture-audit/SKILL.md +256 -0
- package/skills/agent-eval/SKILL.md +144 -0
- package/skills/agent-harness-construction/SKILL.md +72 -0
- package/skills/agent-introspection-debugging/SKILL.md +152 -0
- package/skills/agent-payment-x402/SKILL.md +224 -0
- package/skills/agent-sort/SKILL.md +214 -0
- package/skills/agentic-engineering/SKILL.md +62 -0
- package/skills/agentic-os/SKILL.md +386 -0
- package/skills/ai-first-engineering/SKILL.md +50 -0
- package/skills/ai-regression-testing/SKILL.md +384 -0
- package/skills/android-clean-architecture/SKILL.md +338 -0
- package/skills/angular-developer/SKILL.md +153 -0
- package/skills/angular-developer/references/angular-animations.md +160 -0
- package/skills/angular-developer/references/angular-aria.md +410 -0
- package/skills/angular-developer/references/cli.md +86 -0
- package/skills/angular-developer/references/component-harnesses.md +59 -0
- package/skills/angular-developer/references/component-styling.md +91 -0
- package/skills/angular-developer/references/components.md +117 -0
- package/skills/angular-developer/references/creating-services.md +97 -0
- package/skills/angular-developer/references/data-resolvers.md +69 -0
- package/skills/angular-developer/references/define-routes.md +67 -0
- package/skills/angular-developer/references/defining-providers.md +72 -0
- package/skills/angular-developer/references/di-fundamentals.md +120 -0
- package/skills/angular-developer/references/e2e-testing.md +56 -0
- package/skills/angular-developer/references/effects.md +83 -0
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
- package/skills/angular-developer/references/host-elements.md +80 -0
- package/skills/angular-developer/references/injection-context.md +63 -0
- package/skills/angular-developer/references/inputs.md +101 -0
- package/skills/angular-developer/references/linked-signal.md +59 -0
- package/skills/angular-developer/references/loading-strategies.md +61 -0
- package/skills/angular-developer/references/mcp.md +108 -0
- package/skills/angular-developer/references/navigate-to-routes.md +69 -0
- package/skills/angular-developer/references/outputs.md +86 -0
- package/skills/angular-developer/references/reactive-forms.md +122 -0
- package/skills/angular-developer/references/rendering-strategies.md +44 -0
- package/skills/angular-developer/references/resource.md +77 -0
- package/skills/angular-developer/references/route-animations.md +56 -0
- package/skills/angular-developer/references/route-guards.md +52 -0
- package/skills/angular-developer/references/router-lifecycle.md +45 -0
- package/skills/angular-developer/references/router-testing.md +87 -0
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
- package/skills/angular-developer/references/signal-forms.md +795 -0
- package/skills/angular-developer/references/signals-overview.md +94 -0
- package/skills/angular-developer/references/tailwind-css.md +69 -0
- package/skills/angular-developer/references/template-driven-forms.md +114 -0
- package/skills/angular-developer/references/testing-fundamentals.md +65 -0
- package/skills/api-connector-builder/SKILL.md +120 -0
- package/skills/api-design/SKILL.md +522 -0
- package/skills/architecture-decision-records/SKILL.md +178 -0
- package/skills/article-writing/SKILL.md +78 -0
- package/skills/automation-audit-ops/SKILL.md +141 -0
- package/skills/autonomous-agent-harness/SKILL.md +272 -0
- package/skills/autonomous-loops/SKILL.md +609 -0
- package/skills/backend-patterns/SKILL.md +560 -0
- package/skills/benchmark/SKILL.md +92 -0
- package/skills/blueprint/SKILL.md +104 -0
- package/skills/browser-qa/SKILL.md +86 -0
- package/skills/bun-runtime/SKILL.md +83 -0
- package/skills/canary-watch/SKILL.md +98 -0
- package/skills/carrier-relationship-management/SKILL.md +211 -0
- package/skills/cisco-ios-patterns/SKILL.md +163 -0
- package/skills/ck/SKILL.md +147 -0
- package/skills/ck/commands/forget.mjs +44 -0
- package/skills/ck/commands/info.mjs +24 -0
- package/skills/ck/commands/init.mjs +143 -0
- package/skills/ck/commands/list.mjs +40 -0
- package/skills/ck/commands/migrate.mjs +202 -0
- package/skills/ck/commands/resume.mjs +36 -0
- package/skills/ck/commands/save.mjs +210 -0
- package/skills/ck/commands/shared.mjs +387 -0
- package/skills/ck/hooks/session-start.mjs +224 -0
- package/skills/claude-devfleet/SKILL.md +103 -0
- package/skills/click-path-audit/SKILL.md +244 -0
- package/skills/clickhouse-io/SKILL.md +438 -0
- package/skills/code-tour/SKILL.md +235 -0
- package/skills/codebase-onboarding/SKILL.md +232 -0
- package/skills/coding-standards/SKILL.md +548 -0
- package/skills/compose-multiplatform-patterns/SKILL.md +298 -0
- package/skills/connections-optimizer/SKILL.md +188 -0
- package/skills/content-engine/SKILL.md +126 -0
- package/skills/content-hash-cache-pattern/SKILL.md +160 -0
- package/skills/context-budget/SKILL.md +134 -0
- package/skills/continuous-agent-loop/SKILL.md +44 -0
- package/skills/continuous-learning/SKILL.md +129 -0
- package/skills/continuous-learning/config.json +18 -0
- package/skills/continuous-learning/evaluate-session.sh +69 -0
- package/skills/continuous-learning-v2/SKILL.md +358 -0
- package/skills/continuous-learning-v2/agents/observer-loop.sh +322 -0
- package/skills/continuous-learning-v2/agents/observer.md +198 -0
- package/skills/continuous-learning-v2/agents/session-guardian.sh +150 -0
- package/skills/continuous-learning-v2/agents/start-observer.sh +248 -0
- package/skills/continuous-learning-v2/config.json +8 -0
- package/skills/continuous-learning-v2/hooks/observe.sh +476 -0
- package/skills/continuous-learning-v2/scripts/detect-project.sh +288 -0
- package/skills/continuous-learning-v2/scripts/instinct-cli.py +1519 -0
- package/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -0
- package/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -0
- package/skills/continuous-learning-v2/scripts/test_parse_instinct.py +1018 -0
- package/skills/cost-aware-llm-pipeline/SKILL.md +182 -0
- package/skills/cost-tracking/SKILL.md +147 -0
- package/skills/council/SKILL.md +202 -0
- package/skills/cpp-coding-standards/SKILL.md +722 -0
- package/skills/cpp-testing/SKILL.md +323 -0
- package/skills/crosspost/SKILL.md +110 -0
- package/skills/csharp-testing/SKILL.md +320 -0
- package/skills/customer-billing-ops/SKILL.md +139 -0
- package/skills/customs-trade-compliance/SKILL.md +262 -0
- package/skills/dart-flutter-patterns/SKILL.md +562 -0
- package/skills/dashboard-builder/SKILL.md +108 -0
- package/skills/data-scraper-agent/SKILL.md +764 -0
- package/skills/database-migrations/SKILL.md +428 -0
- package/skills/deep-research/SKILL.md +158 -0
- package/skills/defi-amm-security/SKILL.md +166 -0
- package/skills/deployment-patterns/SKILL.md +426 -0
- package/skills/design-system/SKILL.md +81 -0
- package/skills/django-celery/SKILL.md +456 -0
- package/skills/django-patterns/SKILL.md +733 -0
- package/skills/django-security/SKILL.md +592 -0
- package/skills/django-tdd/SKILL.md +728 -0
- package/skills/django-verification/SKILL.md +468 -0
- package/skills/dmux-workflows/SKILL.md +190 -0
- package/skills/docker-patterns/SKILL.md +363 -0
- package/skills/documentation-lookup/SKILL.md +89 -0
- package/skills/dotnet-patterns/SKILL.md +320 -0
- package/skills/e2e-testing/SKILL.md +325 -0
- package/skills/email-ops/SKILL.md +120 -0
- package/skills/energy-procurement/SKILL.md +227 -0
- package/skills/enterprise-agent-ops/SKILL.md +49 -0
- package/skills/error-handling/SKILL.md +375 -0
- package/skills/eval-harness/SKILL.md +269 -0
- package/skills/evm-token-decimals/SKILL.md +130 -0
- package/skills/exa-search/SKILL.md +106 -0
- package/skills/fal-ai-media/SKILL.md +287 -0
- package/skills/fastapi-patterns/SKILL.md +327 -0
- package/skills/finance-billing-ops/SKILL.md +126 -0
- package/skills/flox-environments/SKILL.md +496 -0
- package/skills/flutter-dart-code-review/SKILL.md +434 -0
- package/skills/foundation-models-on-device/SKILL.md +243 -0
- package/skills/frontend-design-direction/SKILL.md +92 -0
- package/skills/frontend-patterns/SKILL.md +641 -0
- package/skills/frontend-slides/SKILL.md +183 -0
- package/skills/frontend-slides/STYLE_PRESETS.md +330 -0
- package/skills/frontend-slides/animation-patterns.md +122 -0
- package/skills/frontend-slides/html-template.md +419 -0
- package/skills/frontend-slides/scripts/export-pdf.sh +418 -0
- package/skills/frontend-slides/scripts/extract-pptx.py +96 -0
- package/skills/frontend-slides/viewport-base.css +153 -0
- package/skills/fsharp-testing/SKILL.md +279 -0
- package/skills/gan-style-harness/SKILL.md +278 -0
- package/skills/gateguard/SKILL.md +125 -0
- package/skills/git-workflow/SKILL.md +714 -0
- package/skills/github-ops/SKILL.md +143 -0
- package/skills/golang-patterns/SKILL.md +673 -0
- package/skills/golang-testing/SKILL.md +719 -0
- package/skills/google-workspace-ops/SKILL.md +94 -0
- package/skills/healthcare-cdss-patterns/SKILL.md +245 -0
- package/skills/healthcare-emr-patterns/SKILL.md +159 -0
- package/skills/healthcare-eval-harness/SKILL.md +207 -0
- package/skills/healthcare-phi-compliance/SKILL.md +145 -0
- package/skills/hermes-imports/SKILL.md +87 -0
- package/skills/hexagonal-architecture/SKILL.md +275 -0
- package/skills/hipaa-compliance/SKILL.md +78 -0
- package/skills/homelab-network-readiness/SKILL.md +169 -0
- package/skills/homelab-network-setup/SKILL.md +129 -0
- package/skills/homelab-pihole-dns/SKILL.md +274 -0
- package/skills/homelab-vlan-segmentation/SKILL.md +311 -0
- package/skills/homelab-wireguard-vpn/SKILL.md +305 -0
- package/skills/hookify-rules/SKILL.md +128 -0
- package/skills/inventory-demand-planning/SKILL.md +246 -0
- package/skills/investor-materials/SKILL.md +95 -0
- package/skills/investor-outreach/SKILL.md +90 -0
- package/skills/ios-icon-gen/SKILL.md +157 -0
- package/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
- package/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
- package/skills/iterative-retrieval/SKILL.md +209 -0
- package/skills/java-coding-standards/SKILL.md +382 -0
- package/skills/jira-integration/SKILL.md +292 -0
- package/skills/jpa-patterns/SKILL.md +150 -0
- package/skills/knowledge-ops/SKILL.md +153 -0
- package/skills/kotlin-coroutines-flows/SKILL.md +283 -0
- package/skills/kotlin-exposed-patterns/SKILL.md +718 -0
- package/skills/kotlin-ktor-patterns/SKILL.md +688 -0
- package/skills/kotlin-patterns/SKILL.md +710 -0
- package/skills/kotlin-testing/SKILL.md +823 -0
- package/skills/laravel-patterns/SKILL.md +414 -0
- package/skills/laravel-plugin-discovery/SKILL.md +228 -0
- package/skills/laravel-security/SKILL.md +284 -0
- package/skills/laravel-tdd/SKILL.md +282 -0
- package/skills/laravel-verification/SKILL.md +178 -0
- package/skills/lead-intelligence/SKILL.md +320 -0
- package/skills/lead-intelligence/agents/enrichment-agent.md +85 -0
- package/skills/lead-intelligence/agents/mutual-mapper.md +75 -0
- package/skills/lead-intelligence/agents/outreach-drafter.md +98 -0
- package/skills/lead-intelligence/agents/signal-scorer.md +60 -0
- package/skills/liquid-glass-design/SKILL.md +279 -0
- package/skills/llm-trading-agent-security/SKILL.md +146 -0
- package/skills/logistics-exception-management/SKILL.md +221 -0
- package/skills/make-interfaces-feel-better/SKILL.md +151 -0
- package/skills/manim-video/SKILL.md +88 -0
- package/skills/manim-video/assets/network_graph_scene.py +52 -0
- package/skills/market-research/SKILL.md +74 -0
- package/skills/mcp-server-patterns/SKILL.md +68 -0
- package/skills/messages-ops/SKILL.md +103 -0
- package/skills/mle-workflow/SKILL.md +345 -0
- package/skills/motion-advanced/SKILL.md +596 -0
- package/skills/motion-foundations/SKILL.md +299 -0
- package/skills/motion-patterns/SKILL.md +435 -0
- package/skills/motion-ui/SKILL.md +574 -0
- package/skills/mysql-patterns/SKILL.md +411 -0
- package/skills/nanoclaw-repl/SKILL.md +32 -0
- package/skills/nestjs-patterns/SKILL.md +229 -0
- package/skills/netmiko-ssh-automation/SKILL.md +173 -0
- package/skills/network-bgp-diagnostics/SKILL.md +167 -0
- package/skills/network-config-validation/SKILL.md +210 -0
- package/skills/network-interface-health/SKILL.md +152 -0
- package/skills/nextjs-turbopack/SKILL.md +43 -0
- package/skills/nodejs-keccak256/SKILL.md +102 -0
- package/skills/nutrient-document-processing/SKILL.md +166 -0
- package/skills/nuxt4-patterns/SKILL.md +99 -0
- package/skills/openclaw-persona-forge/SKILL.md +288 -0
- package/skills/openclaw-persona-forge/gacha.py +224 -0
- package/skills/openclaw-persona-forge/gacha.sh +5 -0
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
- package/skills/openclaw-persona-forge/references/output-template.md +166 -0
- package/skills/opensource-pipeline/SKILL.md +254 -0
- package/skills/perl-patterns/SKILL.md +503 -0
- package/skills/perl-security/SKILL.md +502 -0
- package/skills/perl-testing/SKILL.md +474 -0
- package/skills/plan-orchestrate/SKILL.md +253 -0
- package/skills/plankton-code-quality/SKILL.md +236 -0
- package/skills/postgres-patterns/SKILL.md +146 -0
- package/skills/product-capability/SKILL.md +140 -0
- package/skills/product-lens/SKILL.md +91 -0
- package/skills/production-audit/SKILL.md +206 -0
- package/skills/production-scheduling/SKILL.md +237 -0
- package/skills/project-flow-ops/SKILL.md +110 -0
- package/skills/prompt-optimizer/SKILL.md +398 -0
- package/skills/python-patterns/SKILL.md +749 -0
- package/skills/python-testing/SKILL.md +815 -0
- package/skills/pytorch-patterns/SKILL.md +395 -0
- package/skills/quality-nonconformance/SKILL.md +259 -0
- package/skills/quarkus-patterns/SKILL.md +721 -0
- package/skills/quarkus-security/SKILL.md +466 -0
- package/skills/quarkus-tdd/SKILL.md +810 -0
- package/skills/quarkus-verification/SKILL.md +478 -0
- package/skills/ralphinho-rfc-pipeline/SKILL.md +66 -0
- package/skills/redis-patterns/SKILL.md +402 -0
- package/skills/regex-vs-llm-structured-text/SKILL.md +219 -0
- package/skills/remotion-video-creation/SKILL.md +43 -0
- package/skills/remotion-video-creation/rules/3d.md +86 -0
- package/skills/remotion-video-creation/rules/animations.md +29 -0
- package/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/skills/remotion-video-creation/rules/assets.md +78 -0
- package/skills/remotion-video-creation/rules/audio.md +172 -0
- package/skills/remotion-video-creation/rules/calculate-metadata.md +104 -0
- package/skills/remotion-video-creation/rules/can-decode.md +75 -0
- package/skills/remotion-video-creation/rules/charts.md +58 -0
- package/skills/remotion-video-creation/rules/compositions.md +146 -0
- package/skills/remotion-video-creation/rules/display-captions.md +126 -0
- package/skills/remotion-video-creation/rules/extract-frames.md +229 -0
- package/skills/remotion-video-creation/rules/fonts.md +152 -0
- package/skills/remotion-video-creation/rules/get-audio-duration.md +58 -0
- package/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -0
- package/skills/remotion-video-creation/rules/get-video-duration.md +58 -0
- package/skills/remotion-video-creation/rules/gifs.md +138 -0
- package/skills/remotion-video-creation/rules/images.md +130 -0
- package/skills/remotion-video-creation/rules/import-srt-captions.md +67 -0
- package/skills/remotion-video-creation/rules/lottie.md +67 -0
- package/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
- package/skills/remotion-video-creation/rules/measuring-text.md +143 -0
- package/skills/remotion-video-creation/rules/sequencing.md +106 -0
- package/skills/remotion-video-creation/rules/tailwind.md +11 -0
- package/skills/remotion-video-creation/rules/text-animations.md +20 -0
- package/skills/remotion-video-creation/rules/timing.md +179 -0
- package/skills/remotion-video-creation/rules/transcribe-captions.md +19 -0
- package/skills/remotion-video-creation/rules/transitions.md +122 -0
- package/skills/remotion-video-creation/rules/trimming.md +52 -0
- package/skills/remotion-video-creation/rules/videos.md +171 -0
- package/skills/repo-scan/SKILL.md +78 -0
- package/skills/research-ops/SKILL.md +111 -0
- package/skills/returns-reverse-logistics/SKILL.md +239 -0
- package/skills/rules-distill/SKILL.md +263 -0
- package/skills/rules-distill/scripts/scan-rules.sh +58 -0
- package/skills/rules-distill/scripts/scan-skills.sh +129 -0
- package/skills/rust-patterns/SKILL.md +498 -0
- package/skills/rust-testing/SKILL.md +499 -0
- package/skills/safety-guard/SKILL.md +74 -0
- package/skills/santa-method/SKILL.md +306 -0
- package/skills/scientific-db-pubmed-database/SKILL.md +175 -0
- package/skills/scientific-db-uspto-database/SKILL.md +177 -0
- package/skills/scientific-pkg-gget/SKILL.md +166 -0
- package/skills/scientific-thinking-literature-review/SKILL.md +192 -0
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
- package/skills/search-first/SKILL.md +181 -0
- package/skills/security-bounty-hunter/SKILL.md +99 -0
- package/skills/security-review/SKILL.md +502 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/seo/SKILL.md +153 -0
- package/skills/skill-comply/SKILL.md +57 -0
- package/skills/skill-comply/fixtures/compliant_trace.jsonl +5 -0
- package/skills/skill-comply/fixtures/noncompliant_trace.jsonl +3 -0
- package/skills/skill-comply/fixtures/tdd_spec.yaml +44 -0
- package/skills/skill-comply/prompts/classifier.md +24 -0
- package/skills/skill-comply/prompts/scenario_generator.md +62 -0
- package/skills/skill-comply/prompts/spec_generator.md +42 -0
- package/skills/skill-comply/pyproject.toml +15 -0
- package/skills/skill-comply/scripts/__init__.py +0 -0
- package/skills/skill-comply/scripts/classifier.py +85 -0
- package/skills/skill-comply/scripts/grader.py +124 -0
- package/skills/skill-comply/scripts/parser.py +107 -0
- package/skills/skill-comply/scripts/report.py +170 -0
- package/skills/skill-comply/scripts/run.py +127 -0
- package/skills/skill-comply/scripts/runner.py +186 -0
- package/skills/skill-comply/scripts/scenario_generator.py +70 -0
- package/skills/skill-comply/scripts/spec_generator.py +72 -0
- package/skills/skill-comply/scripts/utils.py +13 -0
- package/skills/skill-comply/tests/test_grader.py +197 -0
- package/skills/skill-comply/tests/test_parser.py +90 -0
- package/skills/skill-comply/tests/test_runner.py +172 -0
- package/skills/skill-scout/SKILL.md +139 -0
- package/skills/skill-stocktake/SKILL.md +193 -0
- package/skills/skill-stocktake/scripts/quick-diff.sh +87 -0
- package/skills/skill-stocktake/scripts/save-results.sh +56 -0
- package/skills/skill-stocktake/scripts/scan.sh +170 -0
- package/skills/social-graph-ranker/SKILL.md +153 -0
- package/skills/springboot-patterns/SKILL.md +313 -0
- package/skills/springboot-security/SKILL.md +271 -0
- package/skills/springboot-tdd/SKILL.md +157 -0
- package/skills/springboot-verification/SKILL.md +230 -0
- package/skills/strategic-compact/SKILL.md +129 -0
- package/skills/strategic-compact/suggest-compact.sh +54 -0
- package/skills/swift-actor-persistence/SKILL.md +142 -0
- package/skills/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/swift-protocol-di-testing/SKILL.md +189 -0
- package/skills/swiftui-patterns/SKILL.md +259 -0
- package/skills/tdd-workflow/SKILL.md +462 -0
- package/skills/team-builder/SKILL.md +166 -0
- package/skills/terminal-ops/SKILL.md +108 -0
- package/skills/tinystruct-patterns/SKILL.md +130 -0
- package/skills/tinystruct-patterns/references/architecture.md +77 -0
- package/skills/tinystruct-patterns/references/data-handling.md +35 -0
- package/skills/tinystruct-patterns/references/routing.md +57 -0
- package/skills/tinystruct-patterns/references/system-usage.md +74 -0
- package/skills/tinystruct-patterns/references/testing.md +59 -0
- package/skills/token-budget-advisor/SKILL.md +133 -0
- package/skills/ui-demo/SKILL.md +464 -0
- package/skills/ui-to-vue/SKILL.md +134 -0
- package/skills/unified-notifications-ops/SKILL.md +186 -0
- package/skills/verification-loop/SKILL.md +125 -0
- package/skills/video-editing/SKILL.md +309 -0
- package/skills/videodb/SKILL.md +373 -0
- package/skills/videodb/reference/api-reference.md +550 -0
- package/skills/videodb/reference/capture-reference.md +407 -0
- package/skills/videodb/reference/capture.md +101 -0
- package/skills/videodb/reference/editor.md +443 -0
- package/skills/videodb/reference/generative.md +331 -0
- package/skills/videodb/reference/rtstream-reference.md +564 -0
- package/skills/videodb/reference/rtstream.md +65 -0
- package/skills/videodb/reference/search.md +230 -0
- package/skills/videodb/reference/streaming.md +406 -0
- package/skills/videodb/reference/use-cases.md +118 -0
- package/skills/videodb/scripts/ws_listener.py +282 -0
- package/skills/visa-doc-translate/README.md +86 -0
- package/skills/visa-doc-translate/SKILL.md +117 -0
- package/skills/vite-patterns/SKILL.md +448 -0
- package/skills/windows-desktop-e2e/SKILL.md +787 -0
- package/skills/workspace-surface-audit/SKILL.md +124 -0
- package/skills/x-api/SKILL.md +233 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# export-pdf.sh - Export an HTML presentation to PDF
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash scripts/export-pdf.sh <path-to-html> [output.pdf]
|
|
6
|
+
#
|
|
7
|
+
# Examples:
|
|
8
|
+
# bash scripts/export-pdf.sh ./my-deck/index.html
|
|
9
|
+
# bash scripts/export-pdf.sh ./presentation.html ./presentation.pdf
|
|
10
|
+
#
|
|
11
|
+
# What this does:
|
|
12
|
+
# 1. Starts a local server to serve the HTML (fonts and assets need HTTP)
|
|
13
|
+
# 2. Uses Playwright to screenshot each slide at 1920x1080
|
|
14
|
+
# 3. Combines all screenshots into a single PDF
|
|
15
|
+
# 4. Cleans up the server and temp files
|
|
16
|
+
#
|
|
17
|
+
# The PDF preserves colors, fonts, and layout - but not animations.
|
|
18
|
+
# Perfect for email attachments, printing, or embedding in documents.
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# --- Colors ---
|
|
22
|
+
RED='\033[0;31m'
|
|
23
|
+
GREEN='\033[0;32m'
|
|
24
|
+
CYAN='\033[0;36m'
|
|
25
|
+
YELLOW='\033[1;33m'
|
|
26
|
+
BOLD='\033[1m'
|
|
27
|
+
NC='\033[0m'
|
|
28
|
+
|
|
29
|
+
info() { echo -e "${CYAN}INFO:${NC} $*"; }
|
|
30
|
+
ok() { echo -e "${GREEN}OK:${NC} $*"; }
|
|
31
|
+
warn() { echo -e "${YELLOW}WARNING:${NC} $*"; }
|
|
32
|
+
err() { echo -e "${RED}ERROR:${NC} $*" >&2; }
|
|
33
|
+
|
|
34
|
+
# --- Parse flags ---
|
|
35
|
+
|
|
36
|
+
# Default resolution: 1920x1080 (full HD, ~1-2MB per slide)
|
|
37
|
+
# Compact resolution: 1280x720 (HD, ~50-70% smaller files)
|
|
38
|
+
VIEWPORT_W=1920
|
|
39
|
+
VIEWPORT_H=1080
|
|
40
|
+
COMPACT=false
|
|
41
|
+
|
|
42
|
+
POSITIONAL=()
|
|
43
|
+
for arg in "$@"; do
|
|
44
|
+
case $arg in
|
|
45
|
+
--compact)
|
|
46
|
+
COMPACT=true
|
|
47
|
+
VIEWPORT_W=1280
|
|
48
|
+
VIEWPORT_H=720
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
POSITIONAL+=("$arg")
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
done
|
|
55
|
+
set -- "${POSITIONAL[@]}"
|
|
56
|
+
|
|
57
|
+
# --- Input validation ---
|
|
58
|
+
|
|
59
|
+
if [[ $# -lt 1 ]]; then
|
|
60
|
+
err "Usage: bash scripts/export-pdf.sh <path-to-html> [output.pdf] [--compact]"
|
|
61
|
+
err ""
|
|
62
|
+
err "Examples:"
|
|
63
|
+
err " bash scripts/export-pdf.sh ./my-deck/index.html"
|
|
64
|
+
err " bash scripts/export-pdf.sh ./presentation.html ./slides.pdf"
|
|
65
|
+
err " bash scripts/export-pdf.sh ./presentation.html --compact # smaller file size"
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
INPUT_HTML="$1"
|
|
70
|
+
if [[ ! -f "$INPUT_HTML" ]]; then
|
|
71
|
+
err "File not found: $INPUT_HTML"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Resolve to absolute path
|
|
76
|
+
INPUT_HTML=$(cd "$(dirname "$INPUT_HTML")" && pwd)/$(basename "$INPUT_HTML")
|
|
77
|
+
|
|
78
|
+
# Output PDF path: use second argument or derive from input name
|
|
79
|
+
if [[ $# -ge 2 ]]; then
|
|
80
|
+
OUTPUT_PDF="$2"
|
|
81
|
+
else
|
|
82
|
+
OUTPUT_PDF="$(dirname "$INPUT_HTML")/$(basename "$INPUT_HTML" .html).pdf"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Resolve output to absolute path
|
|
86
|
+
OUTPUT_DIR=$(dirname "$OUTPUT_PDF")
|
|
87
|
+
mkdir -p "$OUTPUT_DIR"
|
|
88
|
+
OUTPUT_PDF="$OUTPUT_DIR/$(basename "$OUTPUT_PDF")"
|
|
89
|
+
|
|
90
|
+
echo ""
|
|
91
|
+
echo -e "${BOLD}========================================${NC}"
|
|
92
|
+
echo -e "${BOLD} Export Slides to PDF${NC}"
|
|
93
|
+
echo -e "${BOLD}========================================${NC}"
|
|
94
|
+
echo ""
|
|
95
|
+
|
|
96
|
+
# --- Step 1: Check dependencies ---
|
|
97
|
+
|
|
98
|
+
info "Checking dependencies..."
|
|
99
|
+
|
|
100
|
+
if ! command -v npx &>/dev/null; then
|
|
101
|
+
err "Node.js is required but not installed."
|
|
102
|
+
err ""
|
|
103
|
+
err "Install Node.js:"
|
|
104
|
+
err " macOS: brew install node"
|
|
105
|
+
err " or visit https://nodejs.org and download the installer"
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
ok "Node.js found"
|
|
110
|
+
|
|
111
|
+
# --- Step 2: Create the export script ---
|
|
112
|
+
|
|
113
|
+
# We use a temporary Node.js script with Playwright to:
|
|
114
|
+
# 1. Start a local server (so fonts load correctly)
|
|
115
|
+
# 2. Navigate to each slide
|
|
116
|
+
# 3. Screenshot each slide at 1920x1080 (16:9 landscape)
|
|
117
|
+
# 4. Combine into a single PDF
|
|
118
|
+
|
|
119
|
+
TEMP_DIR=$(mktemp -d)
|
|
120
|
+
TEMP_SCRIPT="$TEMP_DIR/export-slides.mjs"
|
|
121
|
+
|
|
122
|
+
# Figure out which directory to serve (the folder containing the HTML)
|
|
123
|
+
SERVE_DIR=$(dirname "$INPUT_HTML")
|
|
124
|
+
HTML_FILENAME=$(basename "$INPUT_HTML")
|
|
125
|
+
|
|
126
|
+
cat > "$TEMP_SCRIPT" << 'EXPORT_SCRIPT'
|
|
127
|
+
// export-slides.mjs - Playwright script to export HTML slides to PDF
|
|
128
|
+
//
|
|
129
|
+
// How it works:
|
|
130
|
+
// 1. Starts a local HTTP server (needed for fonts/assets to load)
|
|
131
|
+
// 2. Opens the presentation in a headless browser at 1920x1080
|
|
132
|
+
// 3. Counts the total number of slides
|
|
133
|
+
// 4. Screenshots each slide one by one
|
|
134
|
+
// 5. Generates a PDF with all slides as landscape pages
|
|
135
|
+
|
|
136
|
+
import { chromium } from 'playwright';
|
|
137
|
+
import { createServer } from 'http';
|
|
138
|
+
import { readFileSync, existsSync, mkdirSync, unlinkSync, writeFileSync } from 'fs';
|
|
139
|
+
import { join, extname, resolve } from 'path';
|
|
140
|
+
import { execSync } from 'child_process';
|
|
141
|
+
|
|
142
|
+
const SERVE_DIR = process.argv[2];
|
|
143
|
+
const HTML_FILE = process.argv[3];
|
|
144
|
+
const OUTPUT_PDF = process.argv[4];
|
|
145
|
+
const SCREENSHOT_DIR = process.argv[5];
|
|
146
|
+
const VP_WIDTH = parseInt(process.argv[6]) || 1920;
|
|
147
|
+
const VP_HEIGHT = parseInt(process.argv[7]) || 1080;
|
|
148
|
+
|
|
149
|
+
// --- Simple static file server ---
|
|
150
|
+
// (We need HTTP so that Google Fonts and relative assets load correctly)
|
|
151
|
+
|
|
152
|
+
const MIME_TYPES = {
|
|
153
|
+
'.html': 'text/html',
|
|
154
|
+
'.css': 'text/css',
|
|
155
|
+
'.js': 'application/javascript',
|
|
156
|
+
'.json': 'application/json',
|
|
157
|
+
'.png': 'image/png',
|
|
158
|
+
'.jpg': 'image/jpeg',
|
|
159
|
+
'.jpeg': 'image/jpeg',
|
|
160
|
+
'.gif': 'image/gif',
|
|
161
|
+
'.svg': 'image/svg+xml',
|
|
162
|
+
'.webp': 'image/webp',
|
|
163
|
+
'.woff': 'font/woff',
|
|
164
|
+
'.woff2': 'font/woff2',
|
|
165
|
+
'.ttf': 'font/ttf',
|
|
166
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const server = createServer((req, res) => {
|
|
170
|
+
// Decode URL-encoded characters (e.g., %20 -> space) so filenames with spaces resolve correctly
|
|
171
|
+
const decodedUrl = decodeURIComponent(req.url);
|
|
172
|
+
let filePath = join(SERVE_DIR, decodedUrl === '/' ? HTML_FILE : decodedUrl);
|
|
173
|
+
try {
|
|
174
|
+
const content = readFileSync(filePath);
|
|
175
|
+
const ext = extname(filePath).toLowerCase();
|
|
176
|
+
res.writeHead(200, { 'Content-Type': MIME_TYPES[ext] || 'application/octet-stream' });
|
|
177
|
+
res.end(content);
|
|
178
|
+
} catch {
|
|
179
|
+
res.writeHead(404);
|
|
180
|
+
res.end('Not found');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Find a free port
|
|
185
|
+
const port = await new Promise((resolve) => {
|
|
186
|
+
server.listen(0, () => resolve(server.address().port));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
console.log(` Local server on port ${port}`);
|
|
190
|
+
|
|
191
|
+
// --- Screenshot each slide ---
|
|
192
|
+
|
|
193
|
+
const browser = await chromium.launch();
|
|
194
|
+
const page = await browser.newPage({
|
|
195
|
+
viewport: { width: VP_WIDTH, height: VP_HEIGHT },
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Load the presentation
|
|
199
|
+
await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle' });
|
|
200
|
+
|
|
201
|
+
// Wait for fonts to load
|
|
202
|
+
await page.evaluate(() => document.fonts.ready);
|
|
203
|
+
|
|
204
|
+
// Extra wait for animations to settle on the first slide
|
|
205
|
+
await page.waitForTimeout(1500);
|
|
206
|
+
|
|
207
|
+
// Count slides
|
|
208
|
+
const slideCount = await page.evaluate(() => {
|
|
209
|
+
return document.querySelectorAll('.slide').length;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
console.log(` Found ${slideCount} slides`);
|
|
213
|
+
|
|
214
|
+
if (slideCount === 0) {
|
|
215
|
+
console.error(' ERROR: No .slide elements found in the presentation.');
|
|
216
|
+
console.error(' Make sure your HTML uses <div class="slide"> or <section class="slide">.');
|
|
217
|
+
await browser.close();
|
|
218
|
+
server.close();
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Screenshot each slide
|
|
223
|
+
mkdirSync(SCREENSHOT_DIR, { recursive: true });
|
|
224
|
+
const screenshotPaths = [];
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < slideCount; i++) {
|
|
227
|
+
// Navigate to slide by simulating the presentation's navigation
|
|
228
|
+
// Most frontend-slides presentations use a currentSlide index and show/hide
|
|
229
|
+
await page.evaluate((index) => {
|
|
230
|
+
const slides = document.querySelectorAll('.slide');
|
|
231
|
+
|
|
232
|
+
// Try multiple navigation strategies used by frontend-slides:
|
|
233
|
+
|
|
234
|
+
// Strategy 1: Direct slide manipulation (most common in generated decks)
|
|
235
|
+
slides.forEach((slide, idx) => {
|
|
236
|
+
if (idx === index) {
|
|
237
|
+
slide.style.display = '';
|
|
238
|
+
slide.style.opacity = '1';
|
|
239
|
+
slide.style.visibility = 'visible';
|
|
240
|
+
slide.style.position = 'relative';
|
|
241
|
+
slide.style.transform = 'none';
|
|
242
|
+
slide.classList.add('active');
|
|
243
|
+
} else {
|
|
244
|
+
slide.style.display = 'none';
|
|
245
|
+
slide.classList.remove('active');
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Strategy 2: If there's a SlidePresentation class instance, use it
|
|
250
|
+
if (window.presentation && typeof window.presentation.goToSlide === 'function') {
|
|
251
|
+
window.presentation.goToSlide(index);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Strategy 3: Scroll-based (some decks use scroll snapping)
|
|
255
|
+
slides[index]?.scrollIntoView({ behavior: 'instant' });
|
|
256
|
+
}, i);
|
|
257
|
+
|
|
258
|
+
// Wait for any slide transition animations to finish
|
|
259
|
+
await page.waitForTimeout(300);
|
|
260
|
+
|
|
261
|
+
// Wait for intersection observer animations to trigger
|
|
262
|
+
await page.waitForTimeout(200);
|
|
263
|
+
|
|
264
|
+
// Force all .reveal elements on the current slide to be visible
|
|
265
|
+
// (animations normally trigger on scroll/intersection, but we need them visible now)
|
|
266
|
+
await page.evaluate((index) => {
|
|
267
|
+
const slides = document.querySelectorAll('.slide');
|
|
268
|
+
const currentSlide = slides[index];
|
|
269
|
+
if (currentSlide) {
|
|
270
|
+
currentSlide.querySelectorAll('.reveal').forEach(el => {
|
|
271
|
+
el.style.opacity = '1';
|
|
272
|
+
el.style.transform = 'none';
|
|
273
|
+
el.style.visibility = 'visible';
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}, i);
|
|
277
|
+
|
|
278
|
+
await page.waitForTimeout(100);
|
|
279
|
+
|
|
280
|
+
const screenshotPath = join(SCREENSHOT_DIR, `slide-${String(i + 1).padStart(3, '0')}.png`);
|
|
281
|
+
await page.screenshot({ path: screenshotPath, fullPage: false });
|
|
282
|
+
screenshotPaths.push(screenshotPath);
|
|
283
|
+
console.log(` Captured slide ${i + 1}/${slideCount}`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await browser.close();
|
|
287
|
+
server.close();
|
|
288
|
+
|
|
289
|
+
// --- Combine screenshots into PDF ---
|
|
290
|
+
// Use a second Playwright page to generate a PDF from the screenshots
|
|
291
|
+
|
|
292
|
+
console.log(' Assembling PDF...');
|
|
293
|
+
|
|
294
|
+
const browser2 = await chromium.launch();
|
|
295
|
+
const pdfPage = await browser2.newPage();
|
|
296
|
+
|
|
297
|
+
// Build an HTML page with all screenshots, one per page
|
|
298
|
+
const imagesHtml = screenshotPaths.map((p) => {
|
|
299
|
+
const imgData = readFileSync(p).toString('base64');
|
|
300
|
+
return `<div class="page"><img src="data:image/png;base64,${imgData}" /></div>`;
|
|
301
|
+
}).join('\n');
|
|
302
|
+
|
|
303
|
+
const pdfHtml = `<!DOCTYPE html>
|
|
304
|
+
<html>
|
|
305
|
+
<head>
|
|
306
|
+
<style>
|
|
307
|
+
* { margin: 0; padding: 0; }
|
|
308
|
+
@page { size: ${VP_WIDTH}px ${VP_HEIGHT}px; margin: 0; }
|
|
309
|
+
.page {
|
|
310
|
+
width: ${VP_WIDTH}px;
|
|
311
|
+
height: ${VP_HEIGHT}px;
|
|
312
|
+
page-break-after: always;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
}
|
|
315
|
+
.page:last-child { page-break-after: auto; }
|
|
316
|
+
img {
|
|
317
|
+
width: ${VP_WIDTH}px;
|
|
318
|
+
height: ${VP_HEIGHT}px;
|
|
319
|
+
display: block;
|
|
320
|
+
object-fit: contain;
|
|
321
|
+
}
|
|
322
|
+
</style>
|
|
323
|
+
</head>
|
|
324
|
+
<body>${imagesHtml}</body>
|
|
325
|
+
</html>`;
|
|
326
|
+
|
|
327
|
+
await pdfPage.setContent(pdfHtml, { waitUntil: 'load' });
|
|
328
|
+
await pdfPage.pdf({
|
|
329
|
+
path: OUTPUT_PDF,
|
|
330
|
+
width: `${VP_WIDTH}px`,
|
|
331
|
+
height: `${VP_HEIGHT}px`,
|
|
332
|
+
printBackground: true,
|
|
333
|
+
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
await browser2.close();
|
|
337
|
+
|
|
338
|
+
// Clean up screenshots
|
|
339
|
+
screenshotPaths.forEach(p => unlinkSync(p));
|
|
340
|
+
|
|
341
|
+
console.log(` OK: PDF saved to: ${OUTPUT_PDF}`);
|
|
342
|
+
EXPORT_SCRIPT
|
|
343
|
+
|
|
344
|
+
# --- Step 3: Install Playwright in temp directory ---
|
|
345
|
+
# We install Playwright locally in the temp dir so the Node script can import it.
|
|
346
|
+
# This avoids polluting global packages and ensures the script is self-contained.
|
|
347
|
+
|
|
348
|
+
info "Setting up Playwright (headless browser for screenshots)..."
|
|
349
|
+
info "This may take a moment on first run..."
|
|
350
|
+
echo ""
|
|
351
|
+
|
|
352
|
+
cd "$TEMP_DIR"
|
|
353
|
+
|
|
354
|
+
# Create a minimal package.json so npm install works
|
|
355
|
+
cat > "$TEMP_DIR/package.json" << 'PKG'
|
|
356
|
+
{ "name": "slide-export", "private": true, "type": "module" }
|
|
357
|
+
PKG
|
|
358
|
+
|
|
359
|
+
# Install Playwright into the temp directory
|
|
360
|
+
npm install playwright &>/dev/null || {
|
|
361
|
+
err "Failed to install Playwright."
|
|
362
|
+
err "Try running: npm install playwright"
|
|
363
|
+
rm -rf "$TEMP_DIR"
|
|
364
|
+
exit 1
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# Ensure Chromium browser binary is downloaded
|
|
368
|
+
npx playwright install chromium 2>/dev/null || {
|
|
369
|
+
err "Failed to install Chromium browser for Playwright."
|
|
370
|
+
err "Try running manually: npx playwright install chromium"
|
|
371
|
+
rm -rf "$TEMP_DIR"
|
|
372
|
+
exit 1
|
|
373
|
+
}
|
|
374
|
+
ok "Playwright ready"
|
|
375
|
+
echo ""
|
|
376
|
+
|
|
377
|
+
# --- Step 4: Run the export ---
|
|
378
|
+
|
|
379
|
+
SCREENSHOT_DIR="$TEMP_DIR/screenshots"
|
|
380
|
+
|
|
381
|
+
info "Exporting slides to PDF..."
|
|
382
|
+
echo ""
|
|
383
|
+
|
|
384
|
+
# Run from the temp dir so Node can find the locally-installed playwright
|
|
385
|
+
if [[ "$COMPACT" == "true" ]]; then
|
|
386
|
+
info "Using compact mode (1280x720) for smaller file size"
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
node "$TEMP_SCRIPT" "$SERVE_DIR" "$HTML_FILENAME" "$OUTPUT_PDF" "$SCREENSHOT_DIR" "$VIEWPORT_W" "$VIEWPORT_H" || {
|
|
390
|
+
err "PDF export failed."
|
|
391
|
+
rm -rf "$TEMP_DIR"
|
|
392
|
+
exit 1
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# --- Step 5: Cleanup and success ---
|
|
396
|
+
|
|
397
|
+
rm -rf "$TEMP_DIR"
|
|
398
|
+
|
|
399
|
+
echo ""
|
|
400
|
+
echo -e "${BOLD}========================================${NC}"
|
|
401
|
+
ok "PDF exported successfully!"
|
|
402
|
+
echo ""
|
|
403
|
+
echo -e " ${BOLD}File:${NC} $OUTPUT_PDF"
|
|
404
|
+
echo ""
|
|
405
|
+
FILE_SIZE=$(du -h "$OUTPUT_PDF" | cut -f1 | xargs)
|
|
406
|
+
echo " Size: $FILE_SIZE"
|
|
407
|
+
echo ""
|
|
408
|
+
echo " This PDF works everywhere - email, Slack, Notion, print."
|
|
409
|
+
echo " Note: Animations are not preserved (it's a static export)."
|
|
410
|
+
echo -e "${BOLD}========================================${NC}"
|
|
411
|
+
echo ""
|
|
412
|
+
|
|
413
|
+
# Open the PDF automatically
|
|
414
|
+
if command -v open &>/dev/null; then
|
|
415
|
+
open "$OUTPUT_PDF"
|
|
416
|
+
elif command -v xdg-open &>/dev/null; then
|
|
417
|
+
xdg-open "$OUTPUT_PDF"
|
|
418
|
+
fi
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Extract all content from a PowerPoint file (.pptx).
|
|
4
|
+
Returns a JSON structure with slides, text, and images.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python extract-pptx.py <input.pptx> [output_dir]
|
|
8
|
+
|
|
9
|
+
Requires: pip install python-pptx
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
from pptx import Presentation
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def extract_pptx(file_path, output_dir="."):
|
|
19
|
+
"""
|
|
20
|
+
Extract all content from a PowerPoint file.
|
|
21
|
+
Returns a list of slide data dicts with text, images, and notes.
|
|
22
|
+
"""
|
|
23
|
+
prs = Presentation(file_path)
|
|
24
|
+
slides_data = []
|
|
25
|
+
|
|
26
|
+
# Create assets directory for extracted images
|
|
27
|
+
assets_dir = os.path.join(output_dir, "assets")
|
|
28
|
+
os.makedirs(assets_dir, exist_ok=True)
|
|
29
|
+
|
|
30
|
+
for slide_num, slide in enumerate(prs.slides):
|
|
31
|
+
slide_data = {
|
|
32
|
+
"number": slide_num + 1,
|
|
33
|
+
"title": "",
|
|
34
|
+
"content": [],
|
|
35
|
+
"images": [],
|
|
36
|
+
"notes": "",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for shape in slide.shapes:
|
|
40
|
+
# Extract text content
|
|
41
|
+
if shape.has_text_frame:
|
|
42
|
+
if shape == slide.shapes.title:
|
|
43
|
+
slide_data["title"] = shape.text
|
|
44
|
+
else:
|
|
45
|
+
slide_data["content"].append(
|
|
46
|
+
{"type": "text", "content": shape.text}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Extract images
|
|
50
|
+
if shape.shape_type == 13: # Picture type
|
|
51
|
+
image = shape.image
|
|
52
|
+
image_bytes = image.blob
|
|
53
|
+
image_ext = image.ext
|
|
54
|
+
image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
|
|
55
|
+
image_path = os.path.join(assets_dir, image_name)
|
|
56
|
+
|
|
57
|
+
with open(image_path, "wb") as f:
|
|
58
|
+
f.write(image_bytes)
|
|
59
|
+
|
|
60
|
+
slide_data["images"].append(
|
|
61
|
+
{
|
|
62
|
+
"path": f"assets/{image_name}",
|
|
63
|
+
"width": shape.width,
|
|
64
|
+
"height": shape.height,
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Extract speaker notes
|
|
69
|
+
if slide.has_notes_slide:
|
|
70
|
+
notes_frame = slide.notes_slide.notes_text_frame
|
|
71
|
+
slide_data["notes"] = notes_frame.text
|
|
72
|
+
|
|
73
|
+
slides_data.append(slide_data)
|
|
74
|
+
|
|
75
|
+
return slides_data
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
if len(sys.argv) < 2:
|
|
80
|
+
print("Usage: python extract-pptx.py <input.pptx> [output_dir]")
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
|
|
83
|
+
input_file = sys.argv[1]
|
|
84
|
+
output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
|
|
85
|
+
|
|
86
|
+
slides = extract_pptx(input_file, output_dir)
|
|
87
|
+
|
|
88
|
+
# Write extracted data as JSON
|
|
89
|
+
output_path = os.path.join(output_dir, "extracted-slides.json")
|
|
90
|
+
with open(output_path, "w") as f:
|
|
91
|
+
json.dump(slides, f, indent=2)
|
|
92
|
+
|
|
93
|
+
print(f"Extracted {len(slides)} slides to {output_path}")
|
|
94
|
+
for s in slides:
|
|
95
|
+
img_count = len(s["images"])
|
|
96
|
+
print(f" Slide {s['number']}: {s['title'] or '(no title)'} — {img_count} image(s)")
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/* ===========================================
|
|
2
|
+
VIEWPORT FITTING: MANDATORY BASE STYLES
|
|
3
|
+
Include this ENTIRE file in every presentation.
|
|
4
|
+
These styles ensure slides fit exactly in the viewport.
|
|
5
|
+
=========================================== */
|
|
6
|
+
|
|
7
|
+
/* 1. Lock html/body to viewport */
|
|
8
|
+
html, body {
|
|
9
|
+
height: 100%;
|
|
10
|
+
overflow-x: hidden;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
html {
|
|
14
|
+
scroll-snap-type: y mandatory;
|
|
15
|
+
scroll-behavior: smooth;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* 2. Each slide = exact viewport height */
|
|
19
|
+
.slide {
|
|
20
|
+
width: 100vw;
|
|
21
|
+
height: 100vh;
|
|
22
|
+
height: 100dvh; /* Dynamic viewport height for mobile browsers */
|
|
23
|
+
overflow: hidden; /* CRITICAL: Prevent ANY overflow */
|
|
24
|
+
scroll-snap-align: start;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
position: relative;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* 3. Content container with flex for centering */
|
|
31
|
+
.slide-content {
|
|
32
|
+
flex: 1;
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
max-height: 100%;
|
|
37
|
+
overflow: hidden; /* Double-protection against overflow */
|
|
38
|
+
padding: var(--slide-padding);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* 4. ALL typography uses clamp() for responsive scaling */
|
|
42
|
+
:root {
|
|
43
|
+
/* Titles scale from mobile to desktop */
|
|
44
|
+
--title-size: clamp(1.5rem, 5vw, 4rem);
|
|
45
|
+
--h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
|
|
46
|
+
--h3-size: clamp(1rem, 2.5vw, 1.75rem);
|
|
47
|
+
|
|
48
|
+
/* Body text */
|
|
49
|
+
--body-size: clamp(0.75rem, 1.5vw, 1.125rem);
|
|
50
|
+
--small-size: clamp(0.65rem, 1vw, 0.875rem);
|
|
51
|
+
|
|
52
|
+
/* Spacing scales with viewport */
|
|
53
|
+
--slide-padding: clamp(1rem, 4vw, 4rem);
|
|
54
|
+
--content-gap: clamp(0.5rem, 2vw, 2rem);
|
|
55
|
+
--element-gap: clamp(0.25rem, 1vw, 1rem);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* 5. Cards/containers use viewport-relative max sizes */
|
|
59
|
+
.card, .container, .content-box {
|
|
60
|
+
max-width: min(90vw, 1000px);
|
|
61
|
+
max-height: min(80vh, 700px);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* 6. Lists auto-scale with viewport */
|
|
65
|
+
.feature-list, .bullet-list {
|
|
66
|
+
gap: clamp(0.4rem, 1vh, 1rem);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.feature-list li, .bullet-list li {
|
|
70
|
+
font-size: var(--body-size);
|
|
71
|
+
line-height: 1.4;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* 7. Grids adapt to available space */
|
|
75
|
+
.grid {
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
|
|
78
|
+
gap: clamp(0.5rem, 1.5vw, 1rem);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* 8. Images constrained to viewport */
|
|
82
|
+
img, .image-container {
|
|
83
|
+
max-width: 100%;
|
|
84
|
+
max-height: min(50vh, 400px);
|
|
85
|
+
object-fit: contain;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ===========================================
|
|
89
|
+
RESPONSIVE BREAKPOINTS
|
|
90
|
+
Aggressive scaling for smaller viewports
|
|
91
|
+
=========================================== */
|
|
92
|
+
|
|
93
|
+
/* Short viewports (< 700px height) */
|
|
94
|
+
@media (max-height: 700px) {
|
|
95
|
+
:root {
|
|
96
|
+
--slide-padding: clamp(0.75rem, 3vw, 2rem);
|
|
97
|
+
--content-gap: clamp(0.4rem, 1.5vw, 1rem);
|
|
98
|
+
--title-size: clamp(1.25rem, 4.5vw, 2.5rem);
|
|
99
|
+
--h2-size: clamp(1rem, 3vw, 1.75rem);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Very short viewports (< 600px height) */
|
|
104
|
+
@media (max-height: 600px) {
|
|
105
|
+
:root {
|
|
106
|
+
--slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
|
|
107
|
+
--content-gap: clamp(0.3rem, 1vw, 0.75rem);
|
|
108
|
+
--title-size: clamp(1.1rem, 4vw, 2rem);
|
|
109
|
+
--body-size: clamp(0.7rem, 1.2vw, 0.95rem);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Hide non-essential elements */
|
|
113
|
+
.nav-dots, .keyboard-hint, .decorative {
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Extremely short (landscape phones, < 500px height) */
|
|
119
|
+
@media (max-height: 500px) {
|
|
120
|
+
:root {
|
|
121
|
+
--slide-padding: clamp(0.4rem, 2vw, 1rem);
|
|
122
|
+
--title-size: clamp(1rem, 3.5vw, 1.5rem);
|
|
123
|
+
--h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
|
|
124
|
+
--body-size: clamp(0.65rem, 1vw, 0.85rem);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Narrow viewports (< 600px width) */
|
|
129
|
+
@media (max-width: 600px) {
|
|
130
|
+
:root {
|
|
131
|
+
--title-size: clamp(1.25rem, 7vw, 2.5rem);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Stack grids vertically */
|
|
135
|
+
.grid {
|
|
136
|
+
grid-template-columns: 1fr;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ===========================================
|
|
141
|
+
REDUCED MOTION
|
|
142
|
+
Respect user preferences
|
|
143
|
+
=========================================== */
|
|
144
|
+
@media (prefers-reduced-motion: reduce) {
|
|
145
|
+
*, *::before, *::after {
|
|
146
|
+
animation-duration: 0.01ms !important;
|
|
147
|
+
transition-duration: 0.2s !important;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
html {
|
|
151
|
+
scroll-behavior: auto;
|
|
152
|
+
}
|
|
153
|
+
}
|