scriptcast 0.1.0__tar.gz → 0.3.0__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.
- scriptcast-0.3.0/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- scriptcast-0.3.0/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- scriptcast-0.3.0/.github/pull_request_template.md +14 -0
- scriptcast-0.3.0/.github/workflows/ci.yml +46 -0
- scriptcast-0.3.0/.github/workflows/publish-pages.yml +51 -0
- scriptcast-0.3.0/.github/workflows/publish-pypi.yml +26 -0
- scriptcast-0.3.0/.gitignore +180 -0
- scriptcast-0.3.0/CHANGELOG.md +58 -0
- scriptcast-0.3.0/CODE_OF_CONDUCT.md +41 -0
- scriptcast-0.3.0/CONTRIBUTING.md +50 -0
- scriptcast-0.3.0/DIRECTIVES.md +163 -0
- scriptcast-0.3.0/Makefile +42 -0
- scriptcast-0.3.0/PKG-INFO +286 -0
- scriptcast-0.3.0/assets/demo.png +0 -0
- scriptcast-0.3.0/assets/showcase-aurora.png +0 -0
- scriptcast-0.3.0/assets/showcase-dark.png +0 -0
- scriptcast-0.3.0/assets/showcase-light.png +0 -0
- scriptcast-0.3.0/assets/tutorial.png +0 -0
- scriptcast-0.3.0/cliff.toml +44 -0
- scriptcast-0.3.0/examples/.gitignore +5 -0
- scriptcast-0.3.0/examples/demo.sh +13 -0
- scriptcast-0.3.0/examples/fake-db +11 -0
- scriptcast-0.3.0/examples/fake-myapp +7 -0
- scriptcast-0.3.0/examples/showcase.sh +52 -0
- scriptcast-0.3.0/examples/tutorial.sh +104 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/pyproject.toml +25 -7
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/__main__.py +16 -1
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/themes/aurora.sh +1 -1
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/themes/dark.sh +1 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/themes/light.sh +1 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/recorder.py +8 -2
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/shell/adapter.py +6 -0
- scriptcast-0.3.0/scriptcast/shell/zsh.py +72 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_cli.py +15 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_directives.py +1 -1
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_recorder.py +53 -0
- scriptcast-0.3.0/tests/test_shell.py +94 -0
- scriptcast-0.1.0/PKG-INFO +0 -21
- scriptcast-0.1.0/scriptcast/shell/zsh.py +0 -11
- scriptcast-0.1.0/scriptcast.egg-info/PKG-INFO +0 -21
- scriptcast-0.1.0/scriptcast.egg-info/SOURCES.txt +0 -36
- scriptcast-0.1.0/scriptcast.egg-info/dependency_links.txt +0 -1
- scriptcast-0.1.0/scriptcast.egg-info/entry_points.txt +0 -2
- scriptcast-0.1.0/scriptcast.egg-info/requires.txt +0 -10
- scriptcast-0.1.0/scriptcast.egg-info/top_level.txt +0 -5
- scriptcast-0.1.0/setup.cfg +0 -4
- scriptcast-0.1.0/tests/test_shell.py +0 -34
- {scriptcast-0.1.0 → scriptcast-0.3.0}/README.md +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/__init__.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/__init__.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/fonts/DMSans-Regular.ttf +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/assets/fonts/Pacifico.ttf +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/config.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/directives.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/export.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/generator.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/shell/__init__.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/scriptcast/shell/bash.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/__init__.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_config.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_export.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_generator.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_integration.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_registry.py +0 -0
- {scriptcast-0.1.0 → scriptcast-0.3.0}/tests/test_theme.py +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something isn't working
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Describe the bug
|
|
8
|
+
|
|
9
|
+
<!-- A clear description of what the bug is. -->
|
|
10
|
+
|
|
11
|
+
## Steps to reproduce
|
|
12
|
+
|
|
13
|
+
1. Script content (`.sh` file):
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
# paste your script here
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. Command run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
scriptcast ...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Expected behaviour:
|
|
26
|
+
|
|
27
|
+
4. Actual behaviour:
|
|
28
|
+
|
|
29
|
+
## Environment
|
|
30
|
+
|
|
31
|
+
- scriptcast version: <!-- `pip show scriptcast` -->
|
|
32
|
+
- Python version: <!-- `python --version` -->
|
|
33
|
+
- OS:
|
|
34
|
+
- Shell:
|
|
35
|
+
|
|
36
|
+
## .sc file (if available)
|
|
37
|
+
|
|
38
|
+
<!-- Attach or paste the `.sc` file generated before the error, if relevant. -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a new directive or capability
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Use case
|
|
8
|
+
|
|
9
|
+
<!-- What are you trying to do that scriptcast doesn't support today? -->
|
|
10
|
+
|
|
11
|
+
## Proposed directive syntax
|
|
12
|
+
|
|
13
|
+
<!-- If this is a new directive, show how it would look in a script: -->
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
: SC myfeature arg1 arg2
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Expected behaviour in the cast
|
|
20
|
+
|
|
21
|
+
<!-- What should the generated cast look like? -->
|
|
22
|
+
|
|
23
|
+
## Alternatives considered
|
|
24
|
+
|
|
25
|
+
<!-- Any workarounds or alternative approaches you've thought of? -->
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## What does this PR do?
|
|
2
|
+
|
|
3
|
+
<!-- One sentence summary. -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] `make all` passes locally (lint + typecheck + tests)
|
|
8
|
+
- [ ] New behaviour is covered by tests
|
|
9
|
+
- [ ] `DIRECTIVES.md` updated (if directive added or changed)
|
|
10
|
+
- [ ] `CHANGELOG.md` has an entry (or I'm relying on the auto-changelog PR)
|
|
11
|
+
|
|
12
|
+
## Related issues
|
|
13
|
+
|
|
14
|
+
<!-- Closes #123 -->
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, dev]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.12"
|
|
17
|
+
- run: pip install ruff
|
|
18
|
+
- run: ruff check .
|
|
19
|
+
|
|
20
|
+
typecheck:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
- run: pip install -e ".[dev]"
|
|
28
|
+
- run: mypy scriptcast/
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: ${{ matrix.python-version }}
|
|
40
|
+
- run: pip install -e ".[dev]"
|
|
41
|
+
- run: pytest --cov=scriptcast --cov-report=xml
|
|
42
|
+
- uses: codecov/codecov-action@v4
|
|
43
|
+
if: matrix.python-version == '3.12'
|
|
44
|
+
with:
|
|
45
|
+
files: coverage.xml
|
|
46
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
|
2
|
+
name: Deploy GitHub Pages
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["Publish to PyPI"]
|
|
6
|
+
types: [completed]
|
|
7
|
+
|
|
8
|
+
# Allows you to run this workflow manually from the Actions tab
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
|
18
|
+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: "pages"
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
# Build job
|
|
25
|
+
build:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
- name: Setup Pages
|
|
32
|
+
uses: actions/configure-pages@v5
|
|
33
|
+
- name: Build with Jekyll
|
|
34
|
+
uses: actions/jekyll-build-pages@v1
|
|
35
|
+
with:
|
|
36
|
+
source: ./
|
|
37
|
+
destination: ./_site
|
|
38
|
+
- name: Upload artifact
|
|
39
|
+
uses: actions/upload-pages-artifact@v3
|
|
40
|
+
|
|
41
|
+
# Deployment job
|
|
42
|
+
deploy:
|
|
43
|
+
environment:
|
|
44
|
+
name: github-pages
|
|
45
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
needs: build
|
|
48
|
+
steps:
|
|
49
|
+
- name: Deploy to GitHub Pages
|
|
50
|
+
id: deployment
|
|
51
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: uv build
|
|
24
|
+
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python
|
|
2
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
|
3
|
+
|
|
4
|
+
### Python ###
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
build/
|
|
16
|
+
develop-eggs/
|
|
17
|
+
dist/
|
|
18
|
+
downloads/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# PyInstaller
|
|
34
|
+
# Usually these files are written by a python script from a template
|
|
35
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
36
|
+
*.manifest
|
|
37
|
+
*.spec
|
|
38
|
+
|
|
39
|
+
# Installer logs
|
|
40
|
+
pip-log.txt
|
|
41
|
+
pip-delete-this-directory.txt
|
|
42
|
+
|
|
43
|
+
# Unit test / coverage reports
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
.cache
|
|
50
|
+
nosetests.xml
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
*.py,cover
|
|
54
|
+
.hypothesis/
|
|
55
|
+
.pytest_cache/
|
|
56
|
+
cover/
|
|
57
|
+
|
|
58
|
+
# Translations
|
|
59
|
+
*.mo
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
# Django stuff:
|
|
63
|
+
*.log
|
|
64
|
+
local_settings.py
|
|
65
|
+
db.sqlite3
|
|
66
|
+
db.sqlite3-journal
|
|
67
|
+
|
|
68
|
+
# Flask stuff:
|
|
69
|
+
instance/
|
|
70
|
+
.webassets-cache
|
|
71
|
+
|
|
72
|
+
# Scrapy stuff:
|
|
73
|
+
.scrapy
|
|
74
|
+
|
|
75
|
+
# Sphinx documentation
|
|
76
|
+
docs/_build/
|
|
77
|
+
|
|
78
|
+
# PyBuilder
|
|
79
|
+
.pybuilder/
|
|
80
|
+
target/
|
|
81
|
+
|
|
82
|
+
# Jupyter Notebook
|
|
83
|
+
.ipynb_checkpoints
|
|
84
|
+
|
|
85
|
+
# IPython
|
|
86
|
+
profile_default/
|
|
87
|
+
ipython_config.py
|
|
88
|
+
|
|
89
|
+
# pyenv
|
|
90
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
91
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
92
|
+
# .python-version
|
|
93
|
+
|
|
94
|
+
# pipenv
|
|
95
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
96
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
97
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
98
|
+
# install all needed dependencies.
|
|
99
|
+
#Pipfile.lock
|
|
100
|
+
|
|
101
|
+
# poetry
|
|
102
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
103
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
104
|
+
# commonly ignored for libraries.
|
|
105
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
106
|
+
#poetry.lock
|
|
107
|
+
|
|
108
|
+
# pdm
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
110
|
+
#pdm.lock
|
|
111
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
112
|
+
# in version control.
|
|
113
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
114
|
+
.pdm.toml
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.venv
|
|
129
|
+
env/
|
|
130
|
+
venv/
|
|
131
|
+
ENV/
|
|
132
|
+
env.bak/
|
|
133
|
+
venv.bak/
|
|
134
|
+
|
|
135
|
+
# Spyder project settings
|
|
136
|
+
.spyderproject
|
|
137
|
+
.spyproject
|
|
138
|
+
|
|
139
|
+
# Rope project settings
|
|
140
|
+
.ropeproject
|
|
141
|
+
|
|
142
|
+
# mkdocs documentation
|
|
143
|
+
/site
|
|
144
|
+
|
|
145
|
+
# mypy
|
|
146
|
+
.mypy_cache/
|
|
147
|
+
.dmypy.json
|
|
148
|
+
dmypy.json
|
|
149
|
+
|
|
150
|
+
# Pyre type checker
|
|
151
|
+
.pyre/
|
|
152
|
+
|
|
153
|
+
# pytype static type analyzer
|
|
154
|
+
.pytype/
|
|
155
|
+
|
|
156
|
+
# Cython debug symbols
|
|
157
|
+
cython_debug/
|
|
158
|
+
|
|
159
|
+
# PyCharm
|
|
160
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
161
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
162
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
163
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
164
|
+
#.idea/
|
|
165
|
+
|
|
166
|
+
### Python Patch ###
|
|
167
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
168
|
+
poetry.toml
|
|
169
|
+
|
|
170
|
+
# ruff
|
|
171
|
+
.ruff_cache/
|
|
172
|
+
|
|
173
|
+
# LSP config files
|
|
174
|
+
pyrightconfig.json
|
|
175
|
+
|
|
176
|
+
# End of https://www.toptal.com/developers/gitignore/api/python
|
|
177
|
+
|
|
178
|
+
# Git worktrees
|
|
179
|
+
.worktrees/
|
|
180
|
+
uv.lock
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
## [0.3.0] - 2026-04-07
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- Fix default theme loading and zsh xtrace ESC byte corruption ([#1](https://github.com/dacrystal/scriptcast/issues/1))
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-04-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Add --version, ASCII banner, hatch-fancy-pypi-readme, and GitHub Pages workflow
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-04-06
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- SC mock/expect directives, InputLine event, single-cast default
|
|
18
|
+
- Add expect example to basic.sh; fix recorder cwd and send_user newline
|
|
19
|
+
- JSONL .sc format, streaming generator, expect session improvements
|
|
20
|
+
- FilterDirective subprocess command; add CommentDirective
|
|
21
|
+
- Open source readiness — directive plugin system, CLI fix, CI/CD, community files
|
|
22
|
+
- Typing_word_speed, GIF frame overlay, dev tooling
|
|
23
|
+
- Frame layout system with CLI flags for title bar, shadow, background, and watermark
|
|
24
|
+
- Add theme system, scriptcast watermark, and frame layout refactor
|
|
25
|
+
- SVG-based chrome renderer with APNG output support
|
|
26
|
+
- PIL fallback frame parity — content masking, APNG, GIF palette fix
|
|
27
|
+
- Replace gif subcommand with export — content-first compositor
|
|
28
|
+
- Add terminal content pre-processing pipeline
|
|
29
|
+
- Frame bool, dark theme full config, CLI cleanup, DM Sans watermark
|
|
30
|
+
- CLI simplification
|
|
31
|
+
- Export format png, temp gif, aurora/light themes, aurora FrameConfig defaults
|
|
32
|
+
- Complete basic.sh, README demo, and xtrace quoting fixes
|
|
33
|
+
- Unify config pipeline — ThemeConfig nested in ScriptcastConfig, theme.py deleted
|
|
34
|
+
- *(examples)* Redesign demo — showcase.sh, tutorial.sh, fake-myapp
|
|
35
|
+
- Multi-pass directive pipeline redesign
|
|
36
|
+
- Verbatim xtrace capture + PTY recording
|
|
37
|
+
- Unified CLI — single entry point, --no-export flag, config resolved before recording
|
|
38
|
+
- HelpersDirective + PTY read simplification + three bug fixes
|
|
39
|
+
- --xtrace-log flag + export progress bar + frame-bar-title fix
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Remove _handle_passthrough; directives own SC syntax matching
|
|
44
|
+
- Replace SVG chrome rendering with PIL-only chrome+mask approach
|
|
45
|
+
- Codebase cleanup and best-practices refactor
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- CI test failure and ruff lint errors from recent features
|
|
50
|
+
- Ignore missing PIL stubs in mypy (Pillow is optional)
|
|
51
|
+
- Stable GIF palette across frames — eliminates chrome color flickering
|
|
52
|
+
- Reserve palette slots for exact chrome colors in GIF output
|
|
53
|
+
- Frame rendering bug fixes — shadow, border, theme parsing, SVG filter
|
|
54
|
+
- Decode \xNN and \NNN escape sequences in SC directive xtrace lines
|
|
55
|
+
- *(filter)* Apply filter to trace/cmd lines and decouple from ExpectDirective
|
|
56
|
+
- Resolve CI test failures
|
|
57
|
+
- Resolve all mypy errors in scriptcast/
|
|
58
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment:
|
|
18
|
+
|
|
19
|
+
* Demonstrating empathy and kindness toward other people
|
|
20
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
21
|
+
* Giving and gracefully accepting constructive feedback
|
|
22
|
+
* Accepting responsibility and apologizing to those affected by our mistakes
|
|
23
|
+
* Focusing on what is best not just for us as individuals, but for the overall community
|
|
24
|
+
|
|
25
|
+
Examples of unacceptable behavior:
|
|
26
|
+
|
|
27
|
+
* The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
28
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
29
|
+
* Public or private harassment
|
|
30
|
+
* Publishing others' private information without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate
|
|
32
|
+
|
|
33
|
+
## Enforcement
|
|
34
|
+
|
|
35
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
36
|
+
reported to the project maintainer at dacrystal@users.noreply.github.com.
|
|
37
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
38
|
+
|
|
39
|
+
## Attribution
|
|
40
|
+
|
|
41
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributing to scriptcast
|
|
2
|
+
|
|
3
|
+
## Dev Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/dacrystal/scriptcast.git
|
|
7
|
+
cd scriptcast
|
|
8
|
+
pip install -e ".[dev]"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Running Tests
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
make test # pytest with coverage
|
|
15
|
+
make lint # ruff
|
|
16
|
+
make typecheck # mypy
|
|
17
|
+
make all # all three
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
All three must pass before opening a PR. CI enforces this.
|
|
21
|
+
|
|
22
|
+
## Commit Messages
|
|
23
|
+
|
|
24
|
+
Use conventional commits — the changelog is generated from them:
|
|
25
|
+
|
|
26
|
+
- `feat: add SC highlight directive`
|
|
27
|
+
- `fix: expect session drops last output line`
|
|
28
|
+
- `docs: clarify filter-add chaining`
|
|
29
|
+
- `chore: bump ruff version`
|
|
30
|
+
|
|
31
|
+
## Writing a Directive
|
|
32
|
+
|
|
33
|
+
See [DIRECTIVES.md](DIRECTIVES.md) for the full guide.
|
|
34
|
+
|
|
35
|
+
## Pull Request Checklist
|
|
36
|
+
|
|
37
|
+
- [ ] Tests pass locally (`make all`)
|
|
38
|
+
- [ ] New behaviour is covered by tests
|
|
39
|
+
- [ ] CHANGELOG.md has an entry (or the auto-PR from CI will add one)
|
|
40
|
+
- [ ] DIRECTIVES.md updated if you added or changed a directive
|
|
41
|
+
|
|
42
|
+
## Release Process (maintainers only)
|
|
43
|
+
|
|
44
|
+
1. Ensure `CHANGELOG.md` is up to date
|
|
45
|
+
2. Bump version in `pyproject.toml`
|
|
46
|
+
3. `git tag v0.X.0 && git push --tags`
|
|
47
|
+
4. The `release.yml` workflow publishes to PyPI and creates a GitHub Release
|
|
48
|
+
|
|
49
|
+
PyPI uses Trusted Publisher (OIDC) — no API key needed. Configure at:
|
|
50
|
+
pypi.org → your project → Publishing → Add publisher (environment: `release`)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Writing Directives
|
|
2
|
+
|
|
3
|
+
Directives are the extension point in scriptcast. Each directive is a Python
|
|
4
|
+
class that participates in one or more of the three pipeline phases:
|
|
5
|
+
|
|
6
|
+
| Phase | Method | When it runs |
|
|
7
|
+
|-------|--------|-------------|
|
|
8
|
+
| Pre | `pre(queue)` | Before the shell script is executed — rewrites script lines |
|
|
9
|
+
| Post | `post(queue)` | After execution — transforms raw xtrace lines into JSONL events |
|
|
10
|
+
| Gen | `gen(event, queue, active, cursor)` | During cast generation — emits asciinema cast lines |
|
|
11
|
+
|
|
12
|
+
A directive can implement any combination of these phases by overriding the
|
|
13
|
+
corresponding method. The base `Directive` class provides no-op defaults for all
|
|
14
|
+
three, so you only override what you need.
|
|
15
|
+
|
|
16
|
+
## The Directive Base Class
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from scriptcast.directives import Directive
|
|
20
|
+
|
|
21
|
+
class MyDirective(Directive):
|
|
22
|
+
priority: int = 50 # lower = runs earlier in the chain
|
|
23
|
+
handles: str | None = None # gen phase only: directive name in .sc events
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### priority
|
|
27
|
+
|
|
28
|
+
Directives are sorted by `priority` before being placed in the processing chain.
|
|
29
|
+
Lower values run first. Core directive priorities:
|
|
30
|
+
|
|
31
|
+
| Directive | Priority | Notes |
|
|
32
|
+
|-----------|----------|-------|
|
|
33
|
+
| RecordDirective | 10 | Must absorb pause/resume before anything else |
|
|
34
|
+
| MockDirective | 20 | Must absorb mock markers before expect or sc catch them |
|
|
35
|
+
| ExpectDirective | 30 | Owns all lines in an expect session; must precede filter |
|
|
36
|
+
| FilterDirective | 40 | Applies to output lines after expect releases them |
|
|
37
|
+
| CommentDirective | 45 | Must precede ScDirective (catch-all) |
|
|
38
|
+
| SetDirective | 50 | Gen phase only |
|
|
39
|
+
| SleepDirective | 50 | Gen phase only |
|
|
40
|
+
| ScDirective | 99 | Catch-all for any remaining `: SC` line — always last |
|
|
41
|
+
|
|
42
|
+
Pick a priority between 50 and 98 for most new directives.
|
|
43
|
+
|
|
44
|
+
### handles
|
|
45
|
+
|
|
46
|
+
Used in the gen phase only. Set `handles = "myname"` and your directive's `gen()`
|
|
47
|
+
method will be called when a `directive` event with text `myname ...` appears in the
|
|
48
|
+
`.sc` file. If `handles` is `None`, `gen()` is never called automatically.
|
|
49
|
+
|
|
50
|
+
## Phase Details
|
|
51
|
+
|
|
52
|
+
### pre(queue: deque[str]) → list[str] | None
|
|
53
|
+
|
|
54
|
+
Called for each line of the script before it is run. The queue contains the
|
|
55
|
+
remaining unprocessed script lines.
|
|
56
|
+
|
|
57
|
+
- **Return None** if this line is not for you — the next directive gets a chance.
|
|
58
|
+
- **Return a list of strings** (replacement lines) to consume from the queue and
|
|
59
|
+
substitute. You are responsible for `queue.popleft()` on lines you consume.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
def pre(self, queue):
|
|
63
|
+
if not queue[0].startswith(f": {self.dp} mything"):
|
|
64
|
+
return None
|
|
65
|
+
queue.popleft()
|
|
66
|
+
return ["echo 'replaced'\n"]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### post(queue: deque[tuple[float, str]]) → list[str] | None
|
|
70
|
+
|
|
71
|
+
Called for each (timestamp, content) item in the raw xtrace output. The queue
|
|
72
|
+
contains remaining unprocessed items.
|
|
73
|
+
|
|
74
|
+
- **Return None** if this item is not for you.
|
|
75
|
+
- **Return a list of JSONL strings** (or `[]` to consume without emitting).
|
|
76
|
+
- Use `json.dumps([ts, "cmd"|"output"|"input"|"directive", text])` to produce events.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import json
|
|
80
|
+
|
|
81
|
+
def post(self, queue):
|
|
82
|
+
ts, content = queue[0]
|
|
83
|
+
if content != f"{self.tp} : {self.dp} mything":
|
|
84
|
+
return None
|
|
85
|
+
queue.popleft()
|
|
86
|
+
return [json.dumps([ts, "directive", "mything"])]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### gen(event, queue, active, cursor) → tuple[float, list[str]]
|
|
90
|
+
|
|
91
|
+
Called during cast generation when a `directive` event matches `self.handles`.
|
|
92
|
+
|
|
93
|
+
- `event` — `(timestamp: float, "directive", text: str)`
|
|
94
|
+
- `queue` — remaining events (you may peek/consume ahead)
|
|
95
|
+
- `active` — current `ScriptcastConfig` (mutable — you can change timing settings)
|
|
96
|
+
- `cursor` — current time position in the cast (seconds)
|
|
97
|
+
- **Return** `(updated_cursor, list_of_cast_json_lines)`
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import json
|
|
101
|
+
|
|
102
|
+
class MySleepDirective(Directive):
|
|
103
|
+
handles = "mysleep"
|
|
104
|
+
|
|
105
|
+
def gen(self, event, queue, active, cursor):
|
|
106
|
+
_, _, text = event
|
|
107
|
+
parts = text.split()
|
|
108
|
+
if len(parts) >= 2:
|
|
109
|
+
cursor += int(parts[1]) / 1000.0
|
|
110
|
+
return cursor, []
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Registering a Third-Party Directive
|
|
114
|
+
|
|
115
|
+
In your package's `pyproject.toml`:
|
|
116
|
+
|
|
117
|
+
```toml
|
|
118
|
+
[project.entry-points."scriptcast.directives"]
|
|
119
|
+
my-directive = "mypkg.directives:MyDirective"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After `pip install` your package alongside scriptcast, your directive is
|
|
123
|
+
automatically loaded and sorted into the processing chain by priority.
|
|
124
|
+
|
|
125
|
+
## Worked Example: SC pause directive
|
|
126
|
+
|
|
127
|
+
This adds a `SC pause <ms>` recorder directive that inserts a `sleep` event
|
|
128
|
+
into the `.sc` file, causing the generator to pause for the given milliseconds.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# mypkg/directives.py
|
|
132
|
+
import json
|
|
133
|
+
import re
|
|
134
|
+
from scriptcast.directives import Directive
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class PauseDirective(Directive):
|
|
138
|
+
"""SC pause <ms> — insert a pause into the cast.
|
|
139
|
+
|
|
140
|
+
Script syntax: : SC pause 500
|
|
141
|
+
Records as: [ts, "directive", "sleep 500"]
|
|
142
|
+
"""
|
|
143
|
+
priority = 48 # between CommentDirective (45) and ScDirective (99)
|
|
144
|
+
|
|
145
|
+
def __init__(self, dp="SC", tp="+"):
|
|
146
|
+
super().__init__(dp, tp)
|
|
147
|
+
self._re = re.compile(rf"^{re.escape(tp)} : {re.escape(dp)} pause (\d+)$")
|
|
148
|
+
|
|
149
|
+
def post(self, queue):
|
|
150
|
+
ts, content = queue[0]
|
|
151
|
+
m = self._re.match(content)
|
|
152
|
+
if not m:
|
|
153
|
+
return None
|
|
154
|
+
queue.popleft()
|
|
155
|
+
ms = m.group(1)
|
|
156
|
+
return [json.dumps([ts, "directive", f"sleep {ms}"])]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```toml
|
|
160
|
+
# mypkg/pyproject.toml
|
|
161
|
+
[project.entry-points."scriptcast.directives"]
|
|
162
|
+
pause = "mypkg.directives:PauseDirective"
|
|
163
|
+
```
|