django-huddle 0.1.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.
- django_huddle-0.1.0/.editorconfig +37 -0
- django_huddle-0.1.0/.github/dependabot.yml +24 -0
- django_huddle-0.1.0/.github/pull_request_template.md +17 -0
- django_huddle-0.1.0/.github/workflows/ci.yml +120 -0
- django_huddle-0.1.0/.github/workflows/codeql.yml +34 -0
- django_huddle-0.1.0/.github/workflows/publish.yml +62 -0
- django_huddle-0.1.0/.github/workflows/zizmor.yml +36 -0
- django_huddle-0.1.0/.gitignore +63 -0
- django_huddle-0.1.0/.pre-commit-config.yaml +45 -0
- django_huddle-0.1.0/AGENTS.md +44 -0
- django_huddle-0.1.0/CHANGELOG.md +12 -0
- django_huddle-0.1.0/LICENSE +21 -0
- django_huddle-0.1.0/PKG-INFO +278 -0
- django_huddle-0.1.0/README.md +247 -0
- django_huddle-0.1.0/docs/adr/0001-build-chat-on-channels.md +48 -0
- django_huddle-0.1.0/docs/adr/0002-single-chat-policy.md +36 -0
- django_huddle-0.1.0/docs/adr/0003-ids-not-html-over-the-channel-layer.md +32 -0
- django_huddle-0.1.0/docs/adr/0004-token-message-format.md +28 -0
- django_huddle-0.1.0/docs/adr/0005-overridable-components-and-api.md +35 -0
- django_huddle-0.1.0/docs/adr/0006-django-6-floor.md +19 -0
- django_huddle-0.1.0/docs/adr/index.md +14 -0
- django_huddle-0.1.0/example/__init__.py +0 -0
- django_huddle-0.1.0/example/asgi.py +22 -0
- django_huddle-0.1.0/example/settings.py +64 -0
- django_huddle-0.1.0/example/templates/example_base.html +13 -0
- django_huddle-0.1.0/example/urls.py +11 -0
- django_huddle-0.1.0/example/wsgi.py +8 -0
- django_huddle-0.1.0/justfile +28 -0
- django_huddle-0.1.0/manage.py +11 -0
- django_huddle-0.1.0/pyproject.toml +143 -0
- django_huddle-0.1.0/src/huddle/__init__.py +6 -0
- django_huddle-0.1.0/src/huddle/api/__init__.py +4 -0
- django_huddle-0.1.0/src/huddle/api/serializers.py +50 -0
- django_huddle-0.1.0/src/huddle/api/urls.py +16 -0
- django_huddle-0.1.0/src/huddle/api/views.py +255 -0
- django_huddle-0.1.0/src/huddle/apps.py +17 -0
- django_huddle-0.1.0/src/huddle/conf.py +54 -0
- django_huddle-0.1.0/src/huddle/consumers.py +179 -0
- django_huddle-0.1.0/src/huddle/exceptions.py +54 -0
- django_huddle-0.1.0/src/huddle/forms.py +89 -0
- django_huddle-0.1.0/src/huddle/markup.py +93 -0
- django_huddle-0.1.0/src/huddle/migrations/0001_initial.py +112 -0
- django_huddle-0.1.0/src/huddle/migrations/__init__.py +0 -0
- django_huddle-0.1.0/src/huddle/models.py +194 -0
- django_huddle-0.1.0/src/huddle/payloads.py +87 -0
- django_huddle-0.1.0/src/huddle/policy.py +121 -0
- django_huddle-0.1.0/src/huddle/py.typed +0 -0
- django_huddle-0.1.0/src/huddle/querysets.py +137 -0
- django_huddle-0.1.0/src/huddle/realtime.py +58 -0
- django_huddle-0.1.0/src/huddle/rendering.py +117 -0
- django_huddle-0.1.0/src/huddle/routing.py +11 -0
- django_huddle-0.1.0/src/huddle/services.py +248 -0
- django_huddle-0.1.0/src/huddle/signals.py +17 -0
- django_huddle-0.1.0/src/huddle/static/huddle/huddle.css +254 -0
- django_huddle-0.1.0/src/huddle/static/huddle/huddle.js +134 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/base.html +13 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/browse_page.html +2 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/channel_page.html +2 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/browse_list.html +19 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/channel_pane.html +19 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/composer.html +8 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/composer_fields.html +6 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/edit_form.html +7 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/mention_results.html +6 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/message.html +27 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/posted.html +2 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/replied.html +2 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/reply_composer.html +8 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/sidebar.html +22 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/sidebar_entry.html +8 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/thread.html +12 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/thread_summary.html +8 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/timeline_page.html +9 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/ws_message.html +8 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/components/ws_sidebar_entry.html +1 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/create_channel_page.html +12 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/home_page.html +12 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/layout.html +11 -0
- django_huddle-0.1.0/src/huddle/templates/huddle/new_direct_page.html +12 -0
- django_huddle-0.1.0/src/huddle/urls.py +27 -0
- django_huddle-0.1.0/src/huddle/views.py +419 -0
- django_huddle-0.1.0/tests/__init__.py +0 -0
- django_huddle-0.1.0/tests/conftest.py +76 -0
- django_huddle-0.1.0/tests/settings.py +8 -0
- django_huddle-0.1.0/tests/test_api.py +123 -0
- django_huddle-0.1.0/tests/test_consumers.py +129 -0
- django_huddle-0.1.0/tests/test_markup_and_policy.py +114 -0
- django_huddle-0.1.0/tests/test_services.py +202 -0
- django_huddle-0.1.0/tests/test_views.py +192 -0
- django_huddle-0.1.0/tox.ini +21 -0
- django_huddle-0.1.0/uv.lock +1481 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# https://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
charset = utf-8
|
|
6
|
+
end_of_line = lf
|
|
7
|
+
indent_style = space
|
|
8
|
+
indent_size = 4
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
insert_final_newline = true
|
|
11
|
+
|
|
12
|
+
[*.{html,css,js,json,sh,yml,yaml}]
|
|
13
|
+
indent_size = 2
|
|
14
|
+
|
|
15
|
+
[*.bat]
|
|
16
|
+
indent_style = tab
|
|
17
|
+
end_of_line = crlf
|
|
18
|
+
|
|
19
|
+
[LICENSE]
|
|
20
|
+
insert_final_newline = false
|
|
21
|
+
|
|
22
|
+
[justfile]
|
|
23
|
+
indent_style = space
|
|
24
|
+
indent_size = 4
|
|
25
|
+
|
|
26
|
+
# Ignore binary or generated files
|
|
27
|
+
[*.{png,jpg,gif,ico,woff,woff2,ttf,eot,svg,pdf}]
|
|
28
|
+
charset = unset
|
|
29
|
+
end_of_line = unset
|
|
30
|
+
indent_style = unset
|
|
31
|
+
indent_size = unset
|
|
32
|
+
trim_trailing_whitespace = unset
|
|
33
|
+
insert_final_newline = unset
|
|
34
|
+
max_line_length = unset
|
|
35
|
+
|
|
36
|
+
[*.{diff,patch}]
|
|
37
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: github-actions
|
|
4
|
+
directory: /
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
day: monday
|
|
8
|
+
time: "07:00"
|
|
9
|
+
timezone: Europe/London
|
|
10
|
+
cooldown:
|
|
11
|
+
default-days: 7
|
|
12
|
+
open-pull-requests-limit: 5
|
|
13
|
+
|
|
14
|
+
# Cooldown matches exclude-newer in pyproject.toml, so uv can always lock it.
|
|
15
|
+
- package-ecosystem: uv
|
|
16
|
+
directory: /
|
|
17
|
+
schedule:
|
|
18
|
+
interval: weekly
|
|
19
|
+
day: monday
|
|
20
|
+
time: "07:00"
|
|
21
|
+
timezone: Europe/London
|
|
22
|
+
cooldown:
|
|
23
|
+
default-days: 7
|
|
24
|
+
open-pull-requests-limit: 5
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## What
|
|
2
|
+
|
|
3
|
+
<!-- 1-3 sentences. Link the issue if there is one. -->
|
|
4
|
+
|
|
5
|
+
## AI Provenance
|
|
6
|
+
|
|
7
|
+
<!-- Remove this section for human-authored PRs. -->
|
|
8
|
+
|
|
9
|
+
- **Tool:** <!-- Claude Code, Cursor, Copilot, Codex, etc. -->
|
|
10
|
+
- **Model:** <!-- Claude Opus 4.6, GPT-4o, etc. -->
|
|
11
|
+
- **Prompt/thread:** <!-- Paste your prompt or a link to the thread. -->
|
|
12
|
+
|
|
13
|
+
## Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Addresses exactly one issue or feature
|
|
16
|
+
- [ ] New or changed behavior has test coverage
|
|
17
|
+
- [ ] Diff contains only changes for this task
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
types:
|
|
10
|
+
- opened
|
|
11
|
+
- synchronize
|
|
12
|
+
- reopened
|
|
13
|
+
- ready_for_review
|
|
14
|
+
|
|
15
|
+
permissions: {}
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
lint:
|
|
23
|
+
name: Lint
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
|
+
with:
|
|
30
|
+
persist-credentials: false
|
|
31
|
+
|
|
32
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
33
|
+
|
|
34
|
+
- name: Ruff format check
|
|
35
|
+
run: uv run ruff format --check .
|
|
36
|
+
|
|
37
|
+
- name: Ruff lint
|
|
38
|
+
run: uv run ruff check .
|
|
39
|
+
|
|
40
|
+
type-check:
|
|
41
|
+
name: Type check
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
permissions:
|
|
44
|
+
contents: read
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
47
|
+
with:
|
|
48
|
+
persist-credentials: false
|
|
49
|
+
|
|
50
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
51
|
+
|
|
52
|
+
- name: Type check with mypy
|
|
53
|
+
run: uv run mypy src/ tests/ example/
|
|
54
|
+
|
|
55
|
+
test:
|
|
56
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
strategy:
|
|
59
|
+
fail-fast: false
|
|
60
|
+
matrix:
|
|
61
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
62
|
+
permissions:
|
|
63
|
+
contents: read
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
66
|
+
with:
|
|
67
|
+
persist-credentials: false
|
|
68
|
+
|
|
69
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
70
|
+
|
|
71
|
+
- name: Run tox envs for Python ${{ matrix.python-version }}
|
|
72
|
+
run: uvx --with tox-uv tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)
|
|
73
|
+
|
|
74
|
+
- name: Upload coverage data
|
|
75
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
76
|
+
with:
|
|
77
|
+
name: coverage-${{ matrix.python-version }}
|
|
78
|
+
path: .coverage.*
|
|
79
|
+
include-hidden-files: true
|
|
80
|
+
if-no-files-found: error
|
|
81
|
+
|
|
82
|
+
coverage:
|
|
83
|
+
name: Coverage
|
|
84
|
+
needs: [test]
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
permissions:
|
|
87
|
+
contents: read
|
|
88
|
+
steps:
|
|
89
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
90
|
+
with:
|
|
91
|
+
persist-credentials: false
|
|
92
|
+
|
|
93
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
94
|
+
|
|
95
|
+
- name: Download coverage data
|
|
96
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
97
|
+
with:
|
|
98
|
+
pattern: coverage-*
|
|
99
|
+
merge-multiple: true
|
|
100
|
+
|
|
101
|
+
- name: Combine coverage
|
|
102
|
+
run: uv run coverage combine
|
|
103
|
+
|
|
104
|
+
- name: Coverage report
|
|
105
|
+
run: |
|
|
106
|
+
echo '## Coverage Report' >> $GITHUB_STEP_SUMMARY
|
|
107
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
108
|
+
uv run coverage report | tee -a $GITHUB_STEP_SUMMARY
|
|
109
|
+
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
110
|
+
|
|
111
|
+
all-checks-pass:
|
|
112
|
+
name: All checks pass
|
|
113
|
+
if: always()
|
|
114
|
+
needs: [lint, type-check, test, coverage]
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
steps:
|
|
117
|
+
- name: Check job results
|
|
118
|
+
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
|
|
119
|
+
with:
|
|
120
|
+
jobs: ${{ toJSON(needs) }}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 6 * * 1'
|
|
10
|
+
|
|
11
|
+
permissions: {}
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
analyze:
|
|
15
|
+
name: Analyze
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
packages: read
|
|
20
|
+
security-events: write
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- name: Initialize CodeQL
|
|
27
|
+
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
|
28
|
+
with:
|
|
29
|
+
languages: python # Add c-cpp, go, java-kotlin, or swift if needed
|
|
30
|
+
build-mode: none # Switch to autobuild for compiled languages
|
|
31
|
+
queries: security-extended
|
|
32
|
+
|
|
33
|
+
- name: Perform CodeQL Analysis
|
|
34
|
+
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: publish
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
name: Build distribution
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
|
|
24
|
+
# Cache disabled to prevent cache-poisoning attacks on release artifacts.
|
|
25
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: false
|
|
28
|
+
|
|
29
|
+
- name: Build
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Upload distribution
|
|
33
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
name: dist
|
|
36
|
+
path: dist/
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
name: Publish to PyPI
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write
|
|
44
|
+
attestations: write
|
|
45
|
+
# Needs Settings > Environments > pypi: reviewers + v* tag restriction.
|
|
46
|
+
environment:
|
|
47
|
+
name: pypi
|
|
48
|
+
url: https://pypi.org/p/django-huddle
|
|
49
|
+
steps:
|
|
50
|
+
- name: Download distribution
|
|
51
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
|
|
56
|
+
- name: Attest build provenance
|
|
57
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
58
|
+
with:
|
|
59
|
+
subject-path: "dist/*"
|
|
60
|
+
|
|
61
|
+
- name: Publish to PyPI
|
|
62
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Workflow security analysis
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- ".github/workflows/**"
|
|
9
|
+
pull_request:
|
|
10
|
+
types:
|
|
11
|
+
- opened
|
|
12
|
+
- synchronize
|
|
13
|
+
- reopened
|
|
14
|
+
- ready_for_review
|
|
15
|
+
paths:
|
|
16
|
+
- ".github/workflows/**"
|
|
17
|
+
|
|
18
|
+
permissions: {}
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
zizmor:
|
|
22
|
+
name: Run zizmor
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
actions: read
|
|
27
|
+
security-events: write
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
|
+
with:
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
|
|
33
|
+
- name: Run zizmor
|
|
34
|
+
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
|
|
35
|
+
with:
|
|
36
|
+
inputs: ./.github/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
*.egg
|
|
16
|
+
MANIFEST
|
|
17
|
+
|
|
18
|
+
# Unit test / coverage reports
|
|
19
|
+
htmlcov/
|
|
20
|
+
.tox/
|
|
21
|
+
.nox/
|
|
22
|
+
.coverage
|
|
23
|
+
.coverage.*
|
|
24
|
+
coverage.xml
|
|
25
|
+
.hypothesis/
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
|
|
28
|
+
# Environments
|
|
29
|
+
.env
|
|
30
|
+
.envrc
|
|
31
|
+
.venv
|
|
32
|
+
|
|
33
|
+
# uv
|
|
34
|
+
# Generally recommended to include uv.lock in version control for
|
|
35
|
+
# reproducibility. Uncomment for libraries where you want consumers
|
|
36
|
+
# to resolve their own dependencies.
|
|
37
|
+
# uv.lock
|
|
38
|
+
|
|
39
|
+
# Ruff
|
|
40
|
+
.ruff_cache/
|
|
41
|
+
|
|
42
|
+
# Type checkers
|
|
43
|
+
.mypy_cache/
|
|
44
|
+
.dmypy.json
|
|
45
|
+
dmypy.json
|
|
46
|
+
.pyre/
|
|
47
|
+
.pytype/
|
|
48
|
+
|
|
49
|
+
# Example project local database
|
|
50
|
+
example/db.sqlite3
|
|
51
|
+
|
|
52
|
+
# Documentation build output
|
|
53
|
+
site/
|
|
54
|
+
|
|
55
|
+
# IDE files
|
|
56
|
+
# .idea/
|
|
57
|
+
# .vscode/
|
|
58
|
+
|
|
59
|
+
# Local agent guidance (not a committed team convention)
|
|
60
|
+
CLAUDE.md
|
|
61
|
+
example/db.sqlite3
|
|
62
|
+
.venv/
|
|
63
|
+
site/
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
ci:
|
|
2
|
+
autoupdate_schedule: monthly
|
|
3
|
+
skip: [mypy]
|
|
4
|
+
|
|
5
|
+
default_language_version:
|
|
6
|
+
python: python3.13
|
|
7
|
+
|
|
8
|
+
repos:
|
|
9
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
10
|
+
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: check-added-large-files
|
|
13
|
+
- id: check-case-conflict
|
|
14
|
+
- id: check-merge-conflict
|
|
15
|
+
- id: check-toml
|
|
16
|
+
- id: check-yaml
|
|
17
|
+
- id: end-of-file-fixer
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
21
|
+
rev: 3b42b9776870badc9eaffdef9a4a0834b764d87c # frozen: v2.29.3
|
|
22
|
+
hooks:
|
|
23
|
+
- id: pyproject-fmt
|
|
24
|
+
|
|
25
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
26
|
+
rev: 321478e58f4938179c6b86e4ddfa923d1547a49b # frozen: v0.16.6
|
|
27
|
+
hooks:
|
|
28
|
+
- id: ruff-check
|
|
29
|
+
args: [--fix]
|
|
30
|
+
- id: ruff-format
|
|
31
|
+
|
|
32
|
+
- repo: https://github.com/adamchainz/django-upgrade
|
|
33
|
+
rev: e668ea858b908d23a9702b24214f4fc5d07ed50f # frozen: 1.32.0
|
|
34
|
+
hooks:
|
|
35
|
+
- id: django-upgrade
|
|
36
|
+
args: [--target-version, "6.0"]
|
|
37
|
+
|
|
38
|
+
- repo: local
|
|
39
|
+
hooks:
|
|
40
|
+
- id: mypy
|
|
41
|
+
name: mypy
|
|
42
|
+
entry: uv run mypy src/ tests/ example/
|
|
43
|
+
language: system
|
|
44
|
+
types: [python]
|
|
45
|
+
pass_filenames: false
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# django-huddle — agent guidance
|
|
2
|
+
|
|
3
|
+
A public, MIT-licensed Django package providing Slack-style chat (channels, DMs,
|
|
4
|
+
threads, @mentions) over Django Channels. Other projects depend on it, so the
|
|
5
|
+
**public API is a maintenance promise**.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv run pytest # quick run
|
|
11
|
+
uvx --with tox-uv tox run -f py314 # Django 6.0 + 6.1 matrix
|
|
12
|
+
uv run ruff format . && uv run ruff check --fix .
|
|
13
|
+
uv run mypy src/ tests/ example/
|
|
14
|
+
pre-commit run --all-files # before every commit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Invariants
|
|
18
|
+
|
|
19
|
+
1. **Every permission decision goes through `ChatPolicy`** (ADR-0002). Never add
|
|
20
|
+
an inline `if user ...` access check in a view, API view or consumer. Add or
|
|
21
|
+
extend a policy method instead.
|
|
22
|
+
2. **Invisible means 404.** Look channels and messages up through
|
|
23
|
+
`Channel.objects.visible_to(user)`.
|
|
24
|
+
3. **Every write goes through `ChatService`.** It keeps counters and read state
|
|
25
|
+
consistent and sends signals and broadcasts on commit.
|
|
26
|
+
4. **Websocket events carry ids, never HTML** (ADR-0003). Each connection
|
|
27
|
+
re-checks and re-renders for its own user.
|
|
28
|
+
5. **Three outputs, two builders** (ADR-0005). Templates use `ChatRenderer`
|
|
29
|
+
view models; the REST API and websocket JSON use `ChatPayloads`. Keep them in
|
|
30
|
+
step.
|
|
31
|
+
6. **Stored bodies are tokens plus plain text** (ADR-0004). Never store HTML.
|
|
32
|
+
|
|
33
|
+
## Style
|
|
34
|
+
|
|
35
|
+
- Django 6+, Python 3.12+, `from __future__ import annotations`, mypy strict.
|
|
36
|
+
- Query logic lives on QuerySets (`querysets.py`), with docstrings saying what
|
|
37
|
+
each method selects and where it's used.
|
|
38
|
+
- Guard clauses, max nesting depth 2, intention-revealing names.
|
|
39
|
+
- Docstrings carry the why. `#` comments are one line at most. No comments in
|
|
40
|
+
templates, JS or CSS unless essential.
|
|
41
|
+
- Each failure mode gets its own exception under `HuddleError`, carrying
|
|
42
|
+
`context`.
|
|
43
|
+
- One template per UI component under `templates/huddle/components/`.
|
|
44
|
+
- Every PR carries an ADR in `docs/adr/` for each distinct decision.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Channels (public and private), direct and group messages, one-level threads, @mentions.
|
|
8
|
+
- `ChatPolicy` permission hook with scoped channels.
|
|
9
|
+
- `ChatService` for every write, with `message_posted`, `user_mentioned` and `thread_replied` signals.
|
|
10
|
+
- htmx UI built from overridable component templates.
|
|
11
|
+
- Websocket consumer (HTML or JSON mode) and `HuddleConsumerMixin`.
|
|
12
|
+
- Optional REST API (`django-huddle[api]`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Ben Atkinson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|