mirrordash-sdk 0.2.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.
Files changed (36) hide show
  1. mirrordash_sdk-0.2.0/.github/workflows/publish.yml +68 -0
  2. mirrordash_sdk-0.2.0/.github/workflows/test.yml +36 -0
  3. mirrordash_sdk-0.2.0/.gitignore +28 -0
  4. mirrordash_sdk-0.2.0/LICENSE.md +137 -0
  5. mirrordash_sdk-0.2.0/MODULE_AGENTS.md +112 -0
  6. mirrordash_sdk-0.2.0/MODULE_GUIDE.md +685 -0
  7. mirrordash_sdk-0.2.0/PKG-INFO +196 -0
  8. mirrordash_sdk-0.2.0/README.md +48 -0
  9. mirrordash_sdk-0.2.0/mirrordash_sdk/__init__.py +2 -0
  10. mirrordash_sdk-0.2.0/mirrordash_sdk/cli.py +86 -0
  11. mirrordash_sdk-0.2.0/mirrordash_sdk/manager.py +384 -0
  12. mirrordash_sdk-0.2.0/mirrordash_sdk/scaffolder.py +111 -0
  13. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/README.md +36 -0
  14. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/__package__/config_schema.json +34 -0
  15. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/__package__/plugin.py +73 -0
  16. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/__package__/templates/widget.html +18 -0
  17. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/__package__/translations/en.json +8 -0
  18. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/__package__/translations/sv.json +8 -0
  19. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/api/tests/test_plugin.py +54 -0
  20. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/README.md +22 -0
  21. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/__package__/config_schema.json +21 -0
  22. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/__package__/plugin.py +30 -0
  23. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/__package__/templates/widget.html +10 -0
  24. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/__package__/translations/en.json +4 -0
  25. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/__package__/translations/sv.json +4 -0
  26. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/basic/tests/test_plugin.py +23 -0
  27. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/common/__package__/__init__.py +0 -0
  28. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/common/__package__/icon.svg +5 -0
  29. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/common/gitignore +15 -0
  30. mirrordash_sdk-0.2.0/mirrordash_sdk/templates/common/pyproject.toml +26 -0
  31. mirrordash_sdk-0.2.0/mirrordash_sdk/validator.py +240 -0
  32. mirrordash_sdk-0.2.0/pyproject.toml +36 -0
  33. mirrordash_sdk-0.2.0/scripts/release.py +100 -0
  34. mirrordash_sdk-0.2.0/tests/test_cli.py +213 -0
  35. mirrordash_sdk-0.2.0/tests/test_templates.py +80 -0
  36. mirrordash_sdk-0.2.0/uv.lock +111 -0
@@ -0,0 +1,68 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes mirrordash-sdk to PyPI when a tag vX.Y.Z is pushed (git tag v0.2.0 && git push origin v0.2.0).
4
+ # PyPI trusts the publish job in the pypi environment (trusted publishing), so no token is stored.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ uses: ./.github/workflows/test.yml
15
+
16
+ build:
17
+ name: Build package
18
+ runs-on: ubuntu-24.04
19
+ timeout-minutes: 10
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v7
24
+
25
+ - name: Set up uv
26
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
27
+
28
+ # PyPI publishes whatever pyproject.toml says, so a mismatched tag would ship the wrong version under it
29
+ - name: Check the tag matches pyproject.toml
30
+ env:
31
+ TAG: ${{ github.ref_name }}
32
+ run: |
33
+ VERSION=$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
34
+ if [ "$TAG" != "v$VERSION" ]; then
35
+ echo "::error::Tag $TAG does not match pyproject.toml version $VERSION (expected v$VERSION)"
36
+ exit 1
37
+ fi
38
+
39
+ - name: Build package distribution
40
+ run: uv build
41
+
42
+ - name: Save package
43
+ uses: actions/upload-artifact@v7
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+
48
+ # Only this job can publish (id-token), and it runs no build code: it uploads what the build job made
49
+ publish:
50
+ name: Publish to PyPI
51
+ needs: [test, build]
52
+ runs-on: ubuntu-24.04
53
+ timeout-minutes: 10
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/p/mirrordash-sdk
57
+ permissions:
58
+ id-token: write
59
+
60
+ steps:
61
+ - name: Download package
62
+ uses: actions/download-artifact@v8
63
+ with:
64
+ name: dist
65
+ path: dist/
66
+
67
+ - name: Publish package to PyPI
68
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -0,0 +1,36 @@
1
+ name: Tests
2
+
3
+ # Runs the tests on every push to master, and before every PyPI publish (publish.yml calls it).
4
+ # The tests also generate each module template and run that module's own tests.
5
+ on:
6
+ push:
7
+ branches: [master]
8
+ workflow_dispatch:
9
+ workflow_call:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ # A newer push replaces a running test of an older one; runs called by publish are never cancelled
15
+ concurrency:
16
+ group: test-${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: ${{ github.event_name == 'push' }}
18
+
19
+ jobs:
20
+ test:
21
+ name: Tests
22
+ runs-on: ubuntu-24.04
23
+ timeout-minutes: 15
24
+
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v7
28
+
29
+ - name: Set up uv
30
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
31
+
32
+ - name: Install dependencies
33
+ run: uv sync --frozen
34
+
35
+ - name: Run tests
36
+ run: uv run pytest -q
@@ -0,0 +1,28 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ .venv/
5
+ venv/
6
+ ENV/
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+
25
+ # IntelliJ IDEA
26
+ .idea/
27
+ *.iml
28
+
@@ -0,0 +1,137 @@
1
+ # MirrorDash License
2
+
3
+ Required Notice: Copyright (C) 2026 Jonas Öhlander (https://github.com/Menturan/MirrorDash)
4
+
5
+ ---
6
+
7
+ # PolyForm Noncommercial License 1.0.0
8
+
9
+ <https://polyformproject.org/licenses/noncommercial/1.0.0>
10
+
11
+ ## Acceptance
12
+
13
+ In order to get any license under these terms, you must agree
14
+ to them as both strict obligations and conditions to all
15
+ your licenses.
16
+
17
+ ## Copyright License
18
+
19
+ The licensor grants you a copyright license for the
20
+ software to do everything you might do with the software
21
+ that would otherwise infringe the licensor's copyright
22
+ in it for any permitted purpose. However, you may
23
+ only distribute the software according to [Distribution
24
+ License](#distribution-license) and make changes or new works
25
+ based on the software according to [Changes and New Works
26
+ License](#changes-and-new-works-license).
27
+
28
+ ## Distribution License
29
+
30
+ The licensor grants you an additional copyright license
31
+ to distribute copies of the software. Your license
32
+ to distribute covers distributing the software with
33
+ changes and new works permitted by [Changes and New Works
34
+ License](#changes-and-new-works-license).
35
+
36
+ ## Notices
37
+
38
+ You must ensure that anyone who gets a copy of any part of
39
+ the software from you also gets a copy of these terms or the
40
+ URL for them above, as well as copies of any plain-text lines
41
+ beginning with `Required Notice:` that the licensor provided
42
+ with the software. For example:
43
+
44
+ > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
45
+
46
+ ## Changes and New Works License
47
+
48
+ The licensor grants you an additional copyright license to
49
+ make changes and new works based on the software for any
50
+ permitted purpose.
51
+
52
+ ## Patent License
53
+
54
+ The licensor grants you a patent license for the software that
55
+ covers patent claims the licensor can license, or becomes able
56
+ to license, that you would infringe by using the software.
57
+
58
+ ## Noncommercial Purposes
59
+
60
+ Any noncommercial purpose is a permitted purpose.
61
+
62
+ ## Personal Uses
63
+
64
+ Personal use for research, experiment, and testing for
65
+ the benefit of public knowledge, personal study, private
66
+ entertainment, hobby projects, amateur pursuits, or religious
67
+ observance, without any anticipated commercial application,
68
+ is use for a permitted purpose.
69
+
70
+ ## Noncommercial Organizations
71
+
72
+ Use by any charitable organization, educational institution,
73
+ public research organization, public safety or health
74
+ organization, environmental protection organization,
75
+ or government institution is use for a permitted purpose
76
+ regardless of the source of funding or obligations resulting
77
+ from the funding.
78
+
79
+ ## Fair Use
80
+
81
+ You may have "fair use" rights for the software under the
82
+ law. These terms do not limit them.
83
+
84
+ ## No Other Rights
85
+
86
+ These terms do not allow you to sublicense or transfer any of
87
+ your licenses to anyone else, or prevent the licensor from
88
+ granting licenses to anyone else. These terms do not imply
89
+ any other licenses.
90
+
91
+ ## Patent Defense
92
+
93
+ If you make any written claim that the software infringes or
94
+ contributes to infringement of any patent, your patent license
95
+ for the software granted under these terms ends immediately. If
96
+ your company makes such a claim, your patent license ends
97
+ immediately for work on behalf of your company.
98
+
99
+ ## Violations
100
+
101
+ The first time you are notified in writing that you have
102
+ violated any of these terms, or done anything with the software
103
+ not covered by your licenses, your licenses can nonetheless
104
+ continue if you come into full compliance with these terms,
105
+ and take practical steps to correct past violations, within
106
+ 32 days of receiving notice. Otherwise, all your licenses
107
+ end immediately.
108
+
109
+ ## No Liability
110
+
111
+ ***As far as the law allows, the software comes as is, without
112
+ any warranty or condition, and the licensor will not be liable
113
+ to you for any damages arising out of these terms or the use
114
+ or nature of the software, under any kind of legal claim.***
115
+
116
+ ## Definitions
117
+
118
+ The **licensor** is the individual or entity offering these
119
+ terms, and the **software** is the software the licensor makes
120
+ available under these terms.
121
+
122
+ **You** refers to the individual or entity agreeing to these
123
+ terms.
124
+
125
+ **Your company** is any legal entity, sole proprietorship,
126
+ or other kind of organization that you work for, plus all
127
+ organizations that have control over, are under the control of,
128
+ or are under common control with that organization. **Control**
129
+ means ownership of substantially all the assets of an entity,
130
+ or the power to direct its management and policies by vote,
131
+ contract, or otherwise. Control can be direct or indirect.
132
+
133
+ **Your licenses** are all the licenses granted to you for the
134
+ software under these terms.
135
+
136
+ **Use** means anything you do with the software requiring one
137
+ of your licenses.
@@ -0,0 +1,112 @@
1
+ # MODULE_AGENTS.md — MirrorDash Module AI Agent Guide
2
+
3
+ This file provides AI coding agents with instructions for creating, modifying, and testing custom modules for MirrorDash. Read this before developing any module.
4
+
5
+ ---
6
+
7
+ ## 1. Developer Environment & Scaffolding
8
+
9
+ ### Always Use the CLI Scaffolder
10
+ Never build module packages by hand. Create a module from a template; pick `api` when it fetches data from a web API, `basic` otherwise:
11
+ ```bash
12
+ uvx mirrordash-sdk create-module mirrordash-<name> --template api --description "<description>"
13
+ ```
14
+ This gives the right Hatchling layout, entry point, settings form, translations and tests (`uv run pytest`).
15
+
16
+ ### A Local Mirror
17
+ Inside the module directory, set up a local mirror with the module on it and start it (`http://localhost:8000/`, admin password `mirrordash`):
18
+ ```bash
19
+ uvx mirrordash-sdk dev-setup -e
20
+ uvx mirrordash-sdk start
21
+ ```
22
+ `uvx mirrordash-sdk quickstart mirrordash-<name>` does all of the above in one go and opens the browser.
23
+
24
+ ---
25
+
26
+ ## 2. Coding Rules for Modules
27
+
28
+ ### Async & Non-Blocking Loops
29
+ 0. **Fetch with `self.fetch_json`**: never write your own HTTP code for JSON APIs. `data, error = await self.fetch_json(url, headers=..., params=...)` has a timeout, doesn't block, and on a failure returns the last good answer with `error` set (`rejected`, `offline`, `http <code>`, `invalid`). Put API keys in `headers`, never in the URL. Show `data` when there is any and a `.module-message` when `error` is set. In tests: `module.fetch_json = AsyncMock(return_value=(data, None))`.
30
+ 1. **Never block the event loop**: The module's `run_loop` is an async task. Do not use blocking operations (like `requests`, `urllib.request.urlopen`, or `time.sleep()`).
31
+ 2. **Wrap Synchronous I/O**: Use `await asyncio.to_thread(sync_func, ...)` to offload synchronous network or disk reads.
32
+ 3. **Use Async Sleeps**: Sleep only with `await asyncio.sleep(self.interval)`.
33
+ 4. **Handle Shutdown Cleanly**: Always catch and re-raise `asyncio.CancelledError` to allow graceful stops:
34
+ ```python
35
+ except asyncio.CancelledError:
36
+ # perform cleanup...
37
+ raise
38
+ ```
39
+
40
+ ### State & Persistence (OverlayFS Compliance)
41
+ The system runs on a read-only root filesystem in production.
42
+ - **Do not write to package directories**: Never open files relative to `__file__` for writing.
43
+ - **Use Injected Paths**:
44
+ - `self.data_dir`: Write SQLite databases, configurations, and user state here (backed up).
45
+ - `self.cache_dir`: Write temporary data, HTTP response caches, and asset downloads here (excluded from backups).
46
+
47
+ ---
48
+
49
+ ## 3. Localization & Globals
50
+
51
+ ### Respect Global Configurations
52
+ Never hardcode locales, metric units, or time zones. Fall back to settings defined under the global configuration:
53
+
54
+ ```python
55
+ def __init__(self, config):
56
+ global_cfg = config.get("globals", {})
57
+ self.language = global_cfg.get("language", "en")
58
+ self.timezone = global_cfg.get("timezone", "Europe/Stockholm")
59
+ self.time_format = global_cfg.get("time_format", "24h")
60
+ self.temp_unit = global_cfg.get("temperature_unit", "C")
61
+ ```
62
+
63
+ ### Localization Mappings
64
+ 1. Place translation dictionaries in a `translations/` directory (e.g. `en.json`, `sv.json`) inside your package folder.
65
+ 2. The core automatically resolves and merges `translations` onto the Jinja2 render context as `translations` and attaches them to `self.translations`.
66
+ 3. Use the injected helper `self.translate("key", "default_val")` inside Python files.
67
+
68
+ ---
69
+
70
+ ## 4. UI Design & Styling Rules
71
+
72
+ All module designs must adhere to the MirrorDash **Ethereal Design System** to preserve visual unity on a dark ambient mirror display:
73
+
74
+ - **Transparent Backgrounds**: Widgets float on a pure black background. Never define a custom background color on the module wrapper.
75
+ - **Building blocks first**: lay out with the classes every module gets (no CSS needed): `.module-header`, `.display-xl`/`.display-lg` for big numbers, `.flex-row`, `.flex-row-between` (label–value rows), `.flex-column`, `.text-primary`/`.text-secondary`, `.module-message` for errors and empty states. See `http://localhost:8000/design`.
76
+ - **HUD Contrast**: For your own CSS, use the design tokens:
77
+ - Primary text/numbers: `var(--color-high-contrast)` (#ffffff)
78
+ - Secondary metadata/labels: `var(--color-standard-gray)` (#999999)
79
+ - Dimmed borders/dividers: `var(--color-dimmed-charcoal)` (#666666)
80
+ - **Vector Icons**: Use Lucide outline icons via standard HTML attributes. Do not use emoji icons:
81
+ ```html
82
+ <i data-lucide="cloud-rain"></i>
83
+ ```
84
+ - **Translation-Safe Responsive Fluidity**:
85
+ - **No fixed widths**: Do not use hardcoded pixel widths (`width: 150px`) for lists, columns, or layout elements. Swedish, German, or French text strings can be twice as long as English.
86
+ - Use Flexbox or CSS Grid with flexible sizing (`flex: 1`, `min-width: 0`, `max-content`).
87
+ - Use text-overflow ellipsis utilities to handle long strings gracefully.
88
+ - **Shadow DOM Encapsulation**: Remember that modules render inside Shadow DOM. CSS beyond the building blocks goes in a template `<style>` block; other page styles do not cascade in, and global scripts (`document.querySelector`) cannot select your module's elements. Every template `<script>` gets `root` (the module's shadow root): query from it and keep timers on it (`clearInterval(root._timer); root._timer = setInterval(...)`). Never use `document.currentScript` (null in a shadow root) or globals (two instances would share them).
89
+
90
+ ---
91
+
92
+ ## 5. Configuration UI (Admin Dashboard Integration)
93
+
94
+ Provide a `config_schema` dictionary or a `config_schema.json` to allow the user to modify settings from the Admin panel UI.
95
+
96
+ - Use basic JSON schema types (`boolean`, `string`, `integer`).
97
+ - Define title, description, and sensible default values.
98
+ - If providing a set of selections, use `enum` to render a dropdown.
99
+ - API keys, tokens and passwords get `"format": "password"`, so the admin page hides what is typed (`mirrordash-sdk validate` warns otherwise).
100
+ - Don't declare `enabled`, `position`, `carousel_group`, `carousel_interval`, `max_width`, `max_height`, `z_index` or `opacity`: the core adds them to every module.
101
+ - `self.render_template` / `self.translate` don't exist yet inside `__init__`; use them from `run_loop`.
102
+
103
+ ---
104
+
105
+ ## 6. Checklist for Module Commits
106
+ Before submitting a pull request or code change:
107
+ 1. Ensure the package naming matches Hatchling requirements (folder uses hyphens, package/source uses underscores).
108
+ 2. Ensure you have provided a `README.md` at the module root containing:
109
+ - Clear description.
110
+ - Step-by-step setup instructions for API keys/tokens (if any).
111
+ - A `screenshot.png` at the package root, referenced in the markdown.
112
+ 3. Run the test suite: `uv run pytest`, and `uvx mirrordash-sdk validate .`.