smartpipe-cli 1.4.0rc2__tar.gz → 1.4.0rc3__tar.gz
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.
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/CHANGELOG.md +45 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/CITATION.cff +1 -1
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/CONTRIBUTING.md +6 -5
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/PKG-INFO +32 -11
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/README.md +30 -10
- smartpipe_cli-1.4.0rc3/demo/video/README.md +23 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/Main.tsx +26 -7
- smartpipe_cli-1.4.0rc3/demo/video/src/Narration.tsx +94 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/Root.tsx +17 -2
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/Background.tsx +3 -10
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/Terminal.tsx +69 -4
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/config.ts +10 -6
- smartpipe_cli-1.4.0rc3/demo/video/src/narration/durations.json +13 -0
- smartpipe_cli-1.4.0rc3/demo/video/src/narration/lines.json +13 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/scenes/Close.tsx +1 -2
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/scenes/CostHonesty.tsx +27 -42
- smartpipe_cli-1.4.0rc3/demo/video/src/scenes/Graph.tsx +236 -0
- smartpipe_cli-1.4.0rc3/demo/video/src/scenes/Hook.tsx +47 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/scenes/Multimodal.tsx +150 -31
- smartpipe_cli-1.4.0rc3/demo/video/src/scenes/ScaleA.tsx +43 -0
- smartpipe_cli-1.4.0rc3/demo/video/src/scenes/ScaleB.tsx +42 -0
- smartpipe_cli-1.4.0rc3/docs/assets/demo-thumb.jpg +0 -0
- smartpipe_cli-1.4.0rc3/docs/assets/graph-hero.png +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/models-and-providers.md +17 -3
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/README.md +3 -2
- smartpipe_cli-1.4.0rc3/docs/cookbook/knowledge-graph.md +131 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/demo/index.html +1 -1
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/index.md +8 -6
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/privacy.md +24 -10
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/reference/cli.md +34 -9
- smartpipe_cli-1.4.0rc3/docs/verbs/graph.md +217 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/mkdocs.yml +8 -1
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/pyproject.toml +4 -1
- smartpipe_cli-1.4.0rc3/src/smartpipe/cli/auth_cmd.py +268 -0
- smartpipe_cli-1.4.0rc3/src/smartpipe/cli/config_cmd.py +1205 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/doctor_cmd.py +2 -1
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/root.py +1 -1
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/screens.py +1 -0
- smartpipe_cli-1.4.0rc3/src/smartpipe/config/authflow.py +202 -0
- smartpipe_cli-1.4.0rc3/src/smartpipe/config/credentials.py +241 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/display.py +38 -16
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/doctor.py +21 -19
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/picker.py +263 -13
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/state_cache.py +50 -3
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/store.py +31 -2
- smartpipe_cli-1.4.0rc3/src/smartpipe/config/verify.py +288 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/container.py +3 -1
- smartpipe_cli-1.4.0rc3/src/smartpipe/io/richui.py +112 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/catalogs.py +62 -7
- smartpipe_cli-1.4.0rc3/src/smartpipe/models/keycheck.py +108 -0
- smartpipe_cli-1.4.0rc2/demo/video/README.md +0 -17
- smartpipe_cli-1.4.0rc2/demo/video/src/scenes/Hook.tsx +0 -40
- smartpipe_cli-1.4.0rc2/demo/video/src/scenes/Scale.tsx +0 -47
- smartpipe_cli-1.4.0rc2/docs/assets/demo-thumb.jpg +0 -0
- smartpipe_cli-1.4.0rc2/src/smartpipe/cli/auth_cmd.py +0 -78
- smartpipe_cli-1.4.0rc2/src/smartpipe/cli/config_cmd.py +0 -564
- smartpipe_cli-1.4.0rc2/src/smartpipe/config/credentials.py +0 -118
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/.gitignore +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/LICENSE +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/NOTICE +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/Caption.tsx +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/FileGlyph.tsx +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/SceneFrame.tsx +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/components/Wordmark.tsx +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/font.ts +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/index.ts +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/demo/video/src/scenes/ColdOpen.tsx +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/comparison.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/feeding-smartpipe.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/output-formats.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/pipes-and-items.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/structured-output.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/concepts/the-item.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/contract-extraction.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/customer-feedback.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/invoice-reconciliation.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/live-monitoring.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/log-triage.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/meeting-digest.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/ranking-documents.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/stream-enrichment.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/training-data-prep.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/video-qa.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/cookbook/visualizing-results.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/inputs/files.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/install.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/1-first-pipeline.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/2-structured-data.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/3-files-and-media.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/4-free-verbs.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/5-scale-and-cost.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/learn/6-pipelines-that-last.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/quickstart.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/reference/agents.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/reference/custom-verbs.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/reference/sem-files.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/troubleshooting.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/cluster.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/diff.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/distinct.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/embed.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/extend.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/filter.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/getschema.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/join.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/map.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/outliers.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/readable.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/reduce.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/sample.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/sort.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/split.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/summarize.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/top-k.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/docs/verbs/where.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/qa/README.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/qa/fixtures/README.md +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/__main__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/assets/probe.png +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/assets/probe.txt +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/assets/probe.wav +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/cache_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/chart_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/cite_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/cluster_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/completions.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/diff_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/distinct_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/echo_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/embed_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/extend_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/filter_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/getschema_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/graph_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/input_options.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/interrupts.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/join_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/map_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/outliers_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/probe_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/read_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/readable_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/reduce_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/run_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/sample_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/schema_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/schema_workshop.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/sem_file.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/sort_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/split_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/summarize_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/top_k_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/update_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/usage_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/where_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/cli/write_cmd.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/config/paths.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/core/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/core/errors.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/core/install_channel.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/core/jsontools.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/core/versions.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/aggregate.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/blocking.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/chart.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/chunking.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/clustering.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/graphkg.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/graphout.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/predicate.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/preview.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/progressbar.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/prompts.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/ranking.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/repair.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/runner.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/schema.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/schema_dsl.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/schema_repl.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/tally.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/temporal.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/timebin.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/units.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/engine/windows.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/arrow_menu.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/csvrows.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/diagnostics.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/inputs.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/items.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/leaderboard.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/metering.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/preview.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/progress.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/readers.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/render.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/text.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/tty.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/update_check.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/usage.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/io/writers.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/anthropic_adapter.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/base.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/budget.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/cache.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/gemini_native.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/http_support.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/jina.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/local_embed.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/local_ner.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/ocr.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/ollama.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/openai_codex.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/openai_compat.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/openai_oauth.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/resolve.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/retry.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/stt.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/models/windows.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/parsing/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/parsing/detect.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/parsing/extract.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/py.typed +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/__init__.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/chart.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/cluster.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/common.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/convert.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/diff.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/distinct.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/embed.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/extend.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/filter.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/getschema.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/graph.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/graphfull.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/join.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/map.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/outliers.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/oversize.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/readable.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/reduce.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/sample.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/sortverb.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/split.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/summarize.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/top_k.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/where.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/src/smartpipe/verbs/write.py +0 -0
- {smartpipe_cli-1.4.0rc2 → smartpipe_cli-1.4.0rc3}/tests/corpus/README.md +0 -0
|
@@ -5,11 +5,20 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
|
|
8
9
|
## [1.4.0] — 2026-07-08
|
|
9
10
|
|
|
10
11
|
The identity release.
|
|
11
12
|
|
|
12
13
|
### Knowledge graphs (the highlight)
|
|
14
|
+
- **The showcase**: a full verb page with the modality matrix and a
|
|
15
|
+
live-rendering Mermaid sample, a cookbook chapter written from real
|
|
16
|
+
runs against the downloadable playground corpus, the README highlight
|
|
17
|
+
with a screenshot of the case graph, agent SKILL recipes, and `graph`
|
|
18
|
+
on the welcome screen. Found en route: the playground download in the
|
|
19
|
+
docs pointed at the source tarball (no media inside) - all three
|
|
20
|
+
sites now fetch the actual 26 MB release asset.
|
|
21
|
+
|
|
13
22
|
- **`smartpipe graph --fast` turns a corpus into a knowledge graph for
|
|
14
23
|
exactly $0.** A local zero-shot NER model (GLiNER-small over ONNX -
|
|
15
24
|
no new dependencies, no torch, a one-time ~190 MB download like the
|
|
@@ -256,6 +265,42 @@ The identity release.
|
|
|
256
265
|
media-embedding capability.
|
|
257
266
|
|
|
258
267
|
### Setup that reads the room
|
|
268
|
+
- **Rich now backs `config show` and `doctor`.** It is a deliberate core
|
|
269
|
+
dependency under the no-extras policy, imported only while those human-facing
|
|
270
|
+
screens render so `--help`, pipes, and `NO_COLOR` keep their existing contracts.
|
|
271
|
+
- **The interactive config wizard can go backward.** Model, embedding, OCR, and
|
|
272
|
+
final review screens expose explicit Back rows; returning to a stage restores
|
|
273
|
+
its pre-stage checkpoint, and typed model prompts accept `back`, `b`, `no`, or
|
|
274
|
+
`n` instead of accidentally saving `ollama/n`.
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
- **Discarding config is terminal and interruption is ordinary.** Choosing
|
|
278
|
+
discard no longer opens the shell-completion prompt after saying "Not saved";
|
|
279
|
+
Ctrl-C or EOF at any Click prompt exits 130 instead of showing the internal-bug
|
|
280
|
+
screen.
|
|
281
|
+
|
|
282
|
+
- **`auth login` now connects every provider, not just ChatGPT.** Pick
|
|
283
|
+
from the full list - OpenAI appears twice because it really is two
|
|
284
|
+
wires (API key vs ChatGPT login, and the login wire has no
|
|
285
|
+
embeddings) - paste your key at a masked prompt after the create-a-key
|
|
286
|
+
URL, and it validates live before storing (with an honest
|
|
287
|
+
"store anyway - the provider may be having a bad minute" escape).
|
|
288
|
+
Keys live at `~/.local/share/smartpipe/auth.json`, owner-only
|
|
289
|
+
permissions from the first byte, masked everywhere they surface, and
|
|
290
|
+
the environment variable always wins. `auth list` shows what's
|
|
291
|
+
connected and which source is live; `auth logout` removes.
|
|
292
|
+
- **Setup walks three questions in order: text model, embeddings,
|
|
293
|
+
document OCR.** Every stage shows every provider - connected ones
|
|
294
|
+
badged, unconnected ones connectable inline without leaving the flow.
|
|
295
|
+
The embedding stage only lists wires that embed; OCR is optional and
|
|
296
|
+
skippable in one keypress. At the end, one consented verification
|
|
297
|
+
pass (~5 tiny requests) probes what your models can actually do -
|
|
298
|
+
and if the basic text check fails, smartpipe reports a setup fault
|
|
299
|
+
instead of pretending your model lacks modalities. Results feed the
|
|
300
|
+
capability chips you see in every picker from then on, alongside a
|
|
301
|
+
public model registry and your own declarations for self-hosted
|
|
302
|
+
models.
|
|
303
|
+
|
|
259
304
|
- **The schema workshop.** `smartpipe schema` with no arguments at a
|
|
260
305
|
terminal opens a small interactive draft loop - your schema pinned up
|
|
261
306
|
top as colored braces, commands scrolling below: `/add name type`,
|
|
@@ -9,7 +9,7 @@ Requires [uv](https://docs.astral.sh/uv/) and Python ≥ 3.11 (uv can fetch one
|
|
|
9
9
|
|
|
10
10
|
```console
|
|
11
11
|
$ git clone https://github.com/prabal-rje/smartpipe && cd smartpipe
|
|
12
|
-
$ uv sync --all-extras # creates .venv with
|
|
12
|
+
$ uv sync --all-extras # creates .venv with the project and dev tools
|
|
13
13
|
$ uv run smartpipe # welcome screen = working install
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -64,12 +64,13 @@ Reverting to built-in search is emptying the three values again.
|
|
|
64
64
|
|
|
65
65
|
## New dependencies
|
|
66
66
|
|
|
67
|
-
Core install weight is a feature
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
Core install weight is a feature, but D46 deliberately ships the complete
|
|
68
|
+
multimodal surface in core: there are no optional extras. The frozen dependency
|
|
69
|
+
snapshot makes every direct or transitive addition an explicit review decision.
|
|
70
|
+
Heavy imports stay function-local so startup remains budgeted.
|
|
70
71
|
|
|
71
72
|
**New heavy import? Function-local or justify.** `--help` must never import
|
|
72
|
-
`httpx`, `jsonschema`, `anthropic`, or `
|
|
73
|
+
`httpx`, `jsonschema`, `anthropic`, `markitdown`, or `rich` —
|
|
73
74
|
`tests/test_startup_imports.py` is the enforcement (it runs `-X importtime` and
|
|
74
75
|
fails on any banned module). `make startup` gives an advisory wall-clock number;
|
|
75
76
|
the import test is the deterministic gate.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smartpipe-cli
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.0rc3
|
|
4
4
|
Summary: Semantic pipes and queries for your terminal - PDFs, images, audio, video, and text through Unix verbs that understand.
|
|
5
5
|
Project-URL: Homepage, https://github.com/prabal-rje/smartpipe
|
|
6
6
|
Project-URL: Changelog, https://github.com/prabal-rje/smartpipe/blob/main/CHANGELOG.md
|
|
@@ -32,6 +32,7 @@ Requires-Dist: matplotlib>=3.8
|
|
|
32
32
|
Requires-Dist: pdfminer-six>=20231228
|
|
33
33
|
Requires-Dist: plotext>=5
|
|
34
34
|
Requires-Dist: pypdf>=5.0
|
|
35
|
+
Requires-Dist: rich>=13.9
|
|
35
36
|
Requires-Dist: tomli-w>=1.0
|
|
36
37
|
Description-Content-Type: text/markdown
|
|
37
38
|
|
|
@@ -48,9 +49,9 @@ Description-Content-Type: text/markdown
|
|
|
48
49
|
Run PDFs, images, audio, video, and text through Unix verbs that understand
|
|
49
50
|
their input.
|
|
50
51
|
|
|
51
|
-
[](https://prabal-rje.github.io/smartpipe/demo/)
|
|
52
53
|
|
|
53
|
-
*[▶ Watch the full
|
|
54
|
+
*[▶ Watch the full 86-second demo](https://prabal-rje.github.io/smartpipe/demo/) - sound, controls, no download.*
|
|
54
55
|
|
|
55
56
|
## Install
|
|
56
57
|
|
|
@@ -139,6 +140,7 @@ into ordinary Unix tools. Every verb is documented at
|
|
|
139
140
|
| [`distinct`][distinct] | fold near-duplicates; `--exact` is free | `sort -u`, by meaning |
|
|
140
141
|
| [`diff`][diff] | what distinguishes two sets | the post-incident answer |
|
|
141
142
|
| [`outliers`][outliers] | the items least like the rest | novelty, surfaced |
|
|
143
|
+
| [`graph`][graph] | corpus → entity/relationship graph; `--fast` is free | the case wall, with citations |
|
|
142
144
|
|
|
143
145
|
**Free verbs** never call a model. Run them first to cut the corpus before any
|
|
144
146
|
paid stage:
|
|
@@ -154,9 +156,10 @@ paid stage:
|
|
|
154
156
|
| [`chart`][cli] | terminal bars, SVG, facets, time series | quick plots |
|
|
155
157
|
|
|
156
158
|
Some semantic verbs have a **conditionally free mode**: `join --on` (key
|
|
157
|
-
equality, no prompt), `distinct --exact` (hash-only folding), `
|
|
158
|
-
`--dry-run` (compose without sending),
|
|
159
|
-
braces/DSL expression. Each stays at zero model
|
|
159
|
+
equality, no prompt), `distinct --exact` (hash-only folding), `graph --fast`
|
|
160
|
+
(local NER, on-device), `map`/`extend` `--dry-run` (compose without sending),
|
|
161
|
+
and `smartpipe schema` with a braces/DSL expression. Each stays at zero model
|
|
162
|
+
calls by construction.
|
|
160
163
|
|
|
161
164
|
## A one-minute tour
|
|
162
165
|
|
|
@@ -192,6 +195,9 @@ smartpipe map "Extract {vendor string, invoice_number string, total number}" 'in
|
|
|
192
195
|
# 8. video RAG, no vector database: index a folder of recordings once, ask any day
|
|
193
196
|
smartpipe embed 'sessions/**/*.mp4' > sessions.embeddings
|
|
194
197
|
smartpipe top_k 3 --near "user gives up after the coupon fails" < sessions.embeddings
|
|
198
|
+
|
|
199
|
+
# 9. the corpus as a knowledge graph - $0: local NER, zero model calls
|
|
200
|
+
smartpipe graph --fast 'case-files/*.md' --save case.html
|
|
195
201
|
```
|
|
196
202
|
|
|
197
203
|
A `.txt` on a pipe arrives one item per line; `--as file` treats the whole
|
|
@@ -206,6 +212,18 @@ Numbers 7 and 8 are full recipes -
|
|
|
206
212
|
New to this? The [Learn track][quickstart] starts at zero and assumes nothing, including
|
|
207
213
|
what a "model" is.
|
|
208
214
|
|
|
215
|
+
### Highlight: the knowledge graph
|
|
216
|
+
|
|
217
|
+
Number 9 is new: `graph --fast` turns a folder of mixed files into an
|
|
218
|
+
interactive, **cited** knowledge graph without a single model call - a local
|
|
219
|
+
NER model finds the entities you name, co-occurrence weights the edges, and
|
|
220
|
+
hovering any edge shows the files behind it. A focus prompt upgrades the
|
|
221
|
+
strongest edges to model-read relations. Full story:
|
|
222
|
+
[the `graph` page](https://prabal-rje.github.io/smartpipe/verbs/graph/) and
|
|
223
|
+
[the cookbook recipe](docs/cookbook/knowledge-graph.md).
|
|
224
|
+
|
|
225
|
+

|
|
226
|
+
|
|
209
227
|
## Try it on real files
|
|
210
228
|
|
|
211
229
|
No corpus handy? [smartpipe-playground][playground] ships 26 MB of CC0 /
|
|
@@ -213,8 +231,8 @@ public-domain practice files - invoices, reports, photos, recordings, screen
|
|
|
213
231
|
sessions, and JSONL data:
|
|
214
232
|
|
|
215
233
|
```bash
|
|
216
|
-
curl -L https://github.com/prabal-rje/smartpipe-playground/
|
|
217
|
-
cd smartpipe-playground
|
|
234
|
+
curl -L https://github.com/prabal-rje/smartpipe-playground/releases/download/v1/smartpipe-playground-v1.tar.gz | tar xz
|
|
235
|
+
cd smartpipe-playground
|
|
218
236
|
|
|
219
237
|
smartpipe map "Extract {vendor, invoice_number, total number}" 'invoices/*.pdf'
|
|
220
238
|
smartpipe filter "the customer sounds frustrated" 'recordings/*.mp3'
|
|
@@ -236,9 +254,11 @@ For chat, [Ollama][ollama] runs models on your machine. Any cloud model sends
|
|
|
236
254
|
that run's data to its provider - `gpt-5.4-mini`, `claude-opus-4-8`,
|
|
237
255
|
`gemini-3.1-flash-lite`, `mistral-large-latest`, `openrouter/…`.
|
|
238
256
|
|
|
239
|
-
API keys come from environment variables
|
|
240
|
-
subscribers can
|
|
241
|
-
local run and token totals; see
|
|
257
|
+
API keys come from environment variables, or `smartpipe auth login` stores
|
|
258
|
+
one securely for you. ChatGPT subscribers can log in instead of using a key.
|
|
259
|
+
`smartpipe usage` keeps local run and token totals; see
|
|
260
|
+
[Privacy & security][privacy] for where credentials live and how to remove
|
|
261
|
+
them.
|
|
242
262
|
|
|
243
263
|
## Unix behavior
|
|
244
264
|
|
|
@@ -303,6 +323,7 @@ lives in [`qa/`](qa/README.md). The CLI surface is a SemVer contract.
|
|
|
303
323
|
[distinct]: https://prabal-rje.github.io/smartpipe/verbs/distinct/
|
|
304
324
|
[diff]: https://prabal-rje.github.io/smartpipe/verbs/diff/
|
|
305
325
|
[outliers]: https://prabal-rje.github.io/smartpipe/verbs/outliers/
|
|
326
|
+
[graph]: https://prabal-rje.github.io/smartpipe/verbs/graph/
|
|
306
327
|
[where]: https://prabal-rje.github.io/smartpipe/verbs/where/
|
|
307
328
|
[summarize]: https://prabal-rje.github.io/smartpipe/verbs/summarize/
|
|
308
329
|
[sort]: https://prabal-rje.github.io/smartpipe/verbs/sort/
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
Run PDFs, images, audio, video, and text through Unix verbs that understand
|
|
12
12
|
their input.
|
|
13
13
|
|
|
14
|
-
[](https://prabal-rje.github.io/smartpipe/demo/)
|
|
15
15
|
|
|
16
|
-
*[▶ Watch the full
|
|
16
|
+
*[▶ Watch the full 86-second demo](https://prabal-rje.github.io/smartpipe/demo/) - sound, controls, no download.*
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
@@ -102,6 +102,7 @@ into ordinary Unix tools. Every verb is documented at
|
|
|
102
102
|
| [`distinct`][distinct] | fold near-duplicates; `--exact` is free | `sort -u`, by meaning |
|
|
103
103
|
| [`diff`][diff] | what distinguishes two sets | the post-incident answer |
|
|
104
104
|
| [`outliers`][outliers] | the items least like the rest | novelty, surfaced |
|
|
105
|
+
| [`graph`][graph] | corpus → entity/relationship graph; `--fast` is free | the case wall, with citations |
|
|
105
106
|
|
|
106
107
|
**Free verbs** never call a model. Run them first to cut the corpus before any
|
|
107
108
|
paid stage:
|
|
@@ -117,9 +118,10 @@ paid stage:
|
|
|
117
118
|
| [`chart`][cli] | terminal bars, SVG, facets, time series | quick plots |
|
|
118
119
|
|
|
119
120
|
Some semantic verbs have a **conditionally free mode**: `join --on` (key
|
|
120
|
-
equality, no prompt), `distinct --exact` (hash-only folding), `
|
|
121
|
-
`--dry-run` (compose without sending),
|
|
122
|
-
braces/DSL expression. Each stays at zero model
|
|
121
|
+
equality, no prompt), `distinct --exact` (hash-only folding), `graph --fast`
|
|
122
|
+
(local NER, on-device), `map`/`extend` `--dry-run` (compose without sending),
|
|
123
|
+
and `smartpipe schema` with a braces/DSL expression. Each stays at zero model
|
|
124
|
+
calls by construction.
|
|
123
125
|
|
|
124
126
|
## A one-minute tour
|
|
125
127
|
|
|
@@ -155,6 +157,9 @@ smartpipe map "Extract {vendor string, invoice_number string, total number}" 'in
|
|
|
155
157
|
# 8. video RAG, no vector database: index a folder of recordings once, ask any day
|
|
156
158
|
smartpipe embed 'sessions/**/*.mp4' > sessions.embeddings
|
|
157
159
|
smartpipe top_k 3 --near "user gives up after the coupon fails" < sessions.embeddings
|
|
160
|
+
|
|
161
|
+
# 9. the corpus as a knowledge graph - $0: local NER, zero model calls
|
|
162
|
+
smartpipe graph --fast 'case-files/*.md' --save case.html
|
|
158
163
|
```
|
|
159
164
|
|
|
160
165
|
A `.txt` on a pipe arrives one item per line; `--as file` treats the whole
|
|
@@ -169,6 +174,18 @@ Numbers 7 and 8 are full recipes -
|
|
|
169
174
|
New to this? The [Learn track][quickstart] starts at zero and assumes nothing, including
|
|
170
175
|
what a "model" is.
|
|
171
176
|
|
|
177
|
+
### Highlight: the knowledge graph
|
|
178
|
+
|
|
179
|
+
Number 9 is new: `graph --fast` turns a folder of mixed files into an
|
|
180
|
+
interactive, **cited** knowledge graph without a single model call - a local
|
|
181
|
+
NER model finds the entities you name, co-occurrence weights the edges, and
|
|
182
|
+
hovering any edge shows the files behind it. A focus prompt upgrades the
|
|
183
|
+
strongest edges to model-read relations. Full story:
|
|
184
|
+
[the `graph` page](https://prabal-rje.github.io/smartpipe/verbs/graph/) and
|
|
185
|
+
[the cookbook recipe](docs/cookbook/knowledge-graph.md).
|
|
186
|
+
|
|
187
|
+

|
|
188
|
+
|
|
172
189
|
## Try it on real files
|
|
173
190
|
|
|
174
191
|
No corpus handy? [smartpipe-playground][playground] ships 26 MB of CC0 /
|
|
@@ -176,8 +193,8 @@ public-domain practice files - invoices, reports, photos, recordings, screen
|
|
|
176
193
|
sessions, and JSONL data:
|
|
177
194
|
|
|
178
195
|
```bash
|
|
179
|
-
curl -L https://github.com/prabal-rje/smartpipe-playground/
|
|
180
|
-
cd smartpipe-playground
|
|
196
|
+
curl -L https://github.com/prabal-rje/smartpipe-playground/releases/download/v1/smartpipe-playground-v1.tar.gz | tar xz
|
|
197
|
+
cd smartpipe-playground
|
|
181
198
|
|
|
182
199
|
smartpipe map "Extract {vendor, invoice_number, total number}" 'invoices/*.pdf'
|
|
183
200
|
smartpipe filter "the customer sounds frustrated" 'recordings/*.mp3'
|
|
@@ -199,9 +216,11 @@ For chat, [Ollama][ollama] runs models on your machine. Any cloud model sends
|
|
|
199
216
|
that run's data to its provider - `gpt-5.4-mini`, `claude-opus-4-8`,
|
|
200
217
|
`gemini-3.1-flash-lite`, `mistral-large-latest`, `openrouter/…`.
|
|
201
218
|
|
|
202
|
-
API keys come from environment variables
|
|
203
|
-
subscribers can
|
|
204
|
-
local run and token totals; see
|
|
219
|
+
API keys come from environment variables, or `smartpipe auth login` stores
|
|
220
|
+
one securely for you. ChatGPT subscribers can log in instead of using a key.
|
|
221
|
+
`smartpipe usage` keeps local run and token totals; see
|
|
222
|
+
[Privacy & security][privacy] for where credentials live and how to remove
|
|
223
|
+
them.
|
|
205
224
|
|
|
206
225
|
## Unix behavior
|
|
207
226
|
|
|
@@ -266,6 +285,7 @@ lives in [`qa/`](qa/README.md). The CLI surface is a SemVer contract.
|
|
|
266
285
|
[distinct]: https://prabal-rje.github.io/smartpipe/verbs/distinct/
|
|
267
286
|
[diff]: https://prabal-rje.github.io/smartpipe/verbs/diff/
|
|
268
287
|
[outliers]: https://prabal-rje.github.io/smartpipe/verbs/outliers/
|
|
288
|
+
[graph]: https://prabal-rje.github.io/smartpipe/verbs/graph/
|
|
269
289
|
[where]: https://prabal-rje.github.io/smartpipe/verbs/where/
|
|
270
290
|
[summarize]: https://prabal-rje.github.io/smartpipe/verbs/summarize/
|
|
271
291
|
[sort]: https://prabal-rje.github.io/smartpipe/verbs/sort/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# smartpipe demo video
|
|
2
|
+
|
|
3
|
+
A self-contained [Remotion](https://remotion.dev) project that renders the
|
|
4
|
+
86-second smartpipe demo (1920x1080, 30 fps): eight scenes - cold open, the
|
|
5
|
+
hook, multimodal, cost honesty, index, search, graph, close - all driven by
|
|
6
|
+
one reusable `<Terminal>` component and the frame-math constants in
|
|
7
|
+
`src/config.ts` (tune scene lengths there). Run `npm install` once, then
|
|
8
|
+
`npm run studio` to preview in the browser or `npm run render` to produce
|
|
9
|
+
`out/smartpipe-demo.mp4`; each scene is also registered as its own
|
|
10
|
+
composition (`ColdOpen`, `Hook`, `Multimodal`, `CostHonesty`, `ScaleA`,
|
|
11
|
+
`ScaleB`, `Graph`, `Close`), so `npx remotion render Hook out/hook.mp4`
|
|
12
|
+
re-renders one scene independently. The narrated cut is the `MainNarrated`
|
|
13
|
+
composition: the script text lives in `src/narration/lines.json`, and
|
|
14
|
+
`RIME_API_KEY=... node scripts/fetch-narration.mjs` synthesizes the
|
|
15
|
+
voiceover wavs (Rime.AI, speaker cupola) into the gitignored
|
|
16
|
+
`public/narration/` and refreshes `src/narration/durations.json` - fetch
|
|
17
|
+
before rendering `MainNarrated`. The music bed is
|
|
18
|
+
`public/silicon-prism-waltz.m4a` (60 s, looped over the 86 s cut with a
|
|
19
|
+
fade-out, held far below the voice); if you ever need a
|
|
20
|
+
no-strings-attached replacement, `node scripts/make-music.mjs` (ffmpeg
|
|
21
|
+
required) synthesizes an original ambient track from scratch into
|
|
22
|
+
`public/music.m4a` - royalty-free by construction - and you can point the
|
|
23
|
+
`<Audio>` element in `src/Main.tsx` at it.
|
|
@@ -5,22 +5,34 @@ import { Background } from "./components/Background";
|
|
|
5
5
|
import { Close } from "./scenes/Close";
|
|
6
6
|
import { ColdOpen } from "./scenes/ColdOpen";
|
|
7
7
|
import { CostHonesty } from "./scenes/CostHonesty";
|
|
8
|
+
import { Graph } from "./scenes/Graph";
|
|
8
9
|
import { Hook } from "./scenes/Hook";
|
|
9
10
|
import { Multimodal } from "./scenes/Multimodal";
|
|
10
|
-
import {
|
|
11
|
+
import { ScaleA } from "./scenes/ScaleA";
|
|
12
|
+
import { ScaleB } from "./scenes/ScaleB";
|
|
13
|
+
import { duckFactor, Narration } from "./Narration";
|
|
11
14
|
|
|
12
|
-
/**
|
|
13
|
-
|
|
15
|
+
/** Music bed base gain. Owner order (2026-07-09): "lower BG volume much
|
|
16
|
+
* more" — 0.55 → 0.14, about −12 dB. Texture, never presence. */
|
|
17
|
+
const MUSIC_BASE = 0.14;
|
|
18
|
+
|
|
19
|
+
export type MainProps = { narrated?: boolean };
|
|
20
|
+
|
|
21
|
+
/** The full 86s cut: one persistent background, eight scenes in series.
|
|
22
|
+
* With `narrated`, the Rime voiceover plays on top, a subtitle band shows
|
|
23
|
+
* the spoken words, and the music ducks a further few dB under speech. */
|
|
24
|
+
export const Main: React.FC<MainProps> = ({ narrated = false }) => {
|
|
14
25
|
return (
|
|
15
26
|
<AbsoluteFill>
|
|
16
|
-
{/* Music bed: Silicon Prism Waltz (60s, looped to cover the
|
|
27
|
+
{/* Music bed: Silicon Prism Waltz (60s, looped to cover the 86s cut).
|
|
17
28
|
Volume envelope: quick fade-in, steady bed, fade-out over the close.
|
|
18
29
|
A fully synthesized fallback lives at scripts/make-music.mjs. */}
|
|
19
30
|
<Audio
|
|
20
31
|
loop
|
|
21
32
|
src={staticFile("silicon-prism-waltz.m4a")}
|
|
22
33
|
volume={(f) =>
|
|
23
|
-
|
|
34
|
+
MUSIC_BASE *
|
|
35
|
+
(narrated ? duckFactor(f) : 1) *
|
|
24
36
|
interpolate(
|
|
25
37
|
f,
|
|
26
38
|
[0, 36, TOTAL_FRAMES - 96, TOTAL_FRAMES - 8],
|
|
@@ -43,13 +55,20 @@ export const Main: React.FC = () => {
|
|
|
43
55
|
<Series.Sequence durationInFrames={SCENE.cost}>
|
|
44
56
|
<CostHonesty />
|
|
45
57
|
</Series.Sequence>
|
|
46
|
-
<Series.Sequence durationInFrames={SCENE.
|
|
47
|
-
<
|
|
58
|
+
<Series.Sequence durationInFrames={SCENE.scaleA}>
|
|
59
|
+
<ScaleA />
|
|
60
|
+
</Series.Sequence>
|
|
61
|
+
<Series.Sequence durationInFrames={SCENE.scaleB}>
|
|
62
|
+
<ScaleB />
|
|
63
|
+
</Series.Sequence>
|
|
64
|
+
<Series.Sequence durationInFrames={SCENE.graph}>
|
|
65
|
+
<Graph />
|
|
48
66
|
</Series.Sequence>
|
|
49
67
|
<Series.Sequence durationInFrames={SCENE.close}>
|
|
50
68
|
<Close />
|
|
51
69
|
</Series.Sequence>
|
|
52
70
|
</Series>
|
|
71
|
+
{narrated ? <Narration /> : null}
|
|
53
72
|
</AbsoluteFill>
|
|
54
73
|
);
|
|
55
74
|
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
AbsoluteFill,
|
|
4
|
+
Audio,
|
|
5
|
+
interpolate,
|
|
6
|
+
Sequence,
|
|
7
|
+
staticFile,
|
|
8
|
+
useCurrentFrame,
|
|
9
|
+
} from "remotion";
|
|
10
|
+
import { COLORS, FPS } from "./config";
|
|
11
|
+
import { MONO } from "./font";
|
|
12
|
+
import lines from "./narration/lines.json";
|
|
13
|
+
import durations from "./narration/durations.json";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The Rime.AI voiceover: one wav per line of the committed script
|
|
17
|
+
* (src/narration/lines.json), fetched by scripts/fetch-narration.mjs into
|
|
18
|
+
* public/narration/ (gitignored). Each line also renders as a subtitle band
|
|
19
|
+
* below the scene caption cards, and duckFactor() dips the music bed a few
|
|
20
|
+
* dB while anyone is speaking.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
type Line = { id: string; at: number; text: string };
|
|
24
|
+
|
|
25
|
+
const LINES: readonly Line[] = lines;
|
|
26
|
+
const DURATIONS: Record<string, number> = durations;
|
|
27
|
+
|
|
28
|
+
/** Narration mix gain: the clips peak near −1 dBFS — leave a little headroom. */
|
|
29
|
+
const NARRATION_GAIN = 0.85;
|
|
30
|
+
|
|
31
|
+
/** Music multiplier while the voice speaks (≈ −4.4 dB on top of the bed). */
|
|
32
|
+
const VOICE_DUCK = 0.6;
|
|
33
|
+
|
|
34
|
+
const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" } as const;
|
|
35
|
+
|
|
36
|
+
const clipFrames = (id: string): number => {
|
|
37
|
+
const seconds = DURATIONS[id];
|
|
38
|
+
if (seconds === undefined) {
|
|
39
|
+
throw new Error(`no duration for narration clip "${id}" — run scripts/fetch-narration.mjs`);
|
|
40
|
+
}
|
|
41
|
+
return Math.ceil(seconds * FPS);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** 1 in silence, VOICE_DUCK under speech, with 12-frame ramps either side. */
|
|
45
|
+
export const duckFactor = (frame: number): number => {
|
|
46
|
+
const coverage = Math.max(
|
|
47
|
+
...LINES.map((line) => {
|
|
48
|
+
const end = line.at + clipFrames(line.id);
|
|
49
|
+
return interpolate(frame, [line.at - 12, line.at, end, end + 12], [0, 1, 1, 0], clamp);
|
|
50
|
+
}),
|
|
51
|
+
0,
|
|
52
|
+
);
|
|
53
|
+
return 1 - (1 - VOICE_DUCK) * coverage;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** The spoken words, as a dim mono subtitle band under the caption cards. */
|
|
57
|
+
const SubtitleLine: React.FC<{ text: string; duration: number }> = ({
|
|
58
|
+
text,
|
|
59
|
+
duration,
|
|
60
|
+
}) => {
|
|
61
|
+
const frame = useCurrentFrame();
|
|
62
|
+
const opacity = interpolate(frame, [0, 6, duration - 8, duration], [0, 1, 1, 0], clamp);
|
|
63
|
+
return (
|
|
64
|
+
<AbsoluteFill style={{ justifyContent: "flex-end", alignItems: "center" }}>
|
|
65
|
+
<div
|
|
66
|
+
style={{
|
|
67
|
+
marginBottom: 18,
|
|
68
|
+
fontFamily: MONO,
|
|
69
|
+
fontSize: 24,
|
|
70
|
+
color: COLORS.dim,
|
|
71
|
+
letterSpacing: 0.3,
|
|
72
|
+
opacity: opacity * 0.9,
|
|
73
|
+
textShadow: "0 2px 12px rgba(0,0,0,0.85)",
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
{text}
|
|
77
|
+
</div>
|
|
78
|
+
</AbsoluteFill>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const Narration: React.FC = () => (
|
|
83
|
+
<AbsoluteFill>
|
|
84
|
+
{LINES.map((line) => {
|
|
85
|
+
const duration = clipFrames(line.id) + 10;
|
|
86
|
+
return (
|
|
87
|
+
<Sequence key={line.id} from={line.at} durationInFrames={duration}>
|
|
88
|
+
<Audio src={staticFile(`narration/${line.id}.wav`)} volume={NARRATION_GAIN} />
|
|
89
|
+
<SubtitleLine text={line.text} duration={duration} />
|
|
90
|
+
</Sequence>
|
|
91
|
+
);
|
|
92
|
+
})}
|
|
93
|
+
</AbsoluteFill>
|
|
94
|
+
);
|
|
@@ -6,9 +6,11 @@ import { Main } from "./Main";
|
|
|
6
6
|
import { Close } from "./scenes/Close";
|
|
7
7
|
import { ColdOpen } from "./scenes/ColdOpen";
|
|
8
8
|
import { CostHonesty } from "./scenes/CostHonesty";
|
|
9
|
+
import { Graph } from "./scenes/Graph";
|
|
9
10
|
import { Hook } from "./scenes/Hook";
|
|
10
11
|
import { Multimodal } from "./scenes/Multimodal";
|
|
11
|
-
import {
|
|
12
|
+
import { ScaleA } from "./scenes/ScaleA";
|
|
13
|
+
import { ScaleB } from "./scenes/ScaleB";
|
|
12
14
|
|
|
13
15
|
/** Standalone scene = background + scene, so each renders on its own. */
|
|
14
16
|
const standalone = (Scene: React.FC): React.FC => {
|
|
@@ -26,7 +28,9 @@ const SCENES: readonly { id: string; component: React.FC; duration: number }[] =
|
|
|
26
28
|
{ id: "Hook", component: standalone(Hook), duration: SCENE.hook },
|
|
27
29
|
{ id: "Multimodal", component: standalone(Multimodal), duration: SCENE.multimodal },
|
|
28
30
|
{ id: "CostHonesty", component: standalone(CostHonesty), duration: SCENE.cost },
|
|
29
|
-
{ id: "
|
|
31
|
+
{ id: "ScaleA", component: standalone(ScaleA), duration: SCENE.scaleA },
|
|
32
|
+
{ id: "ScaleB", component: standalone(ScaleB), duration: SCENE.scaleB },
|
|
33
|
+
{ id: "Graph", component: standalone(Graph), duration: SCENE.graph },
|
|
30
34
|
{ id: "Close", component: standalone(Close), duration: SCENE.close },
|
|
31
35
|
];
|
|
32
36
|
|
|
@@ -41,6 +45,17 @@ export const RemotionRoot: React.FC = () => {
|
|
|
41
45
|
width={WIDTH}
|
|
42
46
|
height={HEIGHT}
|
|
43
47
|
/>
|
|
48
|
+
{/* The narrated cut: same timeline + Rime voiceover, subtitles, ducked
|
|
49
|
+
music. Needs public/narration/*.wav — run scripts/fetch-narration.mjs. */}
|
|
50
|
+
<Composition
|
|
51
|
+
id="MainNarrated"
|
|
52
|
+
component={Main}
|
|
53
|
+
durationInFrames={TOTAL_FRAMES}
|
|
54
|
+
fps={FPS}
|
|
55
|
+
width={WIDTH}
|
|
56
|
+
height={HEIGHT}
|
|
57
|
+
defaultProps={{ narrated: true }}
|
|
58
|
+
/>
|
|
44
59
|
{SCENES.map((s) => (
|
|
45
60
|
<Composition
|
|
46
61
|
key={s.id}
|
|
@@ -3,8 +3,9 @@ import { AbsoluteFill } from "remotion";
|
|
|
3
3
|
import { COLORS } from "../config";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The stage every scene plays on: near-black
|
|
7
|
-
*
|
|
6
|
+
* The stage every scene plays on: a FLAT near-black ground (owner ruling,
|
|
7
|
+
* 2026-07-09: no glowy background gradient), a whisper of a dot grid,
|
|
8
|
+
* scanlines, and a vignette.
|
|
8
9
|
*/
|
|
9
10
|
export const Background: React.FC = () => {
|
|
10
11
|
return (
|
|
@@ -18,14 +19,6 @@ export const Background: React.FC = () => {
|
|
|
18
19
|
backgroundPosition: "14px 14px",
|
|
19
20
|
}}
|
|
20
21
|
/>
|
|
21
|
-
{/* ambient color glows */}
|
|
22
|
-
<AbsoluteFill
|
|
23
|
-
style={{
|
|
24
|
-
background:
|
|
25
|
-
"radial-gradient(820px 560px at 22% 18%, rgba(34,211,238,0.07), transparent 70%)," +
|
|
26
|
-
"radial-gradient(900px 620px at 82% 78%, rgba(52,211,153,0.055), transparent 70%)",
|
|
27
|
-
}}
|
|
28
|
-
/>
|
|
29
22
|
{/* scanlines */}
|
|
30
23
|
<AbsoluteFill
|
|
31
24
|
style={{
|
|
@@ -8,14 +8,24 @@ import { MONO } from "../font";
|
|
|
8
8
|
*
|
|
9
9
|
* Lines are declared with a `kind` and a start frame (`at`, relative to the
|
|
10
10
|
* enclosing <Sequence>):
|
|
11
|
-
* - "cmd"
|
|
12
|
-
* - "out"
|
|
13
|
-
* - "note"
|
|
11
|
+
* - "cmd" → typed character-by-character behind a block caret
|
|
12
|
+
* - "out" → stdout rows that cascade in with a small spring
|
|
13
|
+
* - "note" → dimmed stderr diagnostics in yellow (receipts, run notes)
|
|
14
|
+
* - "ord" → the cyan `#N` ordinal above a human block (io/writers.py)
|
|
15
|
+
* - "field" → one `key: value` block line — key dim, value normal; with
|
|
16
|
+
* `dim` the whole line is the dimmed `__` spine treatment
|
|
17
|
+
* - "wave" → a small audio peak-envelope garnish (io/preview.py's look)
|
|
18
|
+
* - "pair" → a text snippet plus a dim trailing glyph (embed-row picture)
|
|
19
|
+
* `gap` renders the blank-line separation between blocks.
|
|
14
20
|
*/
|
|
15
21
|
export type TerminalLine =
|
|
16
22
|
| { kind: "cmd"; text: string; at: number; cps?: number }
|
|
17
23
|
| { kind: "out"; text: string; at: number }
|
|
18
|
-
| { kind: "note"; text: string; at: number }
|
|
24
|
+
| { kind: "note"; text: string; at: number; gap?: boolean }
|
|
25
|
+
| { kind: "ord"; text: string; at: number; gap?: boolean }
|
|
26
|
+
| { kind: "field"; k: string; v: string; at: number; dim?: boolean }
|
|
27
|
+
| { kind: "wave"; text: string; at: number }
|
|
28
|
+
| { kind: "pair"; left: string; right: string; at: number };
|
|
19
29
|
|
|
20
30
|
type Span = { text: string; color: string; weight?: number };
|
|
21
31
|
|
|
@@ -238,12 +248,15 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|
|
238
248
|
extrapolateLeft: "clamp",
|
|
239
249
|
extrapolateRight: "clamp",
|
|
240
250
|
});
|
|
251
|
+
const gap =
|
|
252
|
+
(line.kind === "ord" || line.kind === "note") && line.gap === true;
|
|
241
253
|
const style: React.CSSProperties = {
|
|
242
254
|
fontSize,
|
|
243
255
|
lineHeight: `${lineHeight}px`,
|
|
244
256
|
transform: `translateY(${(1 - p) * 15}px)`,
|
|
245
257
|
opacity,
|
|
246
258
|
whiteSpace: "pre",
|
|
259
|
+
marginTop: gap ? Math.round(lineHeight * 0.5) : 0,
|
|
247
260
|
};
|
|
248
261
|
if (line.kind === "note") {
|
|
249
262
|
return (
|
|
@@ -255,6 +268,58 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|
|
255
268
|
</div>
|
|
256
269
|
);
|
|
257
270
|
}
|
|
271
|
+
if (line.kind === "ord") {
|
|
272
|
+
return (
|
|
273
|
+
<div
|
|
274
|
+
key={i}
|
|
275
|
+
style={{ ...style, color: COLORS.cyan, fontWeight: 700 }}
|
|
276
|
+
>
|
|
277
|
+
{line.text}
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (line.kind === "field") {
|
|
282
|
+
// io/render.py `_line`: the key label dim, the value normal; a
|
|
283
|
+
// `dim` line is the `__` spine — dimmed whole, metadata-quiet.
|
|
284
|
+
return (
|
|
285
|
+
<div key={i} style={style}>
|
|
286
|
+
<span style={{ color: line.dim ? COLORS.ghost : COLORS.dim }}>
|
|
287
|
+
{line.k}:{" "}
|
|
288
|
+
</span>
|
|
289
|
+
<span style={{ color: line.dim ? COLORS.faint : COLORS.text }}>
|
|
290
|
+
{line.v}
|
|
291
|
+
</span>
|
|
292
|
+
</div>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (line.kind === "pair") {
|
|
296
|
+
return (
|
|
297
|
+
<div key={i} style={style}>
|
|
298
|
+
<span style={{ color: COLORS.text }}>{line.left}</span>
|
|
299
|
+
<span style={{ color: COLORS.faint, opacity: 0.8 }}>
|
|
300
|
+
{" "}
|
|
301
|
+
{line.right}
|
|
302
|
+
</span>
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (line.kind === "wave") {
|
|
307
|
+
// io/preview.py's audio garnish: a peak envelope, nested + quiet.
|
|
308
|
+
return (
|
|
309
|
+
<div
|
|
310
|
+
key={i}
|
|
311
|
+
style={{
|
|
312
|
+
...style,
|
|
313
|
+
color: COLORS.cyanDim,
|
|
314
|
+
opacity: opacity * 0.55,
|
|
315
|
+
letterSpacing: 1,
|
|
316
|
+
}}
|
|
317
|
+
>
|
|
318
|
+
{" "}
|
|
319
|
+
{line.text}
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
258
323
|
return (
|
|
259
324
|
<div key={i} style={style}>
|
|
260
325
|
<Spans spans={colorizeOut(line.text)} />
|