Compare commits
120
Commits
b12c211a69
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae4a4f0824 | ||
|
|
8d4aa87dd0 | ||
|
|
71aad7fc55 | ||
|
|
bd287a324a | ||
|
|
c671bf0bfe | ||
|
|
c9cd45e712 | ||
|
|
e887fcd435 | ||
|
|
98a9be3615 | ||
|
|
9c3b7c3735 | ||
|
|
5748b3409b | ||
|
|
2ef5f4af8f | ||
|
|
f164d8b76e | ||
|
|
3c022ad2f9 | ||
|
|
9275abcdae | ||
|
|
ba791e856a | ||
|
|
161311bbd0 | ||
|
|
08ae8b7882 | ||
|
|
7f37c01ef4 | ||
|
|
d3c9cbb457 | ||
|
|
78051cc24f | ||
|
|
e2513fdf00 | ||
|
|
bdc539046c | ||
|
|
78887f6e7d | ||
|
|
9beccd31c3 | ||
|
|
b90b56a4ae | ||
|
|
577673fec4 | ||
|
|
d89c48d017 | ||
|
|
4e4a8c886b | ||
|
|
b9b6159e0c | ||
|
|
3dd8025803 | ||
|
|
270805d8c4 | ||
|
|
1a49d39cef | ||
|
|
c14e31cc43 | ||
|
|
d7a0acf0a9 | ||
|
|
81dc673ac2 | ||
|
|
3bfb233614 | ||
|
|
34cf3d79cd | ||
|
|
abca5c2d75 | ||
|
|
748c2060a4 | ||
|
|
7cbae3d221 | ||
|
|
a2486c93c5 | ||
|
|
e61f230978 | ||
|
|
0992f3b863 | ||
|
|
345ef7c56c | ||
|
|
899ab7095b | ||
|
|
786fc97ea0 | ||
|
|
4949aef927 | ||
|
|
a85cb5a928 | ||
|
|
7514ceb04f | ||
|
|
c8cbc90d87 | ||
|
|
8342efd8df | ||
|
|
a5ff81e98d | ||
|
|
c5de4d2b04 | ||
|
|
4e8d165892 | ||
|
|
645b51330a | ||
|
|
5dd1d2b296 | ||
|
|
d81a59b9cb | ||
|
|
8f5d690d75 | ||
|
|
35ce6be8f9 | ||
|
|
4e3a3ac899 | ||
|
|
85e4cbdc67 | ||
|
|
8446965d88 | ||
|
|
dd69b30b84 | ||
|
|
b55b476728 | ||
|
|
d3d6ae0b33 | ||
|
|
afe6aa9592 | ||
|
|
070ae74efa | ||
|
|
b8ae0c625d | ||
|
|
d43efc52be | ||
|
|
7a6f51ce09 | ||
|
|
fd2be150dc | ||
|
|
2656cecd77 | ||
|
|
bbaad073dd | ||
|
|
1e6a0b74c9 | ||
|
|
f080ebe36e | ||
|
|
de8b59ec16 | ||
|
|
5fa85c5979 | ||
|
|
4766ac875a | ||
|
|
01c885589d | ||
|
|
b38fb9e12a | ||
|
|
c7c1b50a3e | ||
|
|
fa17d77fda | ||
|
|
76e9d0347d | ||
|
|
448aa80fa4 | ||
|
|
656f144267 | ||
|
|
ec54ddd0f8 | ||
|
|
bd2648a8d3 | ||
|
|
89675c5f16 | ||
|
|
422a1eacc3 | ||
|
|
de068d173c | ||
|
|
1996411136 | ||
|
|
af440f540d | ||
|
|
2ef74d4799 | ||
|
|
cba3fdebb0 | ||
|
|
ee64a7b14f | ||
|
|
d085512998 | ||
|
|
ca65328b32 | ||
|
|
663188920e | ||
|
|
18efc41669 | ||
|
|
c2f48df98f | ||
|
|
b03fe601d9 | ||
|
|
cfa5d6dabf | ||
|
|
e1ff50a9af | ||
|
|
c9ee688537 | ||
|
|
2c0338329b | ||
|
|
ad01b9bb5b | ||
|
|
2fb6061424 | ||
|
|
0c121a6cdf | ||
|
|
dd373eccf5 | ||
|
|
8f73cc892e | ||
|
|
9f64d829a4 | ||
|
|
69552b74d9 | ||
|
|
b2cea40ddb | ||
|
|
9212e17440 | ||
|
|
74ca803c84 | ||
|
|
41bc61b363 | ||
|
|
8d841863cb | ||
|
|
d7dcc7bfbe | ||
|
|
bc47b506c2 | ||
|
|
883f9d26c2 |
No files matched your search
@@ -4,3 +4,4 @@
|
|||||||
.opencode
|
.opencode
|
||||||
|
|
||||||
result
|
result
|
||||||
|
/nvim.log
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Working in this repository
|
||||||
|
|
||||||
|
Read [the design](docs/design.md) and [current work](docs/current-work.md) for
|
||||||
|
repository decisions and active work. [README.md](README.md) maps configuration owners
|
||||||
|
and maintenance entry points. Global instructions remain applicable.
|
||||||
|
|
||||||
|
- Preserve unrelated changes. Coherent local commits are allowed for agreed batches.
|
||||||
|
Do not push or make other remote changes without explicit approval. System
|
||||||
|
activation remains user-owned.
|
||||||
|
- Do not read secret values for discovery, source the workbench credential-loading
|
||||||
|
environment, or access live company systems with write authority. Test writes need
|
||||||
|
task-specific authority. Moving credentials or synchronizing company content
|
||||||
|
requires agreement; ordinary repository work does not authorize either.
|
||||||
|
- Do not write or run tests or perform extensive validation unless explicitly
|
||||||
|
requested. For routine changes, use a focused review or small manual check.
|
||||||
|
- Run `just validate` only when explicitly requested. Read its short result first;
|
||||||
|
inspect saved logs only for failures or a concrete unresolved concern.
|
||||||
|
- Replace superseded state in `docs/current-work.md`; keep durable rationale in
|
||||||
|
`docs/design.md` and operational detail beside its owner. Use Git for chronology.
|
||||||
|
Remove temporary state when it no longer serves a purpose.
|
||||||
@@ -1,24 +1,97 @@
|
|||||||
# NixOS configuration
|
# NixOS configuration
|
||||||
|
|
||||||
Host identity and topology live in `modules/hosts/<host>/default.nix`; shared account,
|
One shared daily environment on Polaris and Zenith, with shared services on Orion.
|
||||||
service, external-device, and server-topology facts live in `modules/facts.nix`.
|
NixOS and Home Manager are composed through flake-parts and import-tree.
|
||||||
|
|
||||||
- Add a package used by both workstations to the relevant Home Manager capability,
|
## Start here
|
||||||
or to `workstation-apps` in `modules/capabilities/desktop-defaults.nix` when it needs
|
|
||||||
no configuration.
|
|
||||||
- Add a host-specific capability to that host's import list in
|
|
||||||
`modules/hosts/<host>/default.nix`.
|
|
||||||
- Change the selected terminal or editor by replacing the corresponding
|
|
||||||
`primary-terminal-*` import in `modules/profiles/workstation-base.nix` or
|
|
||||||
`primary-editor-*` import in `modules/profiles/host-base.nix`.
|
|
||||||
- Keep native theme settings with the application. Explicit Kanagawa color mappings
|
|
||||||
may import `modules/capabilities/_kanagawa.nix`.
|
|
||||||
|
|
||||||
Common commands:
|
- [Design](docs/design.md): vocabulary, ownership and coding conventions.
|
||||||
|
- [Agent instructions](AGENTS.md): essential collaboration boundaries.
|
||||||
|
- [Current work](docs/current-work.md): implementation state, pending decisions and
|
||||||
|
remaining acceptance checks.
|
||||||
|
|
||||||
|
## Configuration map
|
||||||
|
|
||||||
|
| Owner | Location | Responsibility |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Hosts | `modules/hosts/<host>/` | Identity, hardware, machine-specific selection |
|
||||||
|
| Profiles | `modules/profiles/` | Combinations of capabilities |
|
||||||
|
| Capabilities | `modules/capabilities/` | Behavior and NixOS/Home Manager integration |
|
||||||
|
| Facts | `modules/facts.nix` | Shared identity/topology; host facts live with hosts |
|
||||||
|
| Composition | `flake.nix`, `modules/flake-parts.nix` | Inputs, discovery, host construction |
|
||||||
|
| Secrets | `modules/secrets/` | SOPS declarations and encrypted documents |
|
||||||
|
|
||||||
|
Discovery makes named modules available; imports select them. Flat owners use
|
||||||
|
`name.nix` or `name/default.nix`. Private Nix helpers live under `_support/`, excluded
|
||||||
|
from discovery. Scripts, assets and operational notes stay beside their owner.
|
||||||
|
Select shared behavior through profiles; keep machine differences in hosts.
|
||||||
|
|
||||||
|
The primary editor is selected in `host-base.nix`, the terminal in
|
||||||
|
`workstation-base.nix`. `capabilities/theme/default.nix` selects Kanagawa;
|
||||||
|
application appearance lives in `theme/_support/`, interaction with capabilities.
|
||||||
|
|
||||||
|
Both workstations import the provisional `work` profile. See
|
||||||
|
[workbench setup](modules/capabilities/workbench/README.md) for native devenv,
|
||||||
|
company credentials and always-available read-only mounts. Only company-root and
|
||||||
|
workbench environments are migrated; product/personal projects remain user-owned.
|
||||||
|
|
||||||
|
Orion's wardrobe service has its own
|
||||||
|
[setup and operations guide](modules/capabilities/wardrowbe/README.md).
|
||||||
|
Provision its SOPS secrets and two DNS records before building or activating Orion.
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
```console
|
```console
|
||||||
nix fmt
|
just validate # format, evaluate, build all hosts and workbench tools
|
||||||
nix flake check --no-build
|
just validate polaris # same workflow, build only the named host(s)
|
||||||
nh os switch
|
nix flake update # update inputs; selective input updates are also supported
|
||||||
sops edit modules/secrets/secrets.yaml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Validation copies current tracked and nonignored new files into a temporary snapshot,
|
||||||
|
respecting deletions and leaving the Git index untouched. It formats included Nix
|
||||||
|
files in the working tree, refreshes the snapshot, prepares devenv's upstream
|
||||||
|
evaluation dependencies, evaluates configurations and builds. Avoid concurrent edits:
|
||||||
|
results describe that snapshot. Ordinary Git-backed flake commands outside the
|
||||||
|
wrapper still require new source files to be added to Git.
|
||||||
|
|
||||||
|
Read the short result first. Full logs live in the printed `/tmp/nixos-validate-*`
|
||||||
|
directory. Failure stops the command; snapshots are removed on exit. It neither
|
||||||
|
updates inputs nor creates result links or activates systems. The implementation is
|
||||||
|
`scripts/validate.py`; `just` and Python are installed on both workstations.
|
||||||
|
|
||||||
|
Run targeted host builds or full validation only when explicitly requested. Routine
|
||||||
|
changes need a focused review or small manual check. Build, activation and
|
||||||
|
interactive behavior are separate evidence.
|
||||||
|
|
||||||
|
User-owned activation:
|
||||||
|
|
||||||
|
```console
|
||||||
|
nh os switch
|
||||||
|
nixos-rebuild switch --target-host orion --sudo --flake ~/.config/nixos
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit system ciphertext with `sops edit modules/secrets/secrets.yaml`; company
|
||||||
|
provisioning is documented with workbench. Never put plaintext secrets in Nix.
|
||||||
|
|
||||||
|
## First-use acceptance
|
||||||
|
|
||||||
|
- **Notes:** Super+N and Walker's Notes open the same picker. Enter opens a note;
|
||||||
|
Ctrl+E creates one using the search text. Daily-note commands remain available.
|
||||||
|
- **Orientation:** Super+Shift+N names the focused workspace; submit an empty name
|
||||||
|
to clear it. Named empty workspaces persist until cleared. Noctalia shows labels;
|
||||||
|
terminal titles include the working directory and command.
|
||||||
|
- **Icons/vault:** check Oil from Walker and Nautilus device/symbolic icons. Check
|
||||||
|
Walker's `?` vault provider; upstream Elephant may query `rbw` during startup.
|
||||||
|
- **Calendar:** in Noctalia Settings → Services → Calendar, set the Radicale
|
||||||
|
password through the existing Personal account (GNOME Keyring). Add an ICS account
|
||||||
|
named Work using Outlook's published URL. Keep this URL in writable Noctalia
|
||||||
|
settings, outside this repository. Check both agendas; edit work events in Outlook.
|
||||||
|
- **Tailnet:** run `sudo tailscale up` on each machine and enroll the phone with
|
||||||
|
the Tailscale app. Keep Tailscale SSH disabled; use ordinary SSH keys. With
|
||||||
|
MagicDNS enabled, verify `ssh kiri@orion.<tailnet-name>.ts.net` from a workstation.
|
||||||
|
Existing `ssh orion` still uses the public address until the private route is
|
||||||
|
accepted. Close public Orion SSH only in the subsequent verified transition.
|
||||||
|
|
||||||
|
No systems have been activated as part of this implementation. Old SharePoint
|
||||||
|
mirror content, experimental AI data and unrelated workspace changes are preserved.
|
||||||
|
Historical material under `docs/.archive/` is evidence, not current authority.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# Yookr credential consolidation
|
||||||
|
|
||||||
|
Archived: historical credential inventory and migration evidence.
|
||||||
|
|
||||||
|
This inventory contains names and provenance only. It intentionally contains no
|
||||||
|
credential values or value hashes.
|
||||||
|
|
||||||
|
## Authority
|
||||||
|
|
||||||
|
Use one SOPS-encrypted `~/.config/yookr/credentials.yaml`, backed by the existing
|
||||||
|
mode-0600 user age identity. SOPS 3.13.3 and the identity are already present,
|
||||||
|
and an exact encrypt/decrypt comparison passed on 2026-09-06. The user approved
|
||||||
|
this authority and an active-credentials-first migration. The directory is mode
|
||||||
|
0700 and the encrypted file is mode 0600; it remains outside Git and the Nix
|
||||||
|
store.
|
||||||
|
|
||||||
|
Keep the document hierarchical so operators can inspect and edit everything in
|
||||||
|
one place while callers extract only their own scalar values or subtree. Do not
|
||||||
|
load the complete document into every repository environment. Public endpoints,
|
||||||
|
queue names, bucket names, and other non-secret configuration remain with their
|
||||||
|
normal owners.
|
||||||
|
|
||||||
|
Top-level groups:
|
||||||
|
|
||||||
|
- `company_login`
|
||||||
|
- `databases`
|
||||||
|
- `leafcloud`
|
||||||
|
- `rabbitmq`
|
||||||
|
- `applications`
|
||||||
|
- `archive`
|
||||||
|
|
||||||
|
Active credentials and ten archived credential sources have been migrated.
|
||||||
|
Archive membership does not imply inactivity, so their source namespaces are
|
||||||
|
preserved. Delete redundant plaintext only with explicit approval after each
|
||||||
|
original consumer is verified.
|
||||||
|
|
||||||
|
## Active sources
|
||||||
|
|
||||||
|
- `~/dev/work/yookr-dashboard/backend/.env`: MySQL, MongoDB, Leafcloud S3, and
|
||||||
|
logger credentials.
|
||||||
|
- `~/dev/work/yookr-dashboard/front-end/.env`: `VITE_BASE_TOKEN` is referenced by
|
||||||
|
frontend source and is therefore client-visible, not a protectable secret.
|
||||||
|
- `~/dev/work/yookr-data-science/.env`: WeatherAPI key.
|
||||||
|
- `~/rabbitmq-creds-test`: test RabbitMQ username and password.
|
||||||
|
- `~/irristaud-openstack-creds`: Irristaud project EC2 credentials.
|
||||||
|
- `~/db-creds`: MySQL test/live and MongoDB test/live material.
|
||||||
|
- `~/dev/work/yookr-api/http-client.private.env.json`: company email and
|
||||||
|
password used for API logins.
|
||||||
|
- `~/.local/share/db_ui/connections.json`: existing `traceability` connection.
|
||||||
|
|
||||||
|
All credential-bearing active files above are mode 0600 after the audit.
|
||||||
|
They remain in place pending consumer-by-consumer removal approval. The dashboard
|
||||||
|
backend's MySQL and MongoDB credentials do not match either named test/live pair,
|
||||||
|
so they are preserved separately instead of being assigned an environment by
|
||||||
|
assumption.
|
||||||
|
|
||||||
|
## Archived credential sources
|
||||||
|
|
||||||
|
- Service login: `inteerlijn`, `water-need`, `temperature-relay-dendron`, and
|
||||||
|
`prediction_mildew_rust`.
|
||||||
|
- Rules service: RabbitMQ and database credentials in `rules-service-hex`.
|
||||||
|
- Legacy management: database, logger, and local-login credentials.
|
||||||
|
- Legacy data science and soil-moisture tools: company login credentials.
|
||||||
|
- `salemans`: CMI credentials.
|
||||||
|
|
||||||
|
These files are now mode 0600 and their selected credentials are preserved under
|
||||||
|
`archive`. The user is still cleaning the filesystem, so stale status must be
|
||||||
|
decided per source; stale credentials should be revoked instead of retained
|
||||||
|
indefinitely.
|
||||||
|
|
||||||
|
## Verified consumers
|
||||||
|
|
||||||
|
- `yookr-workbench` decrypts once in memory and exports only its API login, four
|
||||||
|
database connections, Irristaud S3 credential, and test RabbitMQ credential.
|
||||||
|
- Central test API login and the read-only RabbitMQ overview passed.
|
||||||
|
- MariaDB test/live example queries and MongoDB test/live example scripts passed.
|
||||||
|
Both MariaDB sessions negotiated TLS 1.3. The routers expose auto-generated
|
||||||
|
certificates from unavailable private CAs without valid host identities, so
|
||||||
|
only these two URLs disable certificate verification. The MongoDB endpoints
|
||||||
|
publish SRV rather than direct address records and therefore use `mongodb+srv`
|
||||||
|
URLs.
|
||||||
|
- The ephemeral `irristaud:` rclone remote found the sole `irristaud` container
|
||||||
|
and listed its six root entries. No object content was downloaded and no
|
||||||
|
rclone configuration was persisted.
|
||||||
|
|
||||||
|
Dadbod's four display names are active, both MongoDB connections work, both
|
||||||
|
MariaDB examples produce results through Dadbod UI, and the workbench environment
|
||||||
|
supplies successful Kulala response chaining.
|
||||||
|
|
||||||
|
## Excluded from migration
|
||||||
|
|
||||||
|
- Tracked `.env.example` files and public URL-only frontend environments.
|
||||||
|
- Tracked Apache Superset/Hive development defaults.
|
||||||
|
- Empty placeholders, queue names, ports, bucket names, and service URLs.
|
||||||
|
- NixOS SOPS secrets and personal Vaultwarden entries, which already have their
|
||||||
|
own authorities.
|
||||||
|
|
||||||
|
## Observed services
|
||||||
|
|
||||||
|
The scan identified Yookr measurement, authentication, management, aggregation,
|
||||||
|
rules, prediction, dashboard, and data-science workflows plus MySQL, MongoDB,
|
||||||
|
RabbitMQ, Leafcloud S3, logger, SMTP, Vonage, TTN, WeatherAPI, CMI, Superset, and
|
||||||
|
Hive integrations. Presence in an archive does not establish that a service or
|
||||||
|
credential is still active.
|
||||||
|
|
||||||
|
## Immediate findings
|
||||||
|
|
||||||
|
- The dashboard S3 credential and the Irristaud project credential are distinct;
|
||||||
|
the latter is now centralized and its read-only pilot passed.
|
||||||
|
- The generated Yookr OpenAPI document remains malformed, but direct requests use
|
||||||
|
the reviewed local contract and work independently.
|
||||||
|
- Management accepts an account-issued JWT only as the raw `Authorization` value,
|
||||||
|
contrary to its current OpenAPI Bearer declaration.
|
||||||
|
- The Herdr issue 197 pilot produced local-only frontend and backend planning
|
||||||
|
commits without touching company GitHub. Both identify the separate account
|
||||||
|
service, not `yookr-dashboard`, as the implementation owner.
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,190 @@
|
|||||||
|
# Workflow system execution
|
||||||
|
|
||||||
|
Archived: historical implementation evidence, not the current iteration plan.
|
||||||
|
|
||||||
|
Source: [`../workflow-discovery.md`](../workflow-discovery.md)
|
||||||
|
|
||||||
|
Phase one is complete; explicitly deferred pilots remain. This file is the
|
||||||
|
living handoff for operational constraints and future work; implementation
|
||||||
|
history is available in Git.
|
||||||
|
|
||||||
|
## Activation boundary
|
||||||
|
|
||||||
|
Only the user may switch or activate configurations. Agents must not run
|
||||||
|
`nixos-rebuild switch`, Home Manager activation, or an equivalent command that
|
||||||
|
changes the active generation. Evaluation and builds are allowed; after a
|
||||||
|
successful build, provide the exact activation command and wait for the user.
|
||||||
|
|
||||||
|
This boundary includes changes described as user-level, harmless, temporary, or
|
||||||
|
necessary for live verification. A direct Home Manager activation previously
|
||||||
|
restarted Kitty and closed the user's terminal session.
|
||||||
|
|
||||||
|
## Execution discipline
|
||||||
|
|
||||||
|
- Advance all safe independent work before asking the user to act.
|
||||||
|
- Batch declarative changes behind user-controlled activation.
|
||||||
|
- Consolidate empirical, credential, hardware, and approval gates only after no
|
||||||
|
autonomous work remains.
|
||||||
|
- Keep repositories and worktrees outside `~/sync`; synchronize authored
|
||||||
|
artifacts instead.
|
||||||
|
- Keep GitHub and other repository remotes strictly read-only.
|
||||||
|
- Treat Yookr production source as read-only. Dev-tooling changes are allowed;
|
||||||
|
`yookr-workbench` is the only repository whose own source may be changed for
|
||||||
|
these pilots.
|
||||||
|
|
||||||
|
## Current checkpoint
|
||||||
|
|
||||||
|
- State: phase one complete and active; explicitly deferred pilots remain.
|
||||||
|
- Branch: `chore/workflow-system`.
|
||||||
|
- Validation: `nix flake check --no-build --quiet`, all three no-link system
|
||||||
|
builds, generated Zenith and Polaris Niri validation, and all six companion
|
||||||
|
flake evaluations passed on 2026-09-06. The updated data-science shell exposes
|
||||||
|
Python 3.14.7 and its full project check passes. The expected dirty-tree and
|
||||||
|
unknown `modules` warnings were emitted.
|
||||||
|
- Live state: Walker project and terminal actions, daily notes, stable web apps,
|
||||||
|
Evolution D-Bus activation, the Zenith VPN, invocation-scoped GitHub and
|
||||||
|
Context7 credentials, Pinta, and Orion transcription passed their completed
|
||||||
|
pilots. The corrected Actual, WhatsApp, and Outlook launchers all started
|
||||||
|
successfully. The labeled RabbitMQ test/live bookmarks are present, and the
|
||||||
|
invocation-scoped test overview and queue listing passed. Test API login,
|
||||||
|
Yookr administrator read, rules read, aggregation login, and management health
|
||||||
|
passed. Account login plus a raw-token management user read also passed.
|
||||||
|
The approved user-key SOPS authority now contains active credentials and ten
|
||||||
|
provenance-preserving archive namespaces. Its workbench exports passed a test
|
||||||
|
API login, RabbitMQ overview, MySQL test/live `SELECT 1`, MongoDB test/live
|
||||||
|
`ping`, and read-only Irristaud object-storage listing. Dadbod exposes all four
|
||||||
|
names and the MongoDB connections work. Both MariaDB connections now use the
|
||||||
|
native client with narrowly scoped certificate-verification exceptions; their
|
||||||
|
exact example queries passed over TLS 1.3 in both environments. Direct test
|
||||||
|
login and authenticated reads still return HTTP 200 for Yookr, rules, and
|
||||||
|
management. Kulala login and response chaining are confirmed in Neovim, and
|
||||||
|
both MariaDB examples produced results through Dadbod UI.
|
||||||
|
- Herdr ran the issue 197 planning pilot in two predictable, non-overlapping
|
||||||
|
dashboard worktrees. Codex produced frontend commit `20f9d0f`; Claude produced
|
||||||
|
backend commit `bd965ea`. Short, long, done, fallback-agent, and genuinely
|
||||||
|
approval-blocked states behaved correctly, and dirty worktree removal was
|
||||||
|
refused. Nothing reached company GitHub. Both plans found that password reset
|
||||||
|
belongs to the separate account service rather than `yookr-dashboard`.
|
||||||
|
Documented named-session detach/reattach persistence is accepted without a
|
||||||
|
redundant live trial; notifications are intentionally disabled after their
|
||||||
|
live pilot.
|
||||||
|
|
||||||
|
## Phase-one closure
|
||||||
|
|
||||||
|
- The cross-system investigation and Marimo report pilots move into phase two.
|
||||||
|
- Bruno and DBeaver remain available; retiring old applications is not part of
|
||||||
|
phase one.
|
||||||
|
- Issue 197 planning is complete. Before implementation, route it to the actual
|
||||||
|
account repositories; leave the two local dashboard planning branches
|
||||||
|
available as evidence.
|
||||||
|
|
||||||
|
Herdr's bounded pilot is complete. If later use exposes a problem, fall back
|
||||||
|
independently to native agent sessions or native `git worktree add/list/remove`;
|
||||||
|
do not add a wrapper or Worktrunk without a demonstrated gap.
|
||||||
|
|
||||||
|
## Deferred pilots
|
||||||
|
|
||||||
|
Revisit these alongside related phase-two ideas rather than as isolated cleanup.
|
||||||
|
|
||||||
|
- During a docked Zenith session, capture the external display's stable identity
|
||||||
|
and test the two-output `Super+A` exchange. The current recorded session exposed
|
||||||
|
only `eDP-1`.
|
||||||
|
- Resume Evolution only when the user chooses to configure the personal,
|
||||||
|
old-personal, university, work, and Radicale accounts. Keep Thunderbird and
|
||||||
|
Outlook until the daily-use gate passes.
|
||||||
|
- On Android, complete the DAVx5/Radicale and Tasks.org pilots, including default
|
||||||
|
accounts, recurrence, reminders, and Microsoft To Do within documented limits.
|
||||||
|
- Do not replicate the workstation workflow on Polaris until the user resumes
|
||||||
|
the procedure below.
|
||||||
|
|
||||||
|
The Niri pilot is two-output-only. It passes when the active workspaces exchange
|
||||||
|
physical outputs, both arrivals are active, original names are preserved, and
|
||||||
|
focus returns to the original physical output. Any temporary names must be
|
||||||
|
collision-free and restored on success or failure; missing output or workspace
|
||||||
|
state must leave the session unchanged. Keep the helper only after repeated
|
||||||
|
reliable swaps with an acceptable visible transition.
|
||||||
|
|
||||||
|
## Deferred Polaris replication
|
||||||
|
|
||||||
|
Do not access or test Polaris unless the user resumes this deferred replication.
|
||||||
|
Zenith's result remains representative meanwhile.
|
||||||
|
|
||||||
|
1. Activate this NixOS configuration so `networkmanager-openvpn` is installed.
|
||||||
|
2. Restrict `~/sync/yookr-vpn.ovpn` to mode 0600.
|
||||||
|
3. Import it with `nmcli connection import type openvpn file ~/sync/yookr-vpn.ovpn`.
|
||||||
|
4. Rename the connection to `Yookr VPN`, disable autoconnect, and add
|
||||||
|
`data-ciphers=AES-256-CBC`.
|
||||||
|
5. Connect with `nmcli connection up "Yookr VPN"`.
|
||||||
|
6. Create mode-0600 `~/.config/yookr/github-read-only.token` without a trailing
|
||||||
|
newline, then verify a new shell can run
|
||||||
|
`gh project list --owner yookrdev`.
|
||||||
|
7. Create mode-0600 `~/.config/context7/api-key` without a trailing newline,
|
||||||
|
restart Codex after activation, and run one Context7 lookup.
|
||||||
|
|
||||||
|
Replication passes only when the VPN has the expected route and DNS behavior,
|
||||||
|
reaches a test service, reconnects after disconnect and suspend, and survives a
|
||||||
|
reboot without autoconnecting. The two credential files must remain mode 0600;
|
||||||
|
the fresh-shell GitHub read and restarted-Codex Context7 lookup must both work.
|
||||||
|
|
||||||
|
## Durable implementation decisions
|
||||||
|
|
||||||
|
- Active work repositories live under `~/dev/work`, archives under `.archive`,
|
||||||
|
and worktrees under `.worktrees`. Repository trees never enter Syncthing.
|
||||||
|
- Participating repositories expose non-mutating direnv shells, minimal flake
|
||||||
|
checks, native commands through `just`, concise `AGENTS.md`, and machine-local
|
||||||
|
credential inputs.
|
||||||
|
- `yookr-data-science/conf/local/credentials.yml` is an approved narrow
|
||||||
|
application-owned exception to the external credential-file default: Kedro's
|
||||||
|
named credential dataset resolves it from the native ignored `conf/local`
|
||||||
|
hierarchy. Keep it untracked and mode 0600.
|
||||||
|
- `yookr-data-science` follows ordinary `nixos-unstable`, like the other
|
||||||
|
companion repositories; it needs no `devenv-nixpkgs` compatibility input.
|
||||||
|
- `yookr-workbench` starts with cross-system work only; isolation and additional
|
||||||
|
tools wait for observed needs. Its one API environment catalogue covers the
|
||||||
|
Yookr, rules, aggregation, and management APIs; Kulala owns requests and
|
||||||
|
Dadbod receives the four named database connections from the workbench's
|
||||||
|
SOPS-derived environment. The two MariaDB routers expose auto-generated
|
||||||
|
private-CA certificates without a valid host identity, so only those URLs
|
||||||
|
disable certificate verification while retaining TLS; restore verification
|
||||||
|
when infrastructure provides usable CA and host certificates.
|
||||||
|
- The reviewed `yookr-api/openapi.yaml` remains the usable local contract. The
|
||||||
|
deployed generated document's dangling `ReadParameters` reference is accepted
|
||||||
|
for now and does not block direct API requests.
|
||||||
|
- Walker's native Elephant menu scans repository roots and calls one Nix-owned
|
||||||
|
`open-project` command. Rare menu changes take effect at login.
|
||||||
|
- Home Manager declares the three stable Vivaldi launcher paths with forced
|
||||||
|
ownership. Vivaldi's manual `Create shortcuts` action can replace one until a
|
||||||
|
changed Home Manager generation reclaims it; repeating a no-op activation need
|
||||||
|
not relink it. This accepted edge case does not justify filtering, immutable
|
||||||
|
files, or a watcher because that action is not part of normal use.
|
||||||
|
- Kitty and Neovim are the sole declared terminal and editor. Retired duplicate
|
||||||
|
applications and global language runtimes stay absent unless a concrete owner
|
||||||
|
appears.
|
||||||
|
- The fine-grained GitHub token and Context7 key stay in mode-0600 machine-local
|
||||||
|
files and are exposed only to their callers. Secrets do not enter Nix or broad
|
||||||
|
process environments.
|
||||||
|
- The credential audit and approved central SOPS authority are in
|
||||||
|
[`credential-consolidation.md`](credential-consolidation.md). The authority is
|
||||||
|
active, while original plaintext sources remain pending consumer-specific
|
||||||
|
verification and explicit removal approval.
|
||||||
|
- `sync/notes` is the single notebook owner. Daily notes, meetings, and raw
|
||||||
|
transcripts have separate paths, and agents do not create issues or ingest
|
||||||
|
transcripts automatically.
|
||||||
|
- Orion is the sole transcription owner. It waits for file settlement, validates
|
||||||
|
media, archives originals, and publishes an unlabeled transcript when
|
||||||
|
diarization fails.
|
||||||
|
- Evolution remains a deferred presentation-layer pilot. Microsoft 365 and
|
||||||
|
Radicale remain the data authorities; Thunderbird and Outlook remain available.
|
||||||
|
- GitHub and every other repository remote stay read-only. Local files, commits,
|
||||||
|
fetches, clones, and worktrees remain available.
|
||||||
|
- The provisional team chain is Project Overview project → work package → user
|
||||||
|
requirement → Software Development Backlog ticket → repository issue → one
|
||||||
|
pull request. Treat it as discovery context, not automation policy, until the
|
||||||
|
team settles the workflow; company GitHub remains read-only throughout.
|
||||||
|
- Yookr production source stays read-only during workflow pilots. Changes to
|
||||||
|
development tooling are allowed, and `yookr-workbench` remains the explicit
|
||||||
|
source-code exception.
|
||||||
|
|
||||||
|
## Next step
|
||||||
|
|
||||||
|
Begin phase-two discovery from the user's new workflow ideas.
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# SharePoint reference mirror
|
||||||
|
|
||||||
|
Archived: guidance for the existing implementation, not a new design decision.
|
||||||
|
|
||||||
|
Superseded: this describes the earlier single-source, environment-file-gated
|
||||||
|
implementation. The current [working state](../constellation-work.md) records two
|
||||||
|
fixed library mirrors gated only by the presence of `rclone.conf`. Do not use the
|
||||||
|
setup procedure below as current operational guidance.
|
||||||
|
|
||||||
|
The Zenith user timer mirrors one selected company SharePoint document library
|
||||||
|
into `~/.local/share/yookr-reference/sharepoint` hourly. SharePoint is
|
||||||
|
authoritative: remote updates overwrite local files and remote deletions remove
|
||||||
|
local files. Never author files there; edit them in SharePoint's web UI. The job
|
||||||
|
stops before more than 100 local deletions and reports the failure in the user
|
||||||
|
journal. A failed run may leave earlier updates in place and is not evidence of a
|
||||||
|
complete current snapshot; rclone does not delete destination files when the run
|
||||||
|
has errors.
|
||||||
|
|
||||||
|
The timer remains inert until both `~/.config/rclone/rclone.conf` and
|
||||||
|
`~/.config/rclone/sharepoint-reference.env` exist and the latter names a source.
|
||||||
|
Before adding the SharePoint remote or source file, the user must explicitly
|
||||||
|
start the interactive company sign-in and consent step. Use only delegated,
|
||||||
|
cloud-read-only scopes:
|
||||||
|
`Files.Read Files.Read.All Sites.Read.All offline_access`. Do not accept rclone's
|
||||||
|
broader default scopes or create an application registration. Tenant policy may
|
||||||
|
require administrator consent or an approved single-tenant application; involve
|
||||||
|
the administrator only if the consent response requires it.
|
||||||
|
|
||||||
|
After approval, run `rclone config`, choose Microsoft OneDrive, set the advanced
|
||||||
|
`access_scopes` value above, and select one company SharePoint document library
|
||||||
|
(not a personal account). Before enabling the job, inspect and accept its exact
|
||||||
|
scope, footprint, and first change set:
|
||||||
|
|
||||||
|
```console
|
||||||
|
rclone config redacted company-sharepoint
|
||||||
|
rclone lsd company-sharepoint:
|
||||||
|
rclone size company-sharepoint:
|
||||||
|
rclone sync company-sharepoint: ~/.local/share/yookr-reference/sharepoint --dry-run --max-delete 100
|
||||||
|
```
|
||||||
|
|
||||||
|
The remote-scoped redacted output confirms the configured requested scopes, not
|
||||||
|
what the tenant actually granted. Confirm the consent response also contains only
|
||||||
|
the requested read scopes; stop if it denies them or offers broader authority.
|
||||||
|
|
||||||
|
Only after that preview is accepted, create the gate with mode `0600`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
RCLONE_SHAREPOINT_SOURCE=company-sharepoint:
|
||||||
|
```
|
||||||
|
|
||||||
|
Save it as `~/.config/rclone/sharepoint-reference.env`. Home Manager activation
|
||||||
|
then enables the hourly timer. This is the first bounded library, not a permanent
|
||||||
|
allowlist or a claim that all accessible SharePoint content is mirrored. Add
|
||||||
|
another library only after its own size/preview check, using another native unit
|
||||||
|
and dedicated destination rather than widening this source silently. Refresh and
|
||||||
|
inspect the current unit with:
|
||||||
|
|
||||||
|
```console
|
||||||
|
systemctl --user start sharepoint-reference-sync.service
|
||||||
|
systemctl --user status sharepoint-reference-sync.service
|
||||||
|
journalctl --user -u sharepoint-reference-sync.service
|
||||||
|
```
|
||||||
|
|
||||||
|
For an accepted deletion set larger than 100 files, rerun the dry-run and then a
|
||||||
|
manual `rclone sync` without `--max-delete`; the scheduled guard stays in place.
|
||||||
|
The mutable OAuth token remains in rclone's config outside the Nix store. The
|
||||||
|
local directory is user-writable and its read-only consumer policy is not an OS
|
||||||
|
permission or credential-isolation boundary. New mirror files are private to the
|
||||||
|
user by default (`UMask=0077`), but same-user processes can still read and change
|
||||||
|
them.
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# Workflow system blueprint
|
||||||
|
|
||||||
|
Status: phase one implemented and active; explicitly deferred pilots remain.
|
||||||
|
The operational handoff lives in
|
||||||
|
[`plans/workflow-system.md`](plans/workflow-system.md).
|
||||||
|
|
||||||
|
This document is the durable design for the workstation and surrounding Yookr
|
||||||
|
workflow. Implementation history belongs in Git.
|
||||||
|
|
||||||
|
## Governing intent
|
||||||
|
|
||||||
|
Build a quiet, keyboard-first control plane over development, investigation,
|
||||||
|
operations, agents, and personal knowledge. Keep the system small and give each
|
||||||
|
intention one canonical owner.
|
||||||
|
|
||||||
|
- Standardize vocabulary and entry points without forcing unlike workloads onto
|
||||||
|
one implementation.
|
||||||
|
- Put frequent actions on direct keys and less frequent actions on their owning
|
||||||
|
discovery surface.
|
||||||
|
- Keep stable packages, services, integration, and durable preferences in Nix.
|
||||||
|
Keep credentials, sessions, databases, caches, and other mutable state with
|
||||||
|
their owning application.
|
||||||
|
- Prefer native file formats and supported application layers. Add wrappers only
|
||||||
|
for demonstrated gaps.
|
||||||
|
- Repositories own reproducible development context, executable project
|
||||||
|
documentation, and code-specific artifacts.
|
||||||
|
- Select `local`, `test`, or `live` per operation; do not depend on an ambient
|
||||||
|
production context.
|
||||||
|
- Make production and destructive actions more deliberate than navigation and
|
||||||
|
read-only inspection.
|
||||||
|
- Promote automation only after the manual workflow repeats and can be verified.
|
||||||
|
- Optimize conceptual complexity and review burden, not package count or line
|
||||||
|
count in isolation.
|
||||||
|
|
||||||
|
The main coordinates are scope (`work`, `own`, `ext`, or `forks`), Yookr domain,
|
||||||
|
environment (`local`, `test`, or `live`), and intention (develop, inspect, query,
|
||||||
|
review, operate, or deploy).
|
||||||
|
|
||||||
|
## Ownership model
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD013 -->
|
||||||
|
|
||||||
|
| Intention | Canonical owner |
|
||||||
|
| --- | --- |
|
||||||
|
| Find applications, projects, or windows | Walker/Elephant and direct Niri bindings |
|
||||||
|
| Search personal passwords | Walker with `rbw`; company Bitwarden stays in its browser extension |
|
||||||
|
| Navigate browser state | Vivaldi Quick Commands |
|
||||||
|
| Arrange windows and displays | Niri dynamic workspaces |
|
||||||
|
| Work in a terminal | Kitty |
|
||||||
|
| Edit related files and views | Neovim |
|
||||||
|
| Enter a repository environment | `direnv` with `nix-direnv` |
|
||||||
|
| Discover project actions | `just --list` |
|
||||||
|
| Run edited first-party code | Native processes, optionally Process Compose |
|
||||||
|
| Run a container-only project stack | Rootless Podman Compose |
|
||||||
|
| Run a persistent containerized user service | Rootless Podman Quadlet |
|
||||||
|
| Run a durable host service | NixOS modules and systemd |
|
||||||
|
| Exercise APIs | Kulala and `xh`, with tracked `.http` files |
|
||||||
|
| Query databases | Dadbod and native driver CLIs |
|
||||||
|
| Investigate across Yookr systems | `yookr-workbench` |
|
||||||
|
| Connect the Yookr VPN | NetworkManager/OpenVPN through Noctalia |
|
||||||
|
| Inspect test Kubernetes | `just k8s test` using Gardenctl and read-only K9s |
|
||||||
|
| Inspect RabbitMQ | Official management UI; `rabbitmqadmin` for exact terminal operations |
|
||||||
|
| Browse S3-compatible storage | Native `rclone` commands |
|
||||||
|
| Capture and find notes | `zk` through Neovim in one synchronized Markdown notebook |
|
||||||
|
| Present work and personal PIM | Evolution pilot beside Thunderbird and Outlook |
|
||||||
|
| Present personal PIM on Android | DAVx5, aCalendar+, Google Contacts, and Tasks.org |
|
||||||
|
| Give agents durable project context | `AGENTS.md` and project commands |
|
||||||
|
| Run top-level agents | Herdr on demand |
|
||||||
|
| Maintain conversation state and subagents | Each native agent, primarily Codex |
|
||||||
|
| Review local changes | LazyGit in the task checkout |
|
||||||
|
| Review pull requests | `gh`, then a review worktree when local checks are useful |
|
||||||
|
|
||||||
|
<!-- markdownlint-enable MD013 -->
|
||||||
|
|
||||||
|
## Repository contract
|
||||||
|
|
||||||
|
Active work repositories live under `~/dev/work`; inactive repositories live
|
||||||
|
under `~/dev/work/.archive`, and worktrees under `~/dev/work/.worktrees`.
|
||||||
|
Repositories and worktrees never live under `~/sync`.
|
||||||
|
|
||||||
|
Participating repositories use the following small contract:
|
||||||
|
|
||||||
|
- A tracked `.envrc` enters the default flake development shell without starting
|
||||||
|
services, fetching data, or mutating the checkout.
|
||||||
|
- `flake.nix` owns reproducible dependencies, checks, packages, and formatting.
|
||||||
|
- `just --list` indexes non-obvious tasks and delegates to native commands.
|
||||||
|
- `AGENTS.md` contains concise project facts, invariants, architecture pointers,
|
||||||
|
and checks without duplicating global guidance.
|
||||||
|
- Service-owned `.http` and schema-owned `.sql` files stay beside their owner;
|
||||||
|
generated views, caches, and scratch output do not become durable sources.
|
||||||
|
- Secrets enter at runtime from an approved source or a user-only file outside
|
||||||
|
checkouts, initially `~/.config/yookr/<repository>.env`.
|
||||||
|
- A new worktree behaves like a fresh clone and requires its own explicit
|
||||||
|
`direnv allow`.
|
||||||
|
- Branch-derived ports, databases, and container names are added only after a
|
||||||
|
real collision.
|
||||||
|
|
||||||
|
Use Process Compose only when multiple foreground processes benefit from one
|
||||||
|
visible lifecycle. Use Podman Compose for wholly containerized project stacks,
|
||||||
|
Quadlet for persistent user containers, and NixOS modules for durable host
|
||||||
|
services. Do not nest lifecycle tools for another status screen.
|
||||||
|
|
||||||
|
`yookr-workbench` owns cross-system API requests, operational queries, the
|
||||||
|
Garden/Kubernetes entry point, and Marimo investigations. It does not collect
|
||||||
|
service OpenAPI specifications, schema migrations, deployable model code, or
|
||||||
|
copied application state.
|
||||||
|
|
||||||
|
## Interaction and knowledge boundaries
|
||||||
|
|
||||||
|
- Walker scans the four repository roots, excludes `.archive` and `.worktrees`,
|
||||||
|
and invokes one Nix-owned `open-project` command. The command opens Kitty at
|
||||||
|
the repository root, starts Neovim, and leaves a shell after Neovim exits.
|
||||||
|
- Vivaldi remains the bookmark and web-app authority. Home Manager declares
|
||||||
|
stable launchers for Actual, WhatsApp, and Outlook without duplicating browser
|
||||||
|
state and reclaims their paths when a changed Home Manager generation is
|
||||||
|
deployed. Manual shortcut regeneration may replace one meanwhile and is an
|
||||||
|
accepted unused edge case. Its
|
||||||
|
`Operations` folder owns visibly labeled test/live RabbitMQ bookmarks; terminal
|
||||||
|
inspection uses the Nix-provided `rabbitmqadmin-ng` on demand.
|
||||||
|
- Niri keeps dynamic general-purpose workspaces and a compact navigation and
|
||||||
|
movement vocabulary. The two-output active-workspace exchange remains a
|
||||||
|
bounded pilot.
|
||||||
|
- Codex is primary; Claude Code and Pi are fallbacks. Herdr owns persistence and
|
||||||
|
attention for top-level sessions, while each agent owns its internal state.
|
||||||
|
- Use one primary writer per checkout. Subagents may perform bounded independent
|
||||||
|
read-heavy work, but overlapping changes are sequenced.
|
||||||
|
- Substantial multi-session work may keep one living
|
||||||
|
`docs/plans/<task-slug>.md`; lasting knowledge moves to normal documentation
|
||||||
|
and transient execution history is removed when finished.
|
||||||
|
- `~/sync/notes` is the Markdown/zk authority. Daily intake uses
|
||||||
|
`daily/YYYY-MM-DD.md`; planned meetings and raw transcripts use `meetings/`
|
||||||
|
and `transcripts/` respectively.
|
||||||
|
- Microsoft 365 remains authoritative for work/university PIM and Radicale for
|
||||||
|
personal CalDAV, VTODO, and CardDAV. Clients present those authorities without
|
||||||
|
bridging or copying them.
|
||||||
|
- Fossify records on Android and Syncthing delivers completed recordings to
|
||||||
|
Orion. Orion owns event-driven CPU transcription, diarization fallback,
|
||||||
|
archival, and Markdown publication.
|
||||||
|
|
||||||
|
## Safety boundaries
|
||||||
|
|
||||||
|
GitHub and all other remotes are read-only. Local commits, clones, fetches, and
|
||||||
|
worktrees are allowed; remote mutation requires a separate change to policy.
|
||||||
|
Yookr production source is also read-only during these pilots. Development
|
||||||
|
tooling may be changed, and `yookr-workbench` is the explicit source-code
|
||||||
|
exception.
|
||||||
|
|
||||||
|
Credentials and `.env` files normally stay outside Git, the Nix store, and broad
|
||||||
|
synchronization. Machine-local Yookr credentials use the approved user-key SOPS
|
||||||
|
authority at `~/.config/yookr/credentials.yaml`; callers expose only the fields
|
||||||
|
they need. Original plaintext sources remain until their consumers are verified
|
||||||
|
and removal is explicitly approved. The accepted `yookr-vpn.ovpn` is a narrow
|
||||||
|
exception for Zenith and Polaris: keep it user-restricted and import it into
|
||||||
|
NetworkManager without printing or committing its contents.
|
||||||
|
|
||||||
|
`yookr-data-science/conf/local/credentials.yml` is a separate application-owned
|
||||||
|
exception: Kedro resolves its named credential dataset through the conventional
|
||||||
|
ignored `conf/local` hierarchy. Keep the file untracked and mode 0600; do not
|
||||||
|
generalize the exception to other repositories.
|
||||||
|
|
||||||
|
Only the user activates a NixOS or Home Manager generation. Agents may evaluate
|
||||||
|
and build configurations, then provide an activation command.
|
||||||
|
|
||||||
|
## Phase-one closure
|
||||||
|
|
||||||
|
- The workbench environment, all four SOPS-derived Dadbod connections, read-only
|
||||||
|
example-query runs, and Kulala response chaining passed. The cross-system
|
||||||
|
investigation and Marimo report pilots move into phase two.
|
||||||
|
- Add another ephemeral read-only rclone remote only when credentials for a new
|
||||||
|
Leafcloud project become available. Irristaud listing has passed; do not upload.
|
||||||
|
- Use the reviewed local Yookr OpenAPI contract while the accepted malformed
|
||||||
|
generated test document remains deployed.
|
||||||
|
- Herdr's bounded issue 197 pilot is complete. Documented named-session
|
||||||
|
detach/reattach persistence is accepted without a redundant live trial, and
|
||||||
|
notifications are intentionally disabled. Codex/Claude fallback, attention
|
||||||
|
states, local branches, parallel non-overlapping plans, and the dirty-removal
|
||||||
|
guard passed; the plans show that implementation belongs outside the dashboard
|
||||||
|
repository.
|
||||||
|
- Keep Bruno and DBeaver; retiring old applications is outside phase one.
|
||||||
|
|
||||||
|
## Deferred pilots
|
||||||
|
|
||||||
|
Revisit these alongside related phase-two ideas rather than as isolated cleanup.
|
||||||
|
|
||||||
|
- Capture Zenith's external display identity during a docked session and test the
|
||||||
|
two-output `Super+A` exchange, including name and focus restoration on failure.
|
||||||
|
- Resume Evolution and Android PIM only when the user chooses to run those daily
|
||||||
|
use pilots.
|
||||||
|
- Replicate the proven workstation workflow on Polaris only when the user resumes
|
||||||
|
its machine-local VPN and credential procedure.
|
||||||
|
|
||||||
|
## Deferred work
|
||||||
|
|
||||||
|
- Define the team release process and OpenAPI publication policy before encoding
|
||||||
|
deployment automation.
|
||||||
|
- Adopt a company-wide infrastructure secret source only after Yookr selects one.
|
||||||
|
- Publish `yookr-workbench` only when sharing becomes useful.
|
||||||
|
- Pilot SharePoint browse/download through `rclone` after Microsoft consent.
|
||||||
|
- Revisit Worktrunk, `codex agents`, Walker-to-agent launching, formal agent
|
||||||
|
evaluation, remote builders, CI runners, and agent offload only after a concrete
|
||||||
|
gap appears.
|
||||||
|
- Add report servers, notebook hubs, groupware, terminal PIM, meeting summaries,
|
||||||
|
concurrency isolation, OCR, dictation, clipboard remapping, or additional
|
||||||
|
convenience surfaces only after repeated use establishes an owner.
|
||||||
|
|
||||||
|
## Reference baseline
|
||||||
|
|
||||||
|
Recheck fast-moving interfaces against their pinned versions before changing
|
||||||
|
them.
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD013 -->
|
||||||
|
|
||||||
|
- Projects: [Nix flake templates](https://nix.dev/manual/nix/latest/command-ref/new-cli/nix3-flake-init), [nix-direnv](https://github.com/nix-community/nix-direnv), [just manual](https://just.systems/man/en/), [Podman Compose](https://docs.podman.io/en/latest/markdown/podman-compose.1.html), [Quadlet](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html), [Marimo](https://docs.marimo.io/).
|
||||||
|
- APIs/data: [Kulala](https://kulala.app/), [Dadbod](https://github.com/tpope/vim-dadbod), [RabbitMQ management](https://www.rabbitmq.com/docs/management), [rclone S3](https://rclone.org/s3/), [rclone OneDrive/SharePoint](https://rclone.org/onedrive/).
|
||||||
|
- Desktop: [Walker](https://walkerlauncher.com/docs/providers), [Elephant menus](https://github.com/abenz1267/elephant/tree/master/internal/providers/menus), [Vivaldi web apps](https://help.vivaldi.com/desktop/miscellaneous/progressive-web-apps/), [Niri IPC](https://niri-wm.github.io/niri/IPC.html).
|
||||||
|
- Agents: [Codex configuration](https://learn.chatgpt.com/docs/config-file/config-basic), [Codex long-running work](https://learn.chatgpt.com/docs/long-running-work), [Herdr](https://herdr.dev/docs/configuration/), [Git worktrees](https://git-scm.com/docs/git-worktree).
|
||||||
|
- PIM/knowledge: [Evolution account types](https://help.gnome.org/evolution/intro-account-types.html), [Radicale](https://radicale.org/v3.html), [DAVx5](https://manual.davx5.com/introduction.html), [Tasks.org](https://tasks.org/docs/), [`zk` daily journal](https://github.com/zk-org/zk/blob/main/docs/tips/daily-journal.md).
|
||||||
|
- Infrastructure: [Gardenctl](https://gardener.cloud/docs/gardenctl-v2/), [NetworkManager](https://networkmanager.pages.freedesktop.org/NetworkManager/NetworkManager/nmcli.html), [Syncthing](https://docs.syncthing.net/users/syncing).
|
||||||
|
|
||||||
|
<!-- markdownlint-enable MD013 -->
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# Current work
|
||||||
|
|
||||||
|
## Phase and working method
|
||||||
|
|
||||||
|
Implementation is authorized and underway. Do not restart the broad design pass or
|
||||||
|
reconfirm accepted choices. `docs/design.md` owns the model, decisions and coding
|
||||||
|
conventions; README owns navigation/maintenance; operational details live with owners.
|
||||||
|
|
||||||
|
Complete independent agreed work and obvious fixes autonomously. Leave consequential
|
||||||
|
unknowns pending and collect recommendations/alternatives into one substantial review
|
||||||
|
batch. Ask earlier only to prevent broad rework/blockage. The old 2–4-question quota
|
||||||
|
is superseded. Keep effort focused; no speculative systems or peripheral research.
|
||||||
|
Do not write or run tests or perform extensive validation unless explicitly requested.
|
||||||
|
Do not activate, push, decrypt for discovery or access company systems with write
|
||||||
|
authority. No subagents unless explicitly authorized by current instructions.
|
||||||
|
|
||||||
|
Keep this record current by replacing superseded state. Pass-specific procedures
|
||||||
|
stay here, not in AGENTS. The user must agree before the overall pass is complete.
|
||||||
|
The unedited `~/sync/notes/setup-todo.md` remains an idea inbox, not authorization.
|
||||||
|
|
||||||
|
## Wardrowbe on Orion: implemented, awaiting provisioning and activation
|
||||||
|
|
||||||
|
- Orion selects the Wardrowbe capability at `wardrobe.jelles.net`, with Tinyauth
|
||||||
|
password access at `wardrobe-auth.jelles.net`. Trusted household members select
|
||||||
|
their profile through Wardrowbe's development login after the gateway; profile
|
||||||
|
identity is not independently verified.
|
||||||
|
- Podman containers use immutable image digests, a dedicated network and persistent
|
||||||
|
named volumes. Database/queue health gates migrations and API startup; frontend
|
||||||
|
and workers wait for the API. Caddy protects all application routes; published
|
||||||
|
container ports are localhost-only. Internal AI is disabled everywhere. No
|
||||||
|
Codex bridge, Ollama or paid provider is configured.
|
||||||
|
- Four new system SOPS keys and both DNS records remain user-provisioned. Missing
|
||||||
|
SOPS keys must be supplied before Orion builds or activates. See
|
||||||
|
[Wardrowbe setup](../modules/capabilities/wardrowbe/README.md) for provisioning,
|
||||||
|
activation, operations, upgrade limits and manual acceptance.
|
||||||
|
- Focused source/configuration review only. No tests, host evaluation/build,
|
||||||
|
`just validate`, activation or remote changes. No existing secrets were decrypted
|
||||||
|
or read for discovery.
|
||||||
|
- A user-run Orion build failed compiling Contour's SIMD code, pulled in by
|
||||||
|
`enableAllTerminfo`. Orion now selects Kitty terminfo for workstation SSH clients
|
||||||
|
instead of the complete terminal set. The pinned NixOS module was inspected;
|
||||||
|
formatting and focused diff review only. No rebuild was run after this fix.
|
||||||
|
|
||||||
|
## Implemented, reviewed and validated; awaiting user checkpoint
|
||||||
|
|
||||||
|
- Flat capability owners; service category flattened. AI, theme, Handy and
|
||||||
|
transcription support colocated. Imported Nix helpers moved under `_support/`.
|
||||||
|
File management owns Nautilus/GVfs/udisks; keyring remains separate.
|
||||||
|
- Removed Walker project discovery, agent-deck/notify, tmux, Zellij, TeamViewer and
|
||||||
|
direnv. Removed the uninstalled nixos-maintainer skill source. Retained Herdr,
|
||||||
|
Ollama/grounded-docs and their data, mail/calendar tools, dictation/transcription.
|
||||||
|
- Shared notes launcher uses Kitty's explicit directory and native zk note picker;
|
||||||
|
the picker already supports Ctrl+E creation. Oil's mini.icons provider loads
|
||||||
|
before initial directory buffers. Manual workspace naming/clearing and visible
|
||||||
|
Noctalia labels; shell titles include directory/command. Garden shell initialization
|
||||||
|
now belongs to company integration instead of every host's generic shell.
|
||||||
|
- Kanagawa declares Adwaita/hicolor icon fallback; modest Limine branding/layout.
|
||||||
|
Walker uses upstream Elephant without a local vault patch. It may query `rbw` at startup.
|
||||||
|
- GitHub devenv 2.3.2 installed with native Zsh hook and upstream reload defaults.
|
||||||
|
CLI dependencies keep upstream pins/cache; workbench tools use this repository's
|
||||||
|
exact nixpkgs pin. Company tools are also installed through the work profile. Product and
|
||||||
|
personal environments are untouched. No external loader was sourced or changed.
|
||||||
|
- Workbench runtime SecretSpec export helper resolves company/live/test independently,
|
||||||
|
clears stale owned variables, reports value-free status, and supports tools-only
|
||||||
|
entry/retry. Source mappings/provisioning remain pending below.
|
||||||
|
- User-run encrypted company migration helper prepared, never run. Company document
|
||||||
|
absent until user provisioning. Test mapping and login remain deliberately
|
||||||
|
unimplemented until the concrete context and plugin are known; the loader accepts
|
||||||
|
only an explicit noninteractive runtime snapshot. No company credential values
|
||||||
|
have been read.
|
||||||
|
- Both workstations select persistent read-only OBS/SharePoint user mounts, no content
|
||||||
|
cache; replaced Zenith-only hourly mirror. Existing data is preserved. Credentials,
|
||||||
|
old OBS mount handover and runtime acceptance remain user-owned.
|
||||||
|
- Tailscale enabled on all hosts; ordinary SSH keys retained. Zenith SSH is tailnet-only,
|
||||||
|
Polaris LAN SSH unchanged, Orion public SSH unchanged pending verification.
|
||||||
|
- Documentation consolidated; coding conventions in design, short AGENTS retained.
|
||||||
|
Validation prepares upstream devenv's import-from-derivation dependencies before
|
||||||
|
read-only flake checking, accepts the repository's declared official cache, and
|
||||||
|
reports interruption concisely. Full builds still include the tools-only shell.
|
||||||
|
The devenv cache is now in persistent NixOS Nix settings; the flake cache hint
|
||||||
|
was removed.
|
||||||
|
- Noctalia tracks upstream's `cachix` branch with its own nixpkgs pin; persistent
|
||||||
|
Nix settings include the official cache and signing key. The existing 5.2.1
|
||||||
|
commit is unchanged. Its resulting x86_64 package has a matching signed cache
|
||||||
|
entry; the previous package with our nixpkgs pin returned 404. No build or
|
||||||
|
activation was run. For the first activation, pass the cache URL and key from
|
||||||
|
`modules/capabilities/nix.nix` as `extra-substituters` and
|
||||||
|
`extra-trusted-public-keys` options to `nh os switch`; afterward Nix uses the
|
||||||
|
persistent settings.
|
||||||
|
|
||||||
|
## Decisions/provisioning still needed
|
||||||
|
|
||||||
|
1. **Company-root activation.** Native devenv suppresses its hook inside an active
|
||||||
|
devenv shell, including nested projects. No root environment is generated now;
|
||||||
|
recommend globally installed company tools at root and autoactivation only in
|
||||||
|
workbench. This avoids custom activation machinery; confirm at the user checkpoint.
|
||||||
|
2. **Test SecretSpec mapping.** Need the explicit test context and existing application
|
||||||
|
Secret/namespace/key coordinates (metadata only, no values). Confirm whether
|
||||||
|
database Secrets hold complete URLs or separate components. Do not invent keys,
|
||||||
|
silently use the current context or fall back to live/SOPS test credentials.
|
||||||
|
Test loading reports unavailable until its runtime manifest and login snapshot exist.
|
||||||
|
3. **Explicit-login mechanism.** Final review removed the generic ExecCredential
|
||||||
|
interpreter: it duplicated a security-sensitive Kubernetes client protocol before
|
||||||
|
the actual company plugin was known. Inspect the selected context's exec stanza,
|
||||||
|
then choose its native/provider-specific cached login flow and prepare a mode-0600
|
||||||
|
runtime snapshot. Shell entry must not launch authentication.
|
||||||
|
4. **Live MariaDB certificate verification.** The migration helper preserves the
|
||||||
|
old `ssl-verify-server-cert=0` behavior. Before running it, either explicitly accept
|
||||||
|
that legacy risk or identify the correct CA/hostname settings and enable verification.
|
||||||
|
5. **Manual provisioning/acceptance.** Company SOPS recipients/migration, rclone
|
||||||
|
OAuth on both hosts, old OBS mount handover, devenv trust/responsiveness, Noctalia
|
||||||
|
Radicale password and private Outlook ICS URL, tailnet enrollment/private SSH.
|
||||||
|
See workbench README and root README. Do not close public Orion SSH yet.
|
||||||
|
6. **Guidelines review.** Design now spells out owner naming, module selection,
|
||||||
|
helper placement, package reuse/pinning, runtime boundaries and validation. No new
|
||||||
|
test policy, automation framework or pass-specific repo-wide rules were added.
|
||||||
|
|
||||||
|
## Validation state
|
||||||
|
|
||||||
|
Final full validation passed before the final review deletions:
|
||||||
|
`/tmp/nixos-validate-t7zv1zux` (session 88912,
|
||||||
|
completed exit 0). Formatting, devenv preparation, flake check, all three host
|
||||||
|
builds (Orion, Polaris, Zenith), and the tools-only workbench build passed.
|
||||||
|
This includes the final notes startup ordering, FUSE wrapper paths, native Zsh retry
|
||||||
|
function and SecretSpec package collision fix. The vault patch was removed afterward.
|
||||||
|
No systems were activated.
|
||||||
|
|
||||||
|
Native devenv tools-only startup also passed in an isolated temporary home:
|
||||||
|
`/tmp/foundation-native/startup.log`. Company/live/test loading was disabled;
|
||||||
|
SecretSpec 0.20.0 and Python 3.14.7 were available. No company credentials loaded.
|
||||||
|
|
||||||
|
Clean-home manual editor checks passed: direct directory startup renders a Lua-file
|
||||||
|
icon in Oil; opening the note picker after VimEnter reaches TelescopePrompt.
|
||||||
|
Evidence: `/tmp/foundation-editor-check/{startup,notes}.log`. No real notebook or
|
||||||
|
credentials loaded. Built Kanagawa index declares `Inherits=Adwaita,hicolor`.
|
||||||
|
Python source syntax and local documentation links checked; no automated tests added.
|
||||||
|
|
||||||
|
The earlier final review removed the unaccepted Kubernetes login wrapper and the
|
||||||
|
machine-wide devenv cache setting; the flake-scoped cache remains. Full builds were
|
||||||
|
not repeated for those deletions. The later Walker patch removal has not been built.
|
||||||
|
|
||||||
|
Builds do not establish interactive acceptance. The user checkpoint remains; do not
|
||||||
|
repeat full builds for documentation-only handoff edits.
|
||||||
|
|
||||||
|
## Review checkpoint
|
||||||
|
|
||||||
|
The final diff was reviewed for correctness, integration and Ponytail complexity.
|
||||||
|
Desktop/capability moves and repository wiring had no material findings. The review
|
||||||
|
removed the speculative Kubernetes login interpreter. The devenv cache was later
|
||||||
|
moved from flake scope to machine-wide Nix settings at the user's request. The
|
||||||
|
remaining Python has a concrete role: in-memory concurrent secret loading with
|
||||||
|
completeness checks, one-time encrypted credential reshaping, dynamic OBS rclone
|
||||||
|
configuration, or pre-existing dictation/transcription behavior.
|
||||||
|
|
||||||
|
The user checkpoint is still required before this pass is complete. The implementation
|
||||||
|
was committed as `5748b34` after the initial foundation commit `f164d8b`. Later
|
||||||
|
cleanup removed the Walker patch, Handy test and AI capability README.
|
||||||
|
No activation was recorded during that pass. External workbench analysis/content
|
||||||
|
changes are unrelated and must not be committed here. Previous external loader
|
||||||
|
copies remain at `/tmp/workbench-environment-before-6k4dwzuo`; only root/workbench environment documentation and ignore entries were updated.
|
||||||
|
Backups: `/tmp/foundation-workbench-docs-bvrs1hu9`. Old external loaders remain
|
||||||
|
until user activation, so the currently running system is not broken. Constellation documents were already
|
||||||
|
superseded and removed in the prior foundation commit.
|
||||||
|
|
||||||
|
Deliberate deferrals remain in design: backups declined, dashboard scrapped,
|
||||||
|
Zenith Plymouth and extra Noctalia keyboard controls deferred, stronger credential
|
||||||
|
isolation/new agent workflows/indexing and unrelated editor redesign out of scope.
|
||||||
+141
@@ -0,0 +1,141 @@
|
|||||||
|
# Repository design
|
||||||
|
|
||||||
|
Working decisions, open to evidence from implementation and use.
|
||||||
|
|
||||||
|
## Model and ownership
|
||||||
|
|
||||||
|
Build a quiet, keyboard-driven, text-oriented environment from understandable tools
|
||||||
|
and standard interfaces. Humans and agents use the same underlying resources.
|
||||||
|
Graphical applications are deliberate choices where they fit. Streamline existing
|
||||||
|
workflows rather than requiring a new way of working.
|
||||||
|
|
||||||
|
- **Host:** machine identity, hardware and differences in selection.
|
||||||
|
- **Profile:** a deliberate combination of capabilities.
|
||||||
|
- **Capability:** behavior and necessary integration, potentially spanning several
|
||||||
|
applications, NixOS and Home Manager.
|
||||||
|
- **Facts:** shared identity and topology consumed in multiple places.
|
||||||
|
- **Theme:** application-specific appearance, including plugins and assets.
|
||||||
|
Interaction and layout belong to capabilities.
|
||||||
|
|
||||||
|
Polaris and Zenith have the same intended daily use. The `work` profile on both is
|
||||||
|
a provisional ownership boundary, not a different workstation variant. Steam and
|
||||||
|
LAN SSH on Polaris, and laptop power/firmware management on Zenith, are deliberate.
|
||||||
|
Orion owns shared services. Tailnet administration supplements existing access;
|
||||||
|
public Orion SSH closes only after private access is verified. Web services stay public.
|
||||||
|
Orion installs Kitty terminfo for SSH sessions from the workstations.
|
||||||
|
|
||||||
|
Wardrowbe uses a password gateway for trusted household access, followed by profile
|
||||||
|
selection inside the app; verified individual identity is deferred. Its internal AI
|
||||||
|
is disabled. Paid API usage, a Codex bridge and local model integration are deferred.
|
||||||
|
|
||||||
|
Nix owns reproducible installation, integration and selected settings. Applications
|
||||||
|
own credentials, sessions, content and deliberately interactive preferences. No
|
||||||
|
plaintext secrets enter Git, Nix evaluation or store outputs. Company credentials
|
||||||
|
use a separate encrypted SOPS document in this private Gitea repository and the
|
||||||
|
existing per-machine age keys. Credential availability is not operational authority.
|
||||||
|
|
||||||
|
## Coding and composition conventions
|
||||||
|
|
||||||
|
Use a flat set of capability owners: `name.nix` when self-contained, otherwise
|
||||||
|
`name/default.nix` with its scripts, assets and operational notes beside it. Put
|
||||||
|
manually imported Nix helpers under that owner's `_support/` so import-tree does
|
||||||
|
not discover them. Host hardware helpers remain private to their host.
|
||||||
|
|
||||||
|
Discovery publishes modules; explicit imports select behavior. Prefer existing
|
||||||
|
NixOS/Home Manager options and direct module imports. Introduce a shared option or
|
||||||
|
helper only for actual consumers. Use kebab-case owner/module names that describe
|
||||||
|
purpose. NixOS and Home Manager modules may share the same capability name. Small
|
||||||
|
application declarations may remain grouped when they need no substantial integration.
|
||||||
|
Split independent purposes or shared integrations, not every configured package.
|
||||||
|
|
||||||
|
Keep package definitions with their capability and reuse them across installations
|
||||||
|
and development environments. Flake inputs belong in `flake.nix` and are locked in
|
||||||
|
`flake.lock`; fixed external sources need a version/revision and hash. Workbench
|
||||||
|
tools follow the system nixpkgs pin. The GitHub devenv CLI retains its upstream
|
||||||
|
build dependencies. Product environment definitions and pins remain product-owned.
|
||||||
|
Use store-qualified executables or `runtimeInputs` in packaged scripts; make runtime
|
||||||
|
paths and failures explicit. Do not duplicate package lists, generated settings or
|
||||||
|
binding references in documentation.
|
||||||
|
|
||||||
|
These clarify the agreed model; they are not a one-package-per-file rule or a
|
||||||
|
requirement for additional abstractions.
|
||||||
|
|
||||||
|
## Daily environment
|
||||||
|
|
||||||
|
Niri owns navigation and frequent shortcuts; Walker owns application/resource search;
|
||||||
|
Noctalia owns status and system controls. Workspaces remain flexible and optionally
|
||||||
|
named by the user. Terminal titles provide orientation. No project-based workspace
|
||||||
|
scheme, heuristic naming or custom Walker project discovery is wanted.
|
||||||
|
|
||||||
|
Herdr remains an experiment, not a core dependency. Remove agent-deck, tmux and
|
||||||
|
Zellij. Keep Ollama and grounded-docs for experimentation and preserve their data.
|
||||||
|
Ollama's embedding model supports grounded-docs. Keep Handy for local dictation and
|
||||||
|
Orion transcription. Handy preferences, recordings and models remain application-owned.
|
||||||
|
Its push-to-talk release listener requires active-user keyboard access; press from
|
||||||
|
idle, with Super and D on the same keyboard.
|
||||||
|
|
||||||
|
Both notes entry points open the existing zk note picker with note creation.
|
||||||
|
Notes stay in `~/sync/notes`, mostly meeting material; no new taxonomy. HTTP and
|
||||||
|
database targets remain independently selected and visibly named. Dadbod working
|
||||||
|
queries and connection state stay outside Git; see the editor's `database.lua`.
|
||||||
|
|
||||||
|
Kanagawa remains the theme with Adwaita fallback icons. Walker uses upstream Elephant
|
||||||
|
without a local vault patch; it may query rbw during startup. Outlook PWA owns work
|
||||||
|
email/calendar editing. Mailspring owns personal mail; Thunderbird remains a backup. Noctalia
|
||||||
|
provides the combined read-only Radicale/Outlook agenda. The user accepts published
|
||||||
|
ICS link access and supplies the URL privately through runtime settings. Personal
|
||||||
|
calendar editing stays on the phone; keep khal/todoman.
|
||||||
|
|
||||||
|
## Company environment
|
||||||
|
|
||||||
|
`~/yookr` is the company root; product repositories are in `~/yookr/projects`.
|
||||||
|
`~/yookr/workbench` is the access point for company services, databases and files.
|
||||||
|
This repository owns its environment; requests, queries, analysis, findings, Python
|
||||||
|
lock, content and private context stay writable there. The workbench capability
|
||||||
|
owns setup and credential/mount instructions.
|
||||||
|
|
||||||
|
Native devenv replaces direnv, initially with default reload behavior. Migration is
|
||||||
|
limited to workbench and company-root integration. Product, personal, archived and
|
||||||
|
learning projects are user-migrated; do not retain direnv as a transition.
|
||||||
|
|
||||||
|
Use SecretSpec with existing test-cluster application Secrets and SOPS-backed live
|
||||||
|
credentials. There is no live-cluster access. Company login is explicit. Sources
|
||||||
|
must fail independently, tools remain available, and retries must clear stale values.
|
||||||
|
Normal human editor startup can receive both targets; per-connection decryption and
|
||||||
|
a global environment selector are not wanted. Stronger agent credential isolation
|
||||||
|
is deferred; existing restrictions on company operations remain in force.
|
||||||
|
|
||||||
|
OBS and SharePoint are continuously available as read-only mounts under the workbench
|
||||||
|
on both workstations. User services own lifecycle, independent of shell entry.
|
||||||
|
No persistent content cache or hourly mirror; memory buffers and metadata are accepted.
|
||||||
|
Same-user processes, including agents, can read these mounts. Preserve old local data.
|
||||||
|
|
||||||
|
## Knowledge and maintenance
|
||||||
|
|
||||||
|
README owns navigation and maintenance; AGENTS owns essential durable collaboration
|
||||||
|
constraints; this document owns rationale/conventions; current-work owns resumable
|
||||||
|
state. Operational details live beside their owner. Use Git for chronology, replace
|
||||||
|
superseded state, and remove temporary state when no longer useful.
|
||||||
|
|
||||||
|
Keep instructions short and authoritative. Add a rule when it prevents a concrete
|
||||||
|
mistake, resolves recurring ambiguity or points to its owner. Prefer formatter and
|
||||||
|
validation feedback to repeated reminders. This applies the useful principle of
|
||||||
|
progressive disclosure from [OpenAI's harness engineering article](https://openai.com/index/harness-engineering/)
|
||||||
|
without introducing speculative skills, orchestration or evaluation infrastructure.
|
||||||
|
|
||||||
|
`just validate` is available on explicit request. It includes working-tree changes
|
||||||
|
and new nonignored files without staging, formats, evaluates and builds. Targeted
|
||||||
|
host builds are supported; a full run also builds workbench tools. Builds do not
|
||||||
|
prove activation or interactive behavior. Do not write or run tests or perform
|
||||||
|
extensive validation unless explicitly requested.
|
||||||
|
|
||||||
|
Keep native update and activation commands. `nix flake update` is normal; selective
|
||||||
|
updates (often `llm-agents`) are appropriate while nixpkgs breakage awaits repair.
|
||||||
|
Activation remains user-owned. No trivial `just` aliases for native commands.
|
||||||
|
|
||||||
|
## Deliberately deferred
|
||||||
|
|
||||||
|
No backups (user declined), Orion dashboard/monitoring (scrapped), new agent
|
||||||
|
workflows/persistence/indexing/isolation, or broader editor redesign without friction.
|
||||||
|
Zenith's Plymouth refresh and extra Noctalia keyboard controls are deferred.
|
||||||
|
The existing single Syncthing `~/sync` layout is retained. TeamViewer is removed.
|
||||||
Generated
+351
-132
@@ -20,19 +20,97 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784665499,
|
"lastModified": 1790001035,
|
||||||
"narHash": "sha256-9BMxlTxCCDAeoNLtb1a/st7udtTIJep+wpUzquA29VU=",
|
"narHash": "sha256-1/SLBjSYBzDdDhvLQ83wrzBakyS/xNGgtxHGNuveOEQ=",
|
||||||
"owner": "nix-community",
|
"owner": "Mic92",
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"rev": "0f2a1f0b6f42cebe3b149bf62d38754c5e0e9729",
|
"rev": "07a5bfc8ac36c5370199343fa620b69f63186e33",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "Mic92",
|
||||||
|
"ref": "llm-agents",
|
||||||
"repo": "bun2nix",
|
"repo": "bun2nix",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cachix": {
|
||||||
|
"inputs": {
|
||||||
|
"devenv": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"git-hooks": [
|
||||||
|
"devenv",
|
||||||
|
"git-hooks"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1788181969,
|
||||||
|
"narHash": "sha256-OUB6hPlFBB9FRdZgZXSye4lDOg+fbrqKe8ePv2IM7NY=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "cachix",
|
||||||
|
"rev": "98093e8eea6c1635ef1337a576fa76f3e8bd4f39",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "latest",
|
||||||
|
"repo": "cachix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"crate2nix": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772186516,
|
||||||
|
"narHash": "sha256-8s28pzmQ6TOIUzznwFibtW1CMieMUl1rYJIxoQYor58=",
|
||||||
|
"owner": "rossng",
|
||||||
|
"repo": "crate2nix",
|
||||||
|
"rev": "ba5dd398e31ee422fbe021767eb83b0650303a6e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rossng",
|
||||||
|
"repo": "crate2nix",
|
||||||
|
"rev": "ba5dd398e31ee422fbe021767eb83b0650303a6e",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devenv": {
|
||||||
|
"inputs": {
|
||||||
|
"cachix": "cachix",
|
||||||
|
"crate2nix": "crate2nix",
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"ghostty": "ghostty",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
|
"nix": "nix",
|
||||||
|
"nixd": "nixd",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791388908,
|
||||||
|
"narHash": "sha256-eqnGuVYIh6u8XU6UXueceaCi3UfSqbvopeuLYgClEDY=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"rev": "27af3b2029a6d1274db8a570fb57292dae5a2551",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "devenv",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"disko": {
|
"disko": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -40,11 +118,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781152676,
|
"lastModified": 1789770686,
|
||||||
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
|
"narHash": "sha256-uZkBR7yHdIKUFB5SZdfgh1qkGfI3XmYmI/lTiquxbck=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
|
"rev": "725ea35e410ad83be4931d1bff7e090eacaf3563",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -56,16 +134,20 @@
|
|||||||
"elephant": {
|
"elephant": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
"walker",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems"
|
"systems": [
|
||||||
|
"walker",
|
||||||
|
"systems"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784365424,
|
"lastModified": 1790316550,
|
||||||
"narHash": "sha256-kJO1Tww/GkvLq5amrV4BKidYGr5YumM+cH/xYALAiw4=",
|
"narHash": "sha256-Qr/8JQ8zpmmWmZ7UAjhxVdRbSgwQ0uc2r/wUBMe4NBk=",
|
||||||
"owner": "abenz1267",
|
"owner": "abenz1267",
|
||||||
"repo": "elephant",
|
"repo": "elephant",
|
||||||
"rev": "23f37238367355cf46843015ad5e94706200176a",
|
"rev": "a0670fb2cf590a3ceed6f799a45947b86a78d5f4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -74,16 +156,35 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767039857,
|
||||||
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "edolstra",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787559586,
|
"lastModified": 1788306937,
|
||||||
"narHash": "sha256-onL0VLf9vPllmT0H/OlURIU5r5t5WIEl7t4tVNKT0Nw=",
|
"narHash": "sha256-lbrI1UYVpFgKSsyrU9oeF7FHfnu8nLD9ja0ou9f2rxk=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "9d0d87172c374f89da73c1cfe6d81ae62feac1f1",
|
"rev": "f16b25b8c3d2809b87925d0b76652d7821a75c68",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -94,17 +195,14 @@
|
|||||||
},
|
},
|
||||||
"flake-parts_2": {
|
"flake-parts_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
"llm-agents",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785627969,
|
"lastModified": 1790898470,
|
||||||
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
|
"narHash": "sha256-zTwuwezD0w3hpga6a6P8emidzAZFyWqlNeRBlpWOOl8=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
|
"rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -113,22 +211,66 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"helix": {
|
"flake-parts_3": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs-lib": [
|
||||||
"rust-overlay": "rust-overlay"
|
"llm-agents",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784822617,
|
"lastModified": 1790898470,
|
||||||
"narHash": "sha256-IYDL6Vnf13Sa+wbeXZTAxvdLA4h8Ew5ha0spcJy/Yk0=",
|
"narHash": "sha256-zTwuwezD0w3hpga6a6P8emidzAZFyWqlNeRBlpWOOl8=",
|
||||||
"owner": "helix-editor",
|
"owner": "hercules-ci",
|
||||||
"repo": "helix",
|
"repo": "flake-parts",
|
||||||
"rev": "079a789e8cb08ead67f19e1971a1b7438b37354b",
|
"rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "helix-editor",
|
"owner": "hercules-ci",
|
||||||
"repo": "helix",
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ghostty": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1786026742,
|
||||||
|
"narHash": "sha256-tRtgTg/i3w1nFdRHU2IGekfJN8EyP+HARW4MtoUZyqk=",
|
||||||
|
"owner": "ghostty-org",
|
||||||
|
"repo": "ghostty",
|
||||||
|
"rev": "22d13172cde98a0a4dda05d3d6a3fcb0dd8ed018",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ghostty-org",
|
||||||
|
"repo": "ghostty",
|
||||||
|
"rev": "22d13172cde98a0a4dda05d3d6a3fcb0dd8ed018",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1788267358,
|
||||||
|
"narHash": "sha256-nt+lUqYVpc9Y6JeMd2WmXzCDojasdadKo0mWcluvY2Y=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "27555e2624241fb116b49095df4caaee85a25691",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -139,11 +281,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787540599,
|
"lastModified": 1791306979,
|
||||||
"narHash": "sha256-MHwk52ty3NsLhq9a1IbIfBJZlCUJIdiUYFo52JLK9hY=",
|
"narHash": "sha256-KuLePuhwJM9NLt/gTLHzXaOoSZKGoB3m7NfXjiQ98HY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "03f4cd46bc1dd4f3a96da778d2ce9f7ce39dd450",
|
"rev": "fae6e9e42c3b762ab47635cddcfaf6f52374a61b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -154,11 +296,11 @@
|
|||||||
},
|
},
|
||||||
"import-tree": {
|
"import-tree": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784254960,
|
"lastModified": 1788467110,
|
||||||
"narHash": "sha256-iI88R3wHz8wTKQb5orvpc51L/Xr64AJyxid/0MKa/b8=",
|
"narHash": "sha256-ljEMTXP/rH0tOvDzc9gzwww6KcHRPRnEHzd9lK48V7s=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "import-tree",
|
"repo": "import-tree",
|
||||||
"rev": "4ebb10ae17d5f1ad366e7aef5b92cb8eecf24f69",
|
"rev": "eb1b52eaecc57f7c136d07ae8a93e724dfecac46",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -170,19 +312,17 @@
|
|||||||
"llm-agents": {
|
"llm-agents": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"bun2nix": "bun2nix",
|
"bun2nix": "bun2nix",
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts_3",
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs"
|
"systems": "systems",
|
||||||
],
|
"treefmt-nix": "treefmt-nix_2"
|
||||||
"systems": "systems_2",
|
|
||||||
"treefmt-nix": "treefmt-nix"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787551962,
|
"lastModified": 1791440787,
|
||||||
"narHash": "sha256-zfumhlJTGDKSILebSBa8nTKG76ByIPlLkGPAKjxaiwE=",
|
"narHash": "sha256-nRO1wEJmb4wjz+0Mhpg5VWURTqZRBZq3MvMjtPkKuAI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "llm-agents.nix",
|
"repo": "llm-agents.nix",
|
||||||
"rev": "46e6a013fd49779cd2def2c98fa447f197da3fe7",
|
"rev": "2109db8971dc18137ff1ac9393627c761609600d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -191,6 +331,46 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"devenv",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"flake-parts": [
|
||||||
|
"devenv",
|
||||||
|
"flake-parts"
|
||||||
|
],
|
||||||
|
"git-hooks-nix": [
|
||||||
|
"devenv",
|
||||||
|
"git-hooks"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-23-11": [
|
||||||
|
"devenv"
|
||||||
|
],
|
||||||
|
"nixpkgs-regression": [
|
||||||
|
"devenv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1790176416,
|
||||||
|
"narHash": "sha256-acP7QvsB+dHghUeL9AwSSZ3qS//skpqjv484aveg1EQ=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "nix",
|
||||||
|
"rev": "bd10d7f563420c6034b9d1dc75c4168fc01e5afc",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"ref": "devenv-2.35",
|
||||||
|
"repo": "nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix-index-database": {
|
"nix-index-database": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -198,11 +378,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787457452,
|
"lastModified": 1791105314,
|
||||||
"narHash": "sha256-FJh4esFS3zqNNuKwvN3t6wrJGewqp1AUF9DAEvoKPD8=",
|
"narHash": "sha256-oTM7QFgRQ+vxBpMBMc7hEYpqBtpXAzXu2xuB/tru84U=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nix-index-database",
|
"repo": "nix-index-database",
|
||||||
"rev": "c51d5c2ba69c907a34e90c9b6b80cd2b93811745",
|
"rev": "e740bc4e9d8918d7819a51486d5099930c65c72e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -218,11 +398,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787579952,
|
"lastModified": 1791440872,
|
||||||
"narHash": "sha256-Dn6IPaLyltx8hBTyC4hgNWDb7T7WHRYUG6wWh6KBHXA=",
|
"narHash": "sha256-PwiSn1VO3/6BD/Gxw/3Ei2sPB3SccyKyJ7nD72Y+WU4=",
|
||||||
"owner": "BirdeeHub",
|
"owner": "BirdeeHub",
|
||||||
"repo": "nix-wrapper-modules",
|
"repo": "nix-wrapper-modules",
|
||||||
"rev": "b56e973dd2bd62244b44d6542fb52182bc3a3cda",
|
"rev": "d82c328c504648fa952f25fac8332319eb425d4c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -231,16 +411,44 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixd": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_2"
|
"flake-parts": [
|
||||||
|
"devenv",
|
||||||
|
"flake-parts"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"devenv",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787144466,
|
"lastModified": 1788165251,
|
||||||
"narHash": "sha256-HHfv2/HkNSKbbSyU9iD/g8lbP6r4tl33sSw1W4rXCk0=",
|
"narHash": "sha256-ITHt6eU6DuwQqlNV1/wehvKYPE6J8sZIlv75CpDjcHI=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixd",
|
||||||
|
"rev": "e2ca72c353cfbc3d63e942fe9aeae4ec497863bf",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixd",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixos-hardware": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791373788,
|
||||||
|
"narHash": "sha256-c3oMt0QoE+4yiRk6u1m2L9ogjaUw0uTGn9yk808KkgE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "0471accf8d0a8210b31d947497d179ecc99e0021",
|
"rev": "4bc63156b109ee7b4103e3ec30565c8c92bde435",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -251,28 +459,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-src": "nixpkgs-src"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775036866,
|
"lastModified": 1787753358,
|
||||||
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
"narHash": "sha256-Tl77VbWyAKrOfRNQhL6JbQtb/MLzbYa/1RG1gWWfICk=",
|
||||||
"owner": "nixos",
|
"owner": "cachix",
|
||||||
"repo": "nixpkgs",
|
"repo": "devenv-nixpkgs",
|
||||||
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
"rev": "256551e45f6303e142ab4a98be1bf243feb77dc0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "cachix",
|
||||||
"ref": "nixos-unstable",
|
"ref": "rolling",
|
||||||
"repo": "nixpkgs",
|
"repo": "devenv-nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785031560,
|
"lastModified": 1790477367,
|
||||||
"narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=",
|
"narHash": "sha256-oujbxmVKDhci2E4EM3KyAbRUgp5/IsCoiA/LfEF7OOk=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixpkgs.lib",
|
"repo": "nixpkgs.lib",
|
||||||
"rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c",
|
"rev": "f7cd230690d9fc982d06129f9189b101fddcfa3b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -281,42 +492,46 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-src": {
|
||||||
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787414105,
|
"lastModified": 1787394516,
|
||||||
"narHash": "sha256-WncT27+3BOkgTaJZLnCsf3LcYf9RXMuR9ONSN4rzQ7s=",
|
"narHash": "sha256-pRGOQSClnXNI2iLUG6DYpsGvYcuw0drOutVZFTJNw90=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a9e6d84f9c2f9012f5fe7d964a7851352300e61a",
|
"rev": "c8f90650c15282fa8656a041bfbbd2403997a9a7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-26.05",
|
"ref": "nixpkgs-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767892417,
|
"lastModified": 1790600678,
|
||||||
"narHash": "sha256-8bW3q88CEg2u4hSP66Vf4lpbLonHz7hqDNBMcCY7E9U=",
|
"narHash": "sha256-6k3n7X2UsZ6tAlWTz99RimiZab+17tMUrw9sRUcELiA=",
|
||||||
"rev": "3497aa5c9457a9d88d71fa93a4a8368816fbeeba",
|
"owner": "NixOS",
|
||||||
"type": "tarball",
|
"repo": "nixpkgs",
|
||||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre924538.3497aa5c9457/nixexprs.tar.xz"
|
"rev": "f45c6f04c2f013f004bf94e284e95d72898d9393",
|
||||||
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"owner": "NixOS",
|
||||||
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787394516,
|
"lastModified": 1791336361,
|
||||||
"narHash": "sha256-wS+SNrj0wobAXQONggzrpetnZ9E0FL7l0WVWDRQ77Sc=",
|
"narHash": "sha256-7WQwlykef1x8n7aoyw4XP6882i2NRs5F6dVCXZ1NI1g=",
|
||||||
"rev": "c8f90650c15282fa8656a041bfbbd2403997a9a7",
|
"rev": "7dd199b0e2993e37b4775ed66b1c291699608c9f",
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1059707.c8f90650c152/nixexprs.tar.xz"
|
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1086972.7dd199b0e299/nixexprs.tar.xz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -325,11 +540,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787360063,
|
"lastModified": 1790822859,
|
||||||
"narHash": "sha256-95aJfHyQTLWslCXFVNB0odgmVkpdmDezQwTg9mhqV1E=",
|
"narHash": "sha256-VBA0QerzzmL+4wxgtwiCdiLwLpJg2/9xOxhgO3BRJQA=",
|
||||||
"rev": "2c423e03bbafcff28bfadc6781a4a8257f205cb5",
|
"rev": "c59305bab2065cfecc4944690d9eedbb56f3a9fa",
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1059570.2c423e03bbaf/nixexprs.tar.zst"
|
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1083223.c59305bab206/nixexprs.tar.zst"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -341,25 +556,25 @@
|
|||||||
"nixpkgs": "nixpkgs_4"
|
"nixpkgs": "nixpkgs_4"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787575682,
|
"lastModified": 1791427429,
|
||||||
"narHash": "sha256-qSjBgNe40+DxiMV+obDrJ+WCI/zynUbOtcXQDAXv5Fk=",
|
"narHash": "sha256-FzM3cunGIn8oDC1afUsXzGdfI/tK5HjPOHzHXFIAVx4=",
|
||||||
"owner": "noctalia-dev",
|
"owner": "noctalia-dev",
|
||||||
"repo": "noctalia",
|
"repo": "noctalia",
|
||||||
"rev": "0f61b0ae07607739189d07a0a7617ef0d8f3796c",
|
"rev": "3e059de4aec58ba5b81f6c366b2b234a58f5ab0a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "noctalia-dev",
|
"owner": "noctalia-dev",
|
||||||
|
"ref": "cachix",
|
||||||
"repo": "noctalia",
|
"repo": "noctalia",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"devenv": "devenv",
|
||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
"elephant": "elephant",
|
"flake-parts": "flake-parts_2",
|
||||||
"flake-parts": "flake-parts",
|
|
||||||
"helix": "helix",
|
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"import-tree": "import-tree",
|
"import-tree": "import-tree",
|
||||||
"llm-agents": "llm-agents",
|
"llm-agents": "llm-agents",
|
||||||
@@ -367,7 +582,6 @@
|
|||||||
"nix-wrapper-modules": "nix-wrapper-modules",
|
"nix-wrapper-modules": "nix-wrapper-modules",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"nixpkgs": "nixpkgs_3",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
|
||||||
"noctalia": "noctalia",
|
"noctalia": "noctalia",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"walker": "walker"
|
"walker": "walker"
|
||||||
@@ -376,16 +590,16 @@
|
|||||||
"rust-overlay": {
|
"rust-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"helix",
|
"devenv",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775358767,
|
"lastModified": 1788332415,
|
||||||
"narHash": "sha256-f2eC+WIfhjevCPQILuV08i/kmKZzYZpUvkom/33VxCA=",
|
"narHash": "sha256-BTFrmyh0oaVDsvA9NNw0YpbbeppTwU0t70iVx672Ew8=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "20fd44bc663daa53a2575e01293e24e681d62244",
|
"rev": "860d7c835ab91bfc8972b67092f5f2db8e9390a0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -401,11 +615,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786629091,
|
"lastModified": 1791103873,
|
||||||
"narHash": "sha256-gkig4nPi1CWc4Z50GBsjE4ygSE7hMpl/TwID2an2Cck=",
|
"narHash": "sha256-nFxM+pKoZ8LJAEnUXARyCaOAloWgaW9kZQOSjWzKcTE=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "a8627b21b9107c5711c96b84f32a9a4b3d45295f",
|
"rev": "dcd241ba97088c22569d1573286e1b9daad340c0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -416,35 +630,20 @@
|
|||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689347949,
|
"lastModified": 1791244623,
|
||||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
"narHash": "sha256-uuanbkFk8a313Zfrxdcz8Us/yQtaxNnKJtX8v0ZPpd8=",
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default-linux",
|
"repo": "default",
|
||||||
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
|
"rev": "d36eeed142f88b5a636f2e3856a53c61654cf29b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default-linux",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_2": {
|
"systems_2": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems_3": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689347949,
|
"lastModified": 1689347949,
|
||||||
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
|
||||||
@@ -462,7 +661,8 @@
|
|||||||
"treefmt-nix": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"llm-agents",
|
"devenv",
|
||||||
|
"nixd",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -480,22 +680,41 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"treefmt-nix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"llm-agents",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1791203292,
|
||||||
|
"narHash": "sha256-vuzVwvjeucSnwKwMBpyP8r5IzdrMKCHEdwTrRFwUN7I=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "03d8ee1bcbc8f9638907bb79edb9673151d0e22b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"walker": {
|
"walker": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"elephant": [
|
"elephant": "elephant",
|
||||||
"elephant"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems_3"
|
"systems": "systems_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784217994,
|
"lastModified": 1791441924,
|
||||||
"narHash": "sha256-gxHJsrJZo2qbKK47Kf/9ho3i/HhRwkL8XvPnczGPd9E=",
|
"narHash": "sha256-2SLAXtgtsZgjK+dyzDAV47oQNoip3dsGX+XXrT/udEk=",
|
||||||
"owner": "abenz1267",
|
"owner": "abenz1267",
|
||||||
"repo": "walker",
|
"repo": "walker",
|
||||||
"rev": "42b3ed88abf50bc52638fb2835b7f17e3ea3ac4c",
|
"rev": "a515085fd39fae80acc086c8fbfd9b7771a9a11d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -2,29 +2,31 @@
|
|||||||
description = "NixOS Configuration";
|
description = "NixOS Configuration";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
|
devenv.url = "github:cachix/devenv";
|
||||||
disko = {
|
disko = {
|
||||||
url = "github:nix-community/disko";
|
url = "github:nix-community/disko";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
helix.url = "github:helix-editor/helix";
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager";
|
url = "github:nix-community/home-manager";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
import-tree.url = "github:vic/import-tree";
|
import-tree.url = "github:vic/import-tree";
|
||||||
llm-agents = {
|
llm-agents.url = "github:numtide/llm-agents.nix";
|
||||||
url = "github:numtide/llm-agents.nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
nix-wrapper-modules = {
|
nix-wrapper-modules = {
|
||||||
url = "github:BirdeeHub/nix-wrapper-modules";
|
url = "github:BirdeeHub/nix-wrapper-modules";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
nixos-hardware = {
|
||||||
|
url = "github:NixOS/nixos-hardware/master";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
|
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
|
||||||
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-26.05";
|
noctalia = {
|
||||||
noctalia.url = "github:noctalia-dev/noctalia";
|
url = "github:noctalia-dev/noctalia/cachix";
|
||||||
|
# Keep upstream's nixpkgs pin to match its cached builds.
|
||||||
|
};
|
||||||
sops-nix = {
|
sops-nix = {
|
||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -34,13 +36,8 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
elephant = {
|
|
||||||
url = "github:abenz1267/elephant";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
walker = {
|
walker = {
|
||||||
url = "github:abenz1267/walker";
|
url = "github:abenz1267/walker";
|
||||||
inputs.elephant.follows = "elephant";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# List repository maintenance commands.
|
||||||
|
default:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# Format, evaluate, and build all hosts and the workbench; optionally name hosts for iteration.
|
||||||
|
validate *hosts:
|
||||||
|
@python3 scripts/validate.py {{hosts}}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
flake.modules.nixos.actual = {
|
||||||
|
imports = [ config.flake.modules.nixos.caddy ];
|
||||||
|
|
||||||
|
services.actual = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
hostname = "127.0.0.1";
|
||||||
|
port = 3000;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.caddy.virtualHosts.${config.facts.services.actual.domain}.extraConfig =
|
||||||
|
"reverse_proxy 127.0.0.1:3000";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.adb =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = [ pkgs.android-tools ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
{ inputs, config, ... }:
|
|
||||||
let
|
|
||||||
hmModules = config.flake.modules.homeManager;
|
|
||||||
nixosModules = config.flake.modules.nixos;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.ai =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
imports = [ nixosModules.podman ];
|
|
||||||
|
|
||||||
home-manager.sharedModules = [ hmModules.ai ];
|
|
||||||
|
|
||||||
nixpkgs.overlays = [ inputs.llm-agents.overlays.shared-nixpkgs ];
|
|
||||||
|
|
||||||
nix.settings = {
|
|
||||||
extra-substituters = [ "https://cache.numtide.com" ];
|
|
||||||
extra-trusted-public-keys = [
|
|
||||||
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.ollama = {
|
|
||||||
enable = true;
|
|
||||||
package = pkgs.ollama-vulkan;
|
|
||||||
loadModels = [ "nomic-embed-text" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.ai =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
services.podman = {
|
|
||||||
volumes = {
|
|
||||||
grounded-docs-config = { };
|
|
||||||
grounded-docs-data = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
containers.grounded-docs = {
|
|
||||||
image = "ghcr.io/arabold/docs-mcp-server:2.4.0";
|
|
||||||
network = "host";
|
|
||||||
volumes = [
|
|
||||||
"grounded-docs-config.volume:/config"
|
|
||||||
"grounded-docs-data.volume:/data"
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
DOCS_MCP_APP_TELEMETRY_ENABLED = "false";
|
|
||||||
DOCS_MCP_EMBEDDING_MODEL = "openai:nomic-embed-text";
|
|
||||||
DOCS_MCP_EMBEDDINGS_VECTOR_DIMENSION = "768";
|
|
||||||
OPENAI_API_BASE = "http://127.0.0.1:11434/v1";
|
|
||||||
OPENAI_API_KEY = "ollama";
|
|
||||||
};
|
|
||||||
exec = "--protocol http --host 127.0.0.1 --port 6280";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.mcp = {
|
|
||||||
enable = true;
|
|
||||||
servers.nixos = {
|
|
||||||
command = "${pkgs.uv}/bin/uvx";
|
|
||||||
args = [ "mcp-nixos" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
home.packages = [
|
|
||||||
pkgs.llm-agents.codex
|
|
||||||
pkgs.llm-agents.pi
|
|
||||||
pkgs.llm-agents.claude-code
|
|
||||||
pkgs.llm-agents.herdr
|
|
||||||
pkgs.ctx7
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
let
|
||||||
|
sources = import ./sources.nix { inherit pkgs; };
|
||||||
|
context7Headers = pkgs.writeShellScript "context7-http-headers" ''
|
||||||
|
set -euo pipefail
|
||||||
|
CONTEXT7_KEY_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/context7/api-key"
|
||||||
|
CONTEXT7_KEY="$(< "$CONTEXT7_KEY_FILE")"
|
||||||
|
exec ${pkgs.jq}/bin/jq --null-input --compact-output \
|
||||||
|
--arg authorization "Bearer $CONTEXT7_KEY" '{ Authorization: $authorization }'
|
||||||
|
'';
|
||||||
|
codexHooks = {
|
||||||
|
SessionStart = [
|
||||||
|
{
|
||||||
|
hooks = [
|
||||||
|
{
|
||||||
|
type = "command";
|
||||||
|
command = "${pkgs.bash}/bin/bash ${sources.herdr}/src/integration/assets/codex/herdr-agent-state.sh session";
|
||||||
|
timeout = 10;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit context7Headers codexHooks;
|
||||||
|
skills = {
|
||||||
|
context7-mcp = ../skills/context7-mcp;
|
||||||
|
herdr = "${sources.herdr}/skills/herdr";
|
||||||
|
};
|
||||||
|
servers = {
|
||||||
|
context7.url = "https://mcp.context7.com/mcp";
|
||||||
|
nixos.command = "${pkgs.mcp-nixos}/bin/mcp-nixos";
|
||||||
|
openaiDeveloperDocs.url = "https://developers.openai.com/mcp";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
{
|
||||||
|
herdr = pkgs.llm-agents.herdr.src;
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
{ inputs, config, ... }:
|
||||||
|
let
|
||||||
|
hmModules = config.flake.modules.homeManager;
|
||||||
|
nixosModules = config.flake.modules.nixos;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.modules.nixos.ai =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
ai = import ./_support/assets.nix { inherit pkgs; };
|
||||||
|
codexConfig = (pkgs.formats.toml { }).generate "codex-config.toml" {
|
||||||
|
model = "gpt-5.6-sol";
|
||||||
|
model_reasoning_effort = "high";
|
||||||
|
plan_mode_reasoning_effort = "high";
|
||||||
|
service_tier = "default";
|
||||||
|
approvals_reviewer = "auto_review";
|
||||||
|
features.hooks = true;
|
||||||
|
tui = {
|
||||||
|
status_line = [
|
||||||
|
"model-with-reasoning"
|
||||||
|
"current-dir"
|
||||||
|
"context-remaining"
|
||||||
|
"five-hour-limit"
|
||||||
|
"weekly-limit"
|
||||||
|
"git-branch"
|
||||||
|
];
|
||||||
|
status_line_use_colors = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
features.plugins = true;
|
||||||
|
# The Nix package lacks the codex-package.json layout the daemon requires.
|
||||||
|
features.daemon_auto_start = false;
|
||||||
|
hooks = ai.codexHooks;
|
||||||
|
mcp_servers = ai.servers // {
|
||||||
|
context7 = ai.servers.context7 // {
|
||||||
|
http_headers_helper = "${ai.context7Headers}";
|
||||||
|
};
|
||||||
|
nixos = ai.servers.nixos // {
|
||||||
|
startup_timeout_sec = 60.0;
|
||||||
|
tools.nix.approval_mode = "approve";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ nixosModules.podman ];
|
||||||
|
|
||||||
|
home-manager.sharedModules = [ hmModules.ai ];
|
||||||
|
|
||||||
|
environment.etc."codex/config.toml".source = codexConfig;
|
||||||
|
|
||||||
|
nixpkgs.overlays = [ inputs.llm-agents.overlays.shared-nixpkgs ];
|
||||||
|
|
||||||
|
nix.settings = {
|
||||||
|
extra-substituters = [ "https://cache.numtide.com" ];
|
||||||
|
extra-trusted-public-keys = [
|
||||||
|
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.ollama = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.ollama-vulkan;
|
||||||
|
loadModels = [ "nomic-embed-text" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.ai =
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
ai = import ./_support/assets.nix { inherit pkgs; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.podman = {
|
||||||
|
volumes = {
|
||||||
|
grounded-docs-config = { };
|
||||||
|
grounded-docs-data = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
containers.grounded-docs = {
|
||||||
|
image = "ghcr.io/arabold/docs-mcp-server:2.4.0";
|
||||||
|
network = "host";
|
||||||
|
volumes = [
|
||||||
|
"grounded-docs-config.volume:/config"
|
||||||
|
"grounded-docs-data.volume:/data"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
DOCS_MCP_APP_TELEMETRY_ENABLED = "false";
|
||||||
|
DOCS_MCP_EMBEDDING_MODEL = "openai:nomic-embed-text";
|
||||||
|
DOCS_MCP_EMBEDDINGS_VECTOR_DIMENSION = "768";
|
||||||
|
OPENAI_API_BASE = "http://127.0.0.1:11434/v1";
|
||||||
|
OPENAI_API_KEY = "ollama";
|
||||||
|
};
|
||||||
|
exec = "--protocol http --host 127.0.0.1 --port 6280";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
home.packages = [
|
||||||
|
# Node-based Codex plugin hooks and skill scripts need node/npm outside project dev shells.
|
||||||
|
pkgs.nodejs
|
||||||
|
pkgs.python3
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.codex
|
||||||
|
pkgs.bubblewrap
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.chatgpt
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.pi
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.claude-code
|
||||||
|
inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.herdr
|
||||||
|
pkgs.ctx7
|
||||||
|
];
|
||||||
|
|
||||||
|
xdg.configFile."herdr/config.toml".text = ''
|
||||||
|
onboarding = false
|
||||||
|
|
||||||
|
[ui]
|
||||||
|
agent_panel_sort = "spaces"
|
||||||
|
prompt_new_tab_name = false
|
||||||
|
|
||||||
|
[ui.toast]
|
||||||
|
delivery = "off"
|
||||||
|
|
||||||
|
[keys]
|
||||||
|
# Super belongs to Niri; Ctrl navigation belongs to Neovim.
|
||||||
|
focus_pane_left = ["prefix+h", "ctrl+alt+h"]
|
||||||
|
focus_pane_down = ["prefix+j", "ctrl+alt+j"]
|
||||||
|
focus_pane_up = ["prefix+k", "ctrl+alt+k"]
|
||||||
|
focus_pane_right = ["prefix+l", "ctrl+alt+l"]
|
||||||
|
next_workspace = "ctrl+alt+u"
|
||||||
|
previous_workspace = "ctrl+alt+i"
|
||||||
|
navigate_workspace_down = "u"
|
||||||
|
navigate_workspace_up = "i"
|
||||||
|
workspace_picker = ["prefix+w", "ctrl+alt+o"]
|
||||||
|
goto = ["prefix+g", "ctrl+alt+g"]
|
||||||
|
previous_agent = "ctrl+alt+p"
|
||||||
|
next_agent = "ctrl+alt+n"
|
||||||
|
previous_tab = ["prefix+p", "ctrl+alt+["]
|
||||||
|
next_tab = ["prefix+n", "ctrl+alt+]"]
|
||||||
|
new_tab = ["prefix+c", "ctrl+alt+enter"]
|
||||||
|
split_vertical = ["prefix+v", "ctrl+alt+v"]
|
||||||
|
split_horizontal = ["prefix+minus", "ctrl+alt+minus"]
|
||||||
|
zoom = ["prefix+z", "ctrl+alt+f"]
|
||||||
|
detach = ["prefix+q", "ctrl+alt+q"]
|
||||||
|
|
||||||
|
swap_pane_left = ["prefix+shift+h", "ctrl+alt+shift+h"]
|
||||||
|
swap_pane_down = ["prefix+shift+j", "ctrl+alt+shift+j"]
|
||||||
|
swap_pane_up = ["prefix+shift+k", "ctrl+alt+shift+k"]
|
||||||
|
swap_pane_right = ["prefix+shift+l", "ctrl+alt+shift+l"]
|
||||||
|
resize_pane_left = "ctrl+alt+shift+left"
|
||||||
|
resize_pane_down = "ctrl+alt+shift+down"
|
||||||
|
resize_pane_up = "ctrl+alt+shift+up"
|
||||||
|
resize_pane_right = "ctrl+alt+shift+right"
|
||||||
|
resize_mode = ["prefix+r", "ctrl+alt+r"]
|
||||||
|
cycle_pane_next = ["prefix+tab", "ctrl+alt+tab"]
|
||||||
|
cycle_pane_previous = ["prefix+shift+tab", "ctrl+alt+shift+tab"]
|
||||||
|
last_pane = "ctrl+alt+backspace"
|
||||||
|
close_pane = ["prefix+x", "ctrl+alt+x"]
|
||||||
|
rename_pane = ["prefix+shift+p", "ctrl+alt+shift+p"]
|
||||||
|
copy_mode = ["prefix+[", "ctrl+alt+y"]
|
||||||
|
edit_scrollback = ["prefix+e", "ctrl+alt+e"]
|
||||||
|
|
||||||
|
close_tab = ["prefix+shift+x", "ctrl+alt+shift+x"]
|
||||||
|
rename_tab = ["prefix+shift+t", "ctrl+alt+shift+a"]
|
||||||
|
move_tab_previous = "ctrl+alt+shift+["
|
||||||
|
move_tab_next = "ctrl+alt+shift+]"
|
||||||
|
new_workspace = ["prefix+shift+n", "ctrl+alt+shift+enter"]
|
||||||
|
rename_workspace = ["prefix+shift+w", "ctrl+alt+shift+w"]
|
||||||
|
close_workspace = ["prefix+shift+d", "ctrl+alt+shift+d"]
|
||||||
|
open_worktree = "ctrl+alt+w"
|
||||||
|
new_worktree = ["prefix+shift+g", "ctrl+alt+shift+b"]
|
||||||
|
|
||||||
|
toggle_sidebar = ["prefix+b", "ctrl+alt+b"]
|
||||||
|
help = ["prefix+?", "ctrl+alt+slash"]
|
||||||
|
settings = ["prefix+s", "ctrl+alt+s"]
|
||||||
|
reload_config = ["prefix+shift+r", "ctrl+alt+shift+r"]
|
||||||
|
|
||||||
|
[[keys.command]]
|
||||||
|
key = "ctrl+alt+shift+g"
|
||||||
|
type = "popup"
|
||||||
|
command = "lazygit"
|
||||||
|
description = "Lazygit"
|
||||||
|
width = "90%"
|
||||||
|
height = "90%"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Codex owns its writable user config; Nix owns assets and system defaults.
|
||||||
|
home.file =
|
||||||
|
lib.mapAttrs' (
|
||||||
|
name: source: lib.nameValuePair ".agents/skills/${name}" { inherit source; }
|
||||||
|
) ai.skills
|
||||||
|
// {
|
||||||
|
".codex/AGENTS.md".text = builtins.readFile ./instructions/codex.md;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Environment
|
||||||
|
|
||||||
|
- Use `nix run` for missing commands and `--quiet` for Nix commands.
|
||||||
|
- Capture large Nix listings in `/tmp`; read targeted excerpts and failure logs. Locate store files before reading them.
|
||||||
|
- Manage persistent binaries, skills, plugins and MCP definitions in `/home/kiri/.config/nixos`. Keep interactive settings, credentials and session state outside Nix.
|
||||||
|
|
||||||
|
# Work
|
||||||
|
|
||||||
|
- Complete authorized work without repeated confirmation. Make reasonable assumptions for routine choices; ask about consequential unknowns while continuing independent work.
|
||||||
|
- Prefer existing capabilities and simple solutions. Add abstractions or compatibility code only for a concrete need; explain required manual steps.
|
||||||
|
- Verify unfamiliar or consequential APIs against version-appropriate primary documentation; prefer Context7.
|
||||||
|
- Never push without my explicit approval. Other remote actions must remain within the authorized task.
|
||||||
|
- User instructions take precedence over skill guidelines, including testing defaults.
|
||||||
|
|
||||||
|
# Validation
|
||||||
|
|
||||||
|
- Do not write or run tests or perform extensive validation unless explicitly requested.
|
||||||
|
- For routine changes, use a focused review or small manual check. Report only checks performed.
|
||||||
|
|
||||||
|
# Writing
|
||||||
|
|
||||||
|
- Lead with the result. Write concisely in plain language, preserving important reasoning, evidence and limitations.
|
||||||
|
- Omit repetition and stock phrases. Use lists when they improve readability.
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
name: context7-mcp
|
||||||
|
description: This skill should be used when the user asks about libraries, frameworks, API references, or needs code examples. Activates for setup questions, code generation involving libraries, or mentions of specific frameworks like React, Vue, Next.js, Prisma, Supabase, etc.
|
||||||
|
---
|
||||||
|
|
||||||
|
When the user asks about libraries, frameworks, or needs code examples, use Context7 to fetch current documentation instead of relying on training data.
|
||||||
|
|
||||||
|
## When to Use This Skill
|
||||||
|
|
||||||
|
Activate this skill when the user:
|
||||||
|
|
||||||
|
- Asks setup or configuration questions ("How do I configure Next.js middleware?")
|
||||||
|
- Requests code involving libraries ("Write a Prisma query for...")
|
||||||
|
- Needs API references ("What are the Supabase auth methods?")
|
||||||
|
- Mentions specific frameworks (React, Vue, Svelte, Express, Tailwind, etc.)
|
||||||
|
|
||||||
|
## How to Fetch Documentation
|
||||||
|
|
||||||
|
### Step 1: Resolve the Library ID
|
||||||
|
|
||||||
|
Call `resolve-library-id` with:
|
||||||
|
|
||||||
|
- `libraryName`: The library name extracted from the user's question
|
||||||
|
- `query`: The user's full question (improves relevance ranking)
|
||||||
|
|
||||||
|
### Step 2: Select the Best Match
|
||||||
|
|
||||||
|
From the resolution results, choose based on:
|
||||||
|
|
||||||
|
- Exact or closest name match to what the user asked for
|
||||||
|
- Higher benchmark scores indicate better documentation quality
|
||||||
|
- If the user mentioned a version (e.g., "React 19"), prefer version-specific IDs
|
||||||
|
|
||||||
|
### Step 3: Fetch the Documentation
|
||||||
|
|
||||||
|
Call `query-docs` with:
|
||||||
|
|
||||||
|
- `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`)
|
||||||
|
- `query`: The user's specific question, scoped to a single concept
|
||||||
|
|
||||||
|
If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic.
|
||||||
|
|
||||||
|
### Step 4: Use the Documentation
|
||||||
|
|
||||||
|
Incorporate the fetched documentation into your response:
|
||||||
|
|
||||||
|
- Answer the user's question using current, accurate information
|
||||||
|
- Include relevant code examples from the docs
|
||||||
|
- Cite the library version when relevant
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept
|
||||||
|
- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact
|
||||||
|
- **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step
|
||||||
|
- **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks
|
||||||
@@ -6,7 +6,15 @@
|
|||||||
alsa.enable = true;
|
alsa.enable = true;
|
||||||
pulse.enable = true;
|
pulse.enable = true;
|
||||||
jack.enable = true;
|
jack.enable = true;
|
||||||
wireplumber.enable = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.audio =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
home.packages = [
|
||||||
|
pkgs.reaper
|
||||||
|
pkgs.bitwig-studio
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
File renamed without changes.
@@ -1,4 +1,3 @@
|
|||||||
{ ... }:
|
|
||||||
{
|
{
|
||||||
flake.modules.homeManager.desktop-commands =
|
flake.modules.homeManager.desktop-commands =
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
@@ -36,16 +35,6 @@
|
|||||||
xdg.mimeApps.defaultApplicationPackages = [ pkgs.vivaldi ];
|
xdg.mimeApps.defaultApplicationPackages = [ pkgs.vivaldi ];
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.nautilus =
|
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
|
||||||
home.packages = [ pkgs.nautilus ];
|
|
||||||
gtk.gtk3.bookmarks = [
|
|
||||||
"sftp://${config.facts.topology.server.sshAlias} Orion VPS"
|
|
||||||
];
|
|
||||||
xdg.mimeApps.defaultApplicationPackages = [ pkgs.nautilus ];
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.imv =
|
flake.modules.homeManager.imv =
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
@@ -68,60 +57,17 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.workstation-apps =
|
flake.modules.homeManager.workstation-apps =
|
||||||
{ pkgs, ... }:
|
{ lib, pkgs, ... }:
|
||||||
{
|
{
|
||||||
home.file.".harlequin.toml".text = ''
|
|
||||||
[profiles.taop]
|
|
||||||
adapter = "postgres"
|
|
||||||
conn_str = "postgresql://taop:taop@127.0.0.1:5433/taop"
|
|
||||||
|
|
||||||
[profiles.schemas]
|
|
||||||
adapter = "postgres"
|
|
||||||
conn_str = "postgresql://mathesar:mathesar@127.0.0.1:55432/traceability"
|
|
||||||
'';
|
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
postman
|
|
||||||
spotify
|
spotify
|
||||||
rainfrog
|
|
||||||
dblab
|
|
||||||
harlequin
|
|
||||||
tabularis
|
|
||||||
dbgate
|
|
||||||
pgadmin4-desktopmode
|
|
||||||
tbls
|
|
||||||
(pgmodeler.overrideAttrs (old: {
|
|
||||||
version = "2.0.0-beta";
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "nullptrlabs";
|
|
||||||
repo = "pgmodeler";
|
|
||||||
rev = "v2.0.0-beta";
|
|
||||||
hash = "sha256-NH7iRc484HskeSa8fi3nYThLq6kul3MItaiEezA4pd8=";
|
|
||||||
};
|
|
||||||
nativeBuildInputs = builtins.filter (input: input != qt6.qmake) old.nativeBuildInputs ++ [ cmake ];
|
|
||||||
buildInputs = old.buildInputs ++ [ libxml2 ];
|
|
||||||
qmakeFlags = [ ];
|
|
||||||
cmakeFlags = [ "-DPGM_PREFIX=${placeholder "out"}" ];
|
|
||||||
postPatch = ''
|
|
||||||
patchShebangs getbuildnum.sh
|
|
||||||
substituteInPlace BuildConfs.cmake \
|
|
||||||
--replace-fail /usr/bin/clang++ "$CXX" \
|
|
||||||
--replace-fail /usr/bin/clang "$CC"
|
|
||||||
'';
|
|
||||||
postInstall = ''
|
|
||||||
install -Dm444 "$src/assets/conf/pgmodeler_logo.png" \
|
|
||||||
"$out/share/icons/hicolor/256x256/apps/pgmodeler.png"
|
|
||||||
'';
|
|
||||||
}))
|
|
||||||
calcure
|
|
||||||
planify
|
|
||||||
unzip
|
unzip
|
||||||
gimp
|
|
||||||
dbeaver-bin
|
dbeaver-bin
|
||||||
beekeeper-studio
|
|
||||||
onlyoffice-desktopeditors
|
onlyoffice-desktopeditors
|
||||||
obsidian
|
pinta
|
||||||
|
audacity
|
||||||
];
|
];
|
||||||
|
xdg.mimeApps.defaultApplicationPackages = lib.mkAfter [ pkgs.onlyoffice-desktopeditors ];
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.xdg =
|
flake.modules.homeManager.xdg =
|
||||||
@@ -138,9 +84,6 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
cacheHome = "${localDir}/cache";
|
cacheHome = "${localDir}/cache";
|
||||||
configHome = "${homeDir}/.config";
|
|
||||||
dataHome = "${localDir}/share";
|
|
||||||
stateHome = "${localDir}/state";
|
|
||||||
|
|
||||||
userDirs = {
|
userDirs = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -149,7 +92,7 @@
|
|||||||
|
|
||||||
download = "${homeDir}/downloads";
|
download = "${homeDir}/downloads";
|
||||||
documents = "${homeDir}/documents";
|
documents = "${homeDir}/documents";
|
||||||
projects = "${homeDir}/dev";
|
projects = "${homeDir}/projects";
|
||||||
music = "${mediaDir}/music";
|
music = "${mediaDir}/music";
|
||||||
pictures = "${mediaDir}/images";
|
pictures = "${mediaDir}/images";
|
||||||
videos = "${mediaDir}/videos";
|
videos = "${mediaDir}/videos";
|
||||||
|
|||||||
@@ -1,49 +1,32 @@
|
|||||||
{ inputs, ... }:
|
{ inputs, ... }:
|
||||||
{
|
{
|
||||||
|
perSystem = { pkgs, ... }: {
|
||||||
|
packages.devenv = inputs.devenv.packages.${pkgs.stdenv.hostPlatform.system}.devenv;
|
||||||
|
};
|
||||||
flake.modules.homeManager.dev-tools =
|
flake.modules.homeManager.dev-tools =
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
let
|
|
||||||
stablePkgs = import inputs.nixpkgs-stable {
|
|
||||||
inherit (pkgs.stdenv.hostPlatform) system;
|
|
||||||
config = {
|
|
||||||
inherit (pkgs.config) allowUnfree;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo";
|
home.sessionVariables.CARGO_HOME = "${config.xdg.dataHome}/cargo";
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
httpie
|
inputs.devenv.packages.${pkgs.stdenv.hostPlatform.system}.devenv
|
||||||
bruno
|
bruno
|
||||||
usql
|
|
||||||
posting
|
|
||||||
resterm
|
|
||||||
kubectl
|
kubectl
|
||||||
mongodb-compass
|
kubelogin-oidc
|
||||||
|
rclone
|
||||||
scc
|
scc
|
||||||
nodejs
|
visidata
|
||||||
mariadb
|
just
|
||||||
];
|
];
|
||||||
|
|
||||||
programs.lazygit = {
|
programs.lazygit.enable = true;
|
||||||
enable = true;
|
|
||||||
enableZshIntegration = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.jujutsu.enable = true;
|
programs.jujutsu.enable = true;
|
||||||
programs.jq.enable = true;
|
programs.jq.enable = true;
|
||||||
programs.bun.enable = true;
|
|
||||||
programs.ripgrep.enable = true;
|
programs.ripgrep.enable = true;
|
||||||
programs.k9s.enable = true;
|
programs.k9s.enable = true;
|
||||||
|
|
||||||
programs.direnv = {
|
programs.zsh.initContent = ''
|
||||||
enable = true;
|
eval "$(devenv hook zsh)"
|
||||||
nix-direnv.enable = true;
|
'';
|
||||||
|
|
||||||
config.global = {
|
|
||||||
hide_env_diff = true;
|
|
||||||
log_format = "↳ %s";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.file-management = {
|
||||||
|
services.gvfs.enable = true;
|
||||||
|
services.udisks2.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.file-management =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
home.packages = [ pkgs.nautilus ];
|
||||||
|
gtk.gtk3.bookmarks = [
|
||||||
|
"sftp://${config.facts.topology.server.sshAlias} Orion VPS"
|
||||||
|
];
|
||||||
|
xdg.mimeApps.defaultApplicationPackages = [ pkgs.nautilus ];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
{
|
{
|
||||||
flake.modules.nixos.flatpak = {
|
flake.modules.nixos.flatpak.services.flatpak.enable = true;
|
||||||
services.flatpak.enable = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -28,21 +28,9 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
fontconfig = {
|
fontconfig = {
|
||||||
enable = true;
|
|
||||||
antialias = true;
|
|
||||||
allowBitmaps = false;
|
allowBitmaps = false;
|
||||||
useEmbeddedBitmaps = false;
|
useEmbeddedBitmaps = false;
|
||||||
|
subpixel.lcdfilter = "none";
|
||||||
hinting = {
|
|
||||||
enable = true;
|
|
||||||
autohint = false;
|
|
||||||
style = "slight";
|
|
||||||
};
|
|
||||||
|
|
||||||
subpixel = {
|
|
||||||
rgba = "none";
|
|
||||||
lcdfilter = "none";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +1,11 @@
|
|||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
account = config.facts.account;
|
account = config.facts.account;
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.homeManager.git =
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
machine = config.facts.machine;
|
|
||||||
allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
|
|
||||||
|
|
||||||
mkScope =
|
mkScope =
|
||||||
scope:
|
config: scope:
|
||||||
let
|
let
|
||||||
email = account.emails.${scope}.address;
|
email = account.emails.${scope}.address;
|
||||||
key = machine.sshKeys.${scope} or null;
|
key = config.facts.machine.sshKeys.${scope} or null;
|
||||||
hasSigningKey = key != null;
|
hasSigningKey = key != null;
|
||||||
privateKeyPath =
|
privateKeyPath =
|
||||||
if hasSigningKey && key.privateKeyPath != null then key.privateKeyPath else "~/.ssh/id_${scope}";
|
if hasSigningKey && key.privateKeyPath != null then key.privateKeyPath else "~/.ssh/id_${scope}";
|
||||||
@@ -34,17 +23,18 @@ in
|
|||||||
}
|
}
|
||||||
// lib.optionalAttrs hasSigningKey {
|
// lib.optionalAttrs hasSigningKey {
|
||||||
core.sshCommand = "ssh -i ${privateKeyPath} -o IdentitiesOnly=yes";
|
core.sshCommand = "ssh -i ${privateKeyPath} -o IdentitiesOnly=yes";
|
||||||
gpg.ssh.allowedSignersFile = allowedSignersFile;
|
gpg.ssh.allowedSignersFile = "${config.xdg.configHome}/git/allowed_signers";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
personal = mkScope "personal";
|
|
||||||
work = mkScope "work";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" (
|
flake.modules.homeManager.git =
|
||||||
personal.allowedSigners ++ work.allowedSigners ++ [ "" ]
|
{ config, ... }:
|
||||||
);
|
let
|
||||||
|
personal = mkScope config "personal";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
xdg.configFile."git/allowed_signers".text = lib.concatStringsSep "\n" personal.allowedSigners;
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -60,19 +50,17 @@ in
|
|||||||
email = account.emails.personal.address;
|
email = account.emails.personal.address;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
includes = [
|
includes =
|
||||||
{
|
map
|
||||||
condition = "gitdir:${account.nixosConfigurationPath}/";
|
(path: {
|
||||||
|
condition = "gitdir:${path}/";
|
||||||
contents = personal.git;
|
contents = personal.git;
|
||||||
}
|
})
|
||||||
{
|
[
|
||||||
condition = "gitdir:${config.xdg.userDirs.projects}/";
|
account.nixosConfigurationPath
|
||||||
contents = personal.git;
|
"${config.xdg.userDirs.projects}/own"
|
||||||
}
|
"${config.xdg.userDirs.projects}/ext"
|
||||||
{
|
"${config.xdg.userDirs.projects}/forks"
|
||||||
condition = "gitdir:${config.home.homeDirectory}/work/";
|
|
||||||
contents = work.git;
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,4 +69,33 @@ in
|
|||||||
settings.git_protocol = "ssh";
|
settings.git_protocol = "ssh";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.work-git =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
work = mkScope config "work";
|
||||||
|
githubTokenFile = "${config.xdg.configHome}/yookr/github-read-only.token";
|
||||||
|
gh = pkgs.symlinkJoin {
|
||||||
|
name = "gh-with-read-only-token-${pkgs.gh.version}";
|
||||||
|
paths = [ pkgs.gh ];
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
meta.mainProgram = "gh";
|
||||||
|
postBuild = ''
|
||||||
|
wrapProgram "$out/bin/gh" \
|
||||||
|
--run 'if [[ -z "''${GH_TOKEN:-}" && -r "${githubTokenFile}" ]]; then export GH_TOKEN="$(< "${githubTokenFile}")"; fi'
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
xdg.configFile."git/allowed_signers".text = lib.mkAfter (
|
||||||
|
lib.concatStringsSep "\n" work.allowedSigners
|
||||||
|
);
|
||||||
|
programs.git.includes = [
|
||||||
|
{
|
||||||
|
condition = "gitdir:${config.home.homeDirectory}/yookr/";
|
||||||
|
contents = work.git;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
programs.gh.package = gh;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -15,13 +15,9 @@ in
|
|||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
server = {
|
server = {
|
||||||
DOMAIN = service.domain;
|
|
||||||
ROOT_URL = "${service.origin}/";
|
ROOT_URL = "${service.origin}/";
|
||||||
HTTP_PORT = 3001;
|
HTTP_PORT = 3001;
|
||||||
HTTP_ADDR = "127.0.0.1";
|
HTTP_ADDR = "127.0.0.1";
|
||||||
|
|
||||||
START_SSH_SERVER = false;
|
|
||||||
SSH_PORT = 22;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
service.DISABLE_REGISTRATION = true;
|
service.DISABLE_REGISTRATION = true;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.handy = { pkgs, ... }: {
|
||||||
|
services.udev.packages = [
|
||||||
|
(pkgs.writeTextDir "lib/udev/rules.d/70-handy-keyboard.rules" ''
|
||||||
|
SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_INPUT_KEYBOARD}=="1", TAG+="uaccess"
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.handy =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
python = pkgs.python3.withPackages (ps: [ ps.evdev ]);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
home.packages = [ pkgs.handy ];
|
||||||
|
|
||||||
|
systemd.user.services.handy = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Handy dictation";
|
||||||
|
PartOf = [ "graphical-session.target" ];
|
||||||
|
After = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "graphical-session.target" ];
|
||||||
|
Service = {
|
||||||
|
ExecStart = "${pkgs.handy}/bin/handy --start-hidden";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.services.handy-ptt = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Handy push-to-talk key release listener";
|
||||||
|
PartOf = [ "graphical-session.target" ];
|
||||||
|
Requisite = [ "handy.service" ];
|
||||||
|
After = [ "handy.service" ];
|
||||||
|
};
|
||||||
|
Service.ExecStart = "${python}/bin/python ${./push_to_talk.py} ${pkgs.handy}/bin/handy";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
"""Wait for release of the physical Super+D chord that launched this service."""
|
||||||
|
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
from evdev import InputDevice, ecodes as key, list_devices
|
||||||
|
|
||||||
|
|
||||||
|
def held(keys):
|
||||||
|
return key.KEY_D in keys and bool({key.KEY_LEFTMETA, key.KEY_RIGHTMETA} & set(keys))
|
||||||
|
|
||||||
|
|
||||||
|
def interrupted(signum, frame):
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
|
|
||||||
|
def main(handy):
|
||||||
|
signal.signal(signal.SIGTERM, interrupted)
|
||||||
|
for path in list_devices():
|
||||||
|
try:
|
||||||
|
device = InputDevice(path)
|
||||||
|
except OSError:
|
||||||
|
continue # Devices can disappear between enumeration and opening.
|
||||||
|
with closing(device):
|
||||||
|
if not held(device.active_keys()):
|
||||||
|
continue
|
||||||
|
action = "--cancel"
|
||||||
|
try:
|
||||||
|
subprocess.run([handy, "--toggle-transcription"], check=True)
|
||||||
|
# A quick release may already be queued while the CLI starts.
|
||||||
|
if held(device.active_keys()):
|
||||||
|
for event in device.read_loop():
|
||||||
|
if not held(device.active_keys()):
|
||||||
|
break
|
||||||
|
action = "--toggle-transcription"
|
||||||
|
finally:
|
||||||
|
# Disconnects, cancellation and service shutdown discard the recording.
|
||||||
|
subprocess.run([handy, action], check=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main(sys.argv[1])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
{ config, inputs, ... }:
|
|
||||||
let
|
|
||||||
homeModules = config.flake.modules.homeManager;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.helix-cache = {
|
|
||||||
nix.settings = {
|
|
||||||
extra-substituters = [ "https://helix.cachix.org" ];
|
|
||||||
extra-trusted-public-keys = [
|
|
||||||
"helix.cachix.org-1:ejp9KQpR1FBI2onstMQ34yogDm4OgU2ru6lIwPvuCVs="
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.helix =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
svelteLanguageServer = (import ./_svelte-language-server.nix { inherit pkgs; }).overrideAttrs (
|
|
||||||
_: previousAttrs: {
|
|
||||||
postInstall = (previousAttrs.postInstall or "") + ''
|
|
||||||
# @vscode/emmet-helper only enables JSX expansion for JSX syntax;
|
|
||||||
# Helix's Svelte HTML regions also need it for HTML syntax.
|
|
||||||
substituteInPlace \
|
|
||||||
"$out/lib/node_modules/svelte-language-server/packages/language-server/node_modules/@vscode/emmet-helper/lib/cjs/emmetHelper.js" \
|
|
||||||
--replace-fail \
|
|
||||||
"'jsx.enabled': syntax === 'jsx'," \
|
|
||||||
"'jsx.enabled': syntax === 'jsx' || syntax === 'html',"
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
programs.helix = {
|
|
||||||
enable = true;
|
|
||||||
package = inputs.helix.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
|
||||||
extraPackages = with pkgs; [
|
|
||||||
vtsls
|
|
||||||
svelteLanguageServer
|
|
||||||
tailwindcss-language-server
|
|
||||||
|
|
||||||
nixd
|
|
||||||
nixfmt
|
|
||||||
oxfmt
|
|
||||||
|
|
||||||
simple-completion-language-server
|
|
||||||
|
|
||||||
odin
|
|
||||||
ols
|
|
||||||
|
|
||||||
rust-analyzer
|
|
||||||
rustfmt
|
|
||||||
cargo
|
|
||||||
|
|
||||||
zig
|
|
||||||
zls
|
|
||||||
lldb
|
|
||||||
];
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
theme = "kanagawa-custom";
|
|
||||||
editor = {
|
|
||||||
soft-wrap.enable = true;
|
|
||||||
lsp = {
|
|
||||||
auto-document-highlight = true;
|
|
||||||
display-inlay-hints = false;
|
|
||||||
};
|
|
||||||
rainbow-brackets = true;
|
|
||||||
line-number = "relative";
|
|
||||||
cursorline = true;
|
|
||||||
gutters = [
|
|
||||||
"code-action-hint"
|
|
||||||
"diagnostics"
|
|
||||||
"spacer"
|
|
||||||
"line-numbers"
|
|
||||||
"spacer"
|
|
||||||
"diff"
|
|
||||||
];
|
|
||||||
color-modes = true;
|
|
||||||
cursor-shape = {
|
|
||||||
insert = "bar";
|
|
||||||
normal = "block";
|
|
||||||
select = "underline";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
themes.kanagawa-custom = {
|
|
||||||
inherits = "kanagawa";
|
|
||||||
"ui.highlight".underline = {
|
|
||||||
color = "dragonBlue";
|
|
||||||
style = "line";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
languages = {
|
|
||||||
language-server.scls = {
|
|
||||||
command = "simple-completion-language-server";
|
|
||||||
environment.SNIPPETS_PATH = "${pkgs.vimPlugins.friendly-snippets}/snippets";
|
|
||||||
};
|
|
||||||
|
|
||||||
language-server.vtsls.config.vtsls.autoUseWorkspaceTsdk = true;
|
|
||||||
|
|
||||||
language = [
|
|
||||||
{
|
|
||||||
name = "javascript";
|
|
||||||
language-servers = [
|
|
||||||
"vtsls"
|
|
||||||
"scls"
|
|
||||||
];
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "jsx";
|
|
||||||
language-servers = [
|
|
||||||
"vtsls"
|
|
||||||
"scls"
|
|
||||||
];
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "typescript";
|
|
||||||
language-servers = [
|
|
||||||
"vtsls"
|
|
||||||
"scls"
|
|
||||||
];
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "tsx";
|
|
||||||
language-servers = [
|
|
||||||
"vtsls"
|
|
||||||
"scls"
|
|
||||||
];
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "svelte";
|
|
||||||
formatter = {
|
|
||||||
command = "${pkgs.coreutils}/bin/env";
|
|
||||||
args = [
|
|
||||||
"NODE_PATH=%{workspace_directory}/node_modules"
|
|
||||||
"oxfmt"
|
|
||||||
"--stdin-filepath=%{buffer_name}"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
auto-format = true;
|
|
||||||
language-servers = [
|
|
||||||
"scls"
|
|
||||||
"svelteserver"
|
|
||||||
"tailwindcss-ls"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "nix";
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "rust";
|
|
||||||
auto-format = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.primary-editor-helix = {
|
|
||||||
imports = [ homeModules.helix ];
|
|
||||||
programs.helix.defaultEditor = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
{
|
{
|
||||||
flake.modules.homeManager.keyring = {
|
flake.modules.homeManager.keyring.services.gnome-keyring.enable = true;
|
||||||
services.gnome-keyring.enable = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
{ config, lib, ... }:
|
{ ... }:
|
||||||
let
|
|
||||||
palette = (import ./_kanagawa.nix).palette;
|
|
||||||
hex = lib.removePrefix "#";
|
|
||||||
terminalPalette = palette.terminal;
|
|
||||||
mkPalette = colors: lib.concatStringsSep ";" (map hex colors);
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
flake.modules.nixos.limine =
|
flake.modules.nixos.limine =
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
displayValues = builtins.attrValues config.facts.machine.displays;
|
primaryDisplay = lib.findFirst (display: display.primary) null (
|
||||||
primaryDisplays = lib.filter (display: display.primary) displayValues;
|
builtins.attrValues config.facts.machine.displays
|
||||||
primaryDisplay = if primaryDisplays == [ ] then null else builtins.head primaryDisplays;
|
);
|
||||||
interfaceResolution =
|
interfaceResolution =
|
||||||
if primaryDisplay != null && primaryDisplay.width != null then
|
if primaryDisplay != null && primaryDisplay.width != null then
|
||||||
"${toString primaryDisplay.width}x${toString primaryDisplay.height}"
|
"${toString primaryDisplay.width}x${toString primaryDisplay.height}"
|
||||||
@@ -24,53 +18,9 @@ in
|
|||||||
limine = {
|
limine = {
|
||||||
enable = true;
|
enable = true;
|
||||||
maxGenerations = 10;
|
maxGenerations = 10;
|
||||||
|
|
||||||
style = {
|
|
||||||
backdrop = hex palette.secondaryBackground;
|
|
||||||
|
|
||||||
graphicalTerminal = {
|
|
||||||
background = "00${hex palette.background}";
|
|
||||||
foreground = hex palette.foreground;
|
|
||||||
brightForeground = hex palette.selectionForeground;
|
|
||||||
brightBackground = hex palette.selectionBackground;
|
|
||||||
palette = mkPalette [
|
|
||||||
terminalPalette.color0
|
|
||||||
terminalPalette.color1
|
|
||||||
terminalPalette.color2
|
|
||||||
terminalPalette.color3
|
|
||||||
terminalPalette.color4
|
|
||||||
terminalPalette.color5
|
|
||||||
terminalPalette.color6
|
|
||||||
terminalPalette.color7
|
|
||||||
];
|
|
||||||
brightPalette = mkPalette [
|
|
||||||
terminalPalette.color8
|
|
||||||
terminalPalette.color9
|
|
||||||
terminalPalette.color10
|
|
||||||
terminalPalette.color11
|
|
||||||
terminalPalette.color12
|
|
||||||
terminalPalette.color13
|
|
||||||
terminalPalette.color14
|
|
||||||
terminalPalette.color15
|
|
||||||
];
|
|
||||||
font = {
|
|
||||||
scale = "2x2";
|
|
||||||
spacing = 1;
|
|
||||||
};
|
|
||||||
margin = 64;
|
|
||||||
marginGradient = 24;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface = {
|
|
||||||
branding = config.networking.hostName;
|
|
||||||
brandingColor = hex palette.accents.blue;
|
|
||||||
helpHidden = false;
|
|
||||||
resolution = interfaceResolution;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// lib.optionalAttrs (interfaceResolution != null) {
|
|
||||||
resolution = interfaceResolution;
|
resolution = interfaceResolution;
|
||||||
|
|
||||||
|
style.interface.resolution = interfaceResolution;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.localsend = {
|
||||||
|
programs.localsend.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,9 +4,7 @@
|
|||||||
{
|
{
|
||||||
programs.mpv = {
|
programs.mpv = {
|
||||||
enable = true;
|
enable = true;
|
||||||
bindings = {
|
bindings.D = "cycle deband";
|
||||||
D = "cycle deband";
|
|
||||||
};
|
|
||||||
config = {
|
config = {
|
||||||
profile = "high-quality";
|
profile = "high-quality";
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
{ themeName }:
|
|
||||||
''
|
|
||||||
require("kanagawa").setup({
|
|
||||||
dimInactive = true,
|
|
||||||
colors = {
|
|
||||||
theme = {
|
|
||||||
all = {
|
|
||||||
ui = {
|
|
||||||
bg_gutter = "none"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
overrides = function(colors)
|
|
||||||
local theme = colors.theme
|
|
||||||
|
|
||||||
local makeDiagnosticColor = function(color)
|
|
||||||
local c = require("kanagawa.lib.color")
|
|
||||||
return { fg = color, bg = c(color):blend(theme.ui.bg, 0.95):to_hex() }
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
TelescopeTitle = { fg = theme.ui.special, bold = true },
|
|
||||||
TelescopePromptNormal = { bg = theme.ui.bg_p1 },
|
|
||||||
TelescopePromptBorder = { fg = theme.ui.bg_p1, bg = theme.ui.bg_p1 },
|
|
||||||
TelescopeResultsNormal = { fg = theme.ui.fg_dim, bg = theme.ui.bg_m1 },
|
|
||||||
TelescopeResultsBorder = { fg = theme.ui.bg_m1, bg = theme.ui.bg_m1 },
|
|
||||||
TelescopePreviewNormal = { bg = theme.ui.bg_dim },
|
|
||||||
TelescopePreviewBorder = { bg = theme.ui.bg_dim, fg = theme.ui.bg_dim },
|
|
||||||
|
|
||||||
Pmenu = { fg = theme.ui.shade0, bg = theme.ui.bg_p1 }, -- add `blend = vim.o.pumblend` to enable transparency
|
|
||||||
PmenuSel = { fg = "NONE", bg = theme.ui.bg_p2 },
|
|
||||||
PmenuSbar = { bg = theme.ui.bg_m1 },
|
|
||||||
PmenuThumb = { bg = theme.ui.bg_p2 },
|
|
||||||
|
|
||||||
DiagnosticVirtualTextHint = makeDiagnosticColor(theme.diag.hint),
|
|
||||||
DiagnosticVirtualTextInfo = makeDiagnosticColor(theme.diag.info),
|
|
||||||
DiagnosticVirtualTextWarn = makeDiagnosticColor(theme.diag.warning),
|
|
||||||
DiagnosticVirtualTextError = makeDiagnosticColor(theme.diag.error),
|
|
||||||
|
|
||||||
DiagnosticVirtualLinesHint = makeDiagnosticColor(theme.diag.hint),
|
|
||||||
DiagnosticVirtualLinesInfo = makeDiagnosticColor(theme.diag.info),
|
|
||||||
DiagnosticVirtualLinesWarn = makeDiagnosticColor(theme.diag.warning),
|
|
||||||
DiagnosticVirtualLinesError = makeDiagnosticColor(theme.diag.error),
|
|
||||||
|
|
||||||
FloatBorder = { fg = theme.ui.float.fg_border, bg = theme.ui.float.bg },
|
|
||||||
NormalFloat = { fg = theme.ui.float.fg, bg = theme.ui.float.bg },
|
|
||||||
WinBar = { fg = theme.ui.fg_dim, bg = "NONE" },
|
|
||||||
WinBarNC = { fg = theme.ui.nontext, bg = theme.ui.bg_dim },
|
|
||||||
}
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.cmd.colorscheme("${themeName}")
|
|
||||||
''
|
|
||||||
File renamed without changes.
@@ -1,7 +1,6 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
let
|
let
|
||||||
account = config.facts.account;
|
account = config.facts.account;
|
||||||
homeModules = config.flake.modules.homeManager;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.homeManager.neovim =
|
flake.modules.homeManager.neovim =
|
||||||
@@ -14,7 +13,7 @@ in
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
nvimTreesitter = pkgs.vimPlugins.nvim-treesitter;
|
nvimTreesitter = pkgs.vimPlugins.nvim-treesitter;
|
||||||
svelteLanguageServer = import ../_svelte-language-server.nix { inherit pkgs; };
|
svelteLanguageServer = import ./_support/svelte-language-server.nix { inherit pkgs; };
|
||||||
svelteGrammar = nvimTreesitter.builtGrammars.svelte.overrideAttrs (_: {
|
svelteGrammar = nvimTreesitter.builtGrammars.svelte.overrideAttrs (_: {
|
||||||
version = "0.0.0+rev=c2457b9";
|
version = "0.0.0+rev=c2457b9";
|
||||||
src = pkgs.fetchFromGitHub {
|
src = pkgs.fetchFromGitHub {
|
||||||
@@ -24,11 +23,33 @@ in
|
|||||||
hash = "sha256-RIAe30TTzZhHuXxC2VRVk/X73Qm1u2LWOW3AsnUfRzI=";
|
hash = "sha256-RIAe30TTzZhHuXxC2VRVk/X73Qm1u2LWOW3AsnUfRzI=";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
nvimTreesitterWithAllGrammars = nvimTreesitter.withPlugins (
|
nvimTreesitterWithGrammars = nvimTreesitter.withPlugins (
|
||||||
_:
|
grammars: with grammars; [
|
||||||
map (
|
astro
|
||||||
grammar: if grammar == nvimTreesitter.builtGrammars.svelte then svelteGrammar else grammar
|
css
|
||||||
) nvimTreesitter.allGrammars
|
html
|
||||||
|
http
|
||||||
|
javascript
|
||||||
|
jsdoc
|
||||||
|
json
|
||||||
|
lua
|
||||||
|
markdown
|
||||||
|
markdown_inline
|
||||||
|
nix
|
||||||
|
python
|
||||||
|
query
|
||||||
|
regex
|
||||||
|
rust
|
||||||
|
sql
|
||||||
|
svelteGrammar
|
||||||
|
toml
|
||||||
|
tsx
|
||||||
|
typescript
|
||||||
|
typst
|
||||||
|
vim
|
||||||
|
vimdoc
|
||||||
|
yaml
|
||||||
|
]
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -41,118 +62,146 @@ in
|
|||||||
|
|
||||||
wrappers.neovim = {
|
wrappers.neovim = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
settings.config_directory = ./lua-config;
|
settings.config_directory = ./lua-config;
|
||||||
|
|
||||||
runtimePkgs = with pkgs; [
|
runtimePkgs = with pkgs; [
|
||||||
ripgrep
|
ripgrep
|
||||||
fd
|
fd
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
|
|
||||||
stylua
|
stylua
|
||||||
|
nixfmt
|
||||||
|
oxfmt
|
||||||
taplo
|
taplo
|
||||||
yamlfmt
|
yamlfmt
|
||||||
lua-language-server
|
|
||||||
nixd
|
|
||||||
nixfmt
|
|
||||||
dafny
|
|
||||||
vscode-langservers-extracted
|
|
||||||
tailwindcss-language-server
|
|
||||||
rust-analyzer
|
|
||||||
rustfmt
|
rustfmt
|
||||||
astro-language-server
|
|
||||||
tinymist
|
|
||||||
typstyle
|
typstyle
|
||||||
websocat
|
|
||||||
ty
|
|
||||||
ruff
|
ruff
|
||||||
|
|
||||||
|
lua-language-server
|
||||||
|
nixd
|
||||||
|
vscode-langservers-extracted
|
||||||
|
yaml-language-server
|
||||||
vtsls
|
vtsls
|
||||||
oxlint
|
|
||||||
oxfmt
|
|
||||||
emmet-language-server
|
|
||||||
svelteLanguageServer
|
svelteLanguageServer
|
||||||
|
tailwindcss-language-server
|
||||||
|
emmet-language-server
|
||||||
|
oxlint
|
||||||
|
rust-analyzer
|
||||||
|
astro-language-server
|
||||||
|
tinymist
|
||||||
|
ty
|
||||||
|
|
||||||
|
postgresql
|
||||||
|
mariadb
|
||||||
|
mongosh
|
||||||
|
|
||||||
|
kulala-core
|
||||||
|
kulala-fmt
|
||||||
|
curl
|
||||||
|
jq
|
||||||
|
openssl
|
||||||
|
|
||||||
|
zk
|
||||||
];
|
];
|
||||||
|
|
||||||
specs = {
|
specs = {
|
||||||
lz-n = {
|
lz-n.data = pkgs.vimPlugins.lz-n;
|
||||||
data = pkgs.vimPlugins.lz-n;
|
|
||||||
};
|
|
||||||
|
|
||||||
support = {
|
editing = {
|
||||||
data = with pkgs.vimPlugins; [
|
|
||||||
plenary-nvim
|
|
||||||
kanagawa-nvim
|
|
||||||
nvimTreesitterWithAllGrammars
|
|
||||||
nvim-treesitter-textobjects
|
|
||||||
rainbow-delimiters-nvim
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
completion = {
|
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
blink-cmp
|
mini-nvim
|
||||||
luasnip
|
guess-indent-nvim
|
||||||
friendly-snippets
|
nvimTreesitterWithGrammars
|
||||||
colorful-menu-nvim
|
nvim-treesitter-textobjects
|
||||||
|
rainbow-delimiters-nvim
|
||||||
|
nvim-ts-autotag
|
||||||
|
grug-far-nvim
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
navigation = {
|
||||||
|
lazy = true;
|
||||||
|
data = with pkgs.vimPlugins; [
|
||||||
|
plenary-nvim
|
||||||
|
telescope-nvim
|
||||||
|
telescope-fzf-native-nvim
|
||||||
|
telescope-ui-select-nvim
|
||||||
|
{
|
||||||
|
data = oil-nvim;
|
||||||
|
lazy = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
diagnostics = {
|
||||||
|
lazy = true;
|
||||||
|
data = with pkgs.vimPlugins; [ trouble-nvim ];
|
||||||
|
};
|
||||||
|
|
||||||
lsp = {
|
lsp = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
nvim-lspconfig
|
nvim-lspconfig
|
||||||
lazydev-nvim
|
lazydev-nvim
|
||||||
nvim-navic
|
nvim-navic
|
||||||
trouble-nvim
|
SchemaStore-nvim
|
||||||
typst-preview-nvim
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
completion = {
|
||||||
|
lazy = true;
|
||||||
|
data = with pkgs.vimPlugins; [
|
||||||
|
blink-cmp
|
||||||
|
colorful-menu-nvim
|
||||||
|
friendly-snippets
|
||||||
|
{
|
||||||
|
data = blink-compat;
|
||||||
|
lazy = false;
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
formatting = {
|
formatting = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [ conform-nvim ];
|
||||||
conform-nvim
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
core = {
|
|
||||||
lazy = true;
|
|
||||||
data = with pkgs.vimPlugins; [
|
|
||||||
mini-nvim
|
|
||||||
guess-indent-nvim
|
|
||||||
direnv-vim
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ui = {
|
ui = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
|
render-markdown-nvim
|
||||||
lualine-nvim
|
lualine-nvim
|
||||||
gitsigns-nvim
|
gitsigns-nvim
|
||||||
|
diffview-nvim
|
||||||
zen-mode-nvim
|
zen-mode-nvim
|
||||||
which-key-nvim
|
which-key-nvim
|
||||||
kulala-nvim
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
project = {
|
database = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [
|
||||||
project-nvim
|
vim-dadbod
|
||||||
|
vim-dadbod-completion
|
||||||
|
vim-dadbod-ui
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
telescope = {
|
http = {
|
||||||
lazy = true;
|
lazy = true;
|
||||||
data = with pkgs.vimPlugins; [
|
data = with pkgs.vimPlugins; [ kulala-nvim ];
|
||||||
telescope-nvim
|
};
|
||||||
telescope-fzf-native-nvim
|
|
||||||
telescope-ui-select-nvim
|
notes = {
|
||||||
];
|
lazy = true;
|
||||||
|
data = with pkgs.vimPlugins; [ zk-nvim ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env.ZK_NOTEBOOK_DIR = "${config.home.homeDirectory}/sync/notes";
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
nixdExtras = {
|
nixdExtras = {
|
||||||
nixpkgs = "import ${pkgs.path} {}";
|
nixpkgs = "import ${pkgs.path} {}";
|
||||||
@@ -160,31 +209,21 @@ in
|
|||||||
home_manager_options = ''(builtins.getFlake "path://${account.nixosConfigurationPath}").nixosConfigurations.${osConfig.facts.machine.name}.options.home-manager.users.type.getSubOptions []'';
|
home_manager_options = ''(builtins.getFlake "path://${account.nixosConfigurationPath}").nixosConfigurations.${osConfig.facts.machine.name}.options.home-manager.users.type.getSubOptions []'';
|
||||||
};
|
};
|
||||||
|
|
||||||
themeSetup = import ./_kanagawa-theme.nix {
|
kulalaCorePath = "${pkgs.kulala-core}/bin/kulala-core";
|
||||||
themeName = "kanagawa-wave";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typstPreviewDependencies = {
|
|
||||||
tinymist = "${pkgs.tinymist}/bin/tinymist";
|
|
||||||
websocat = "${pkgs.websocat}/bin/websocat";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
hosts.python3.nvim-host.enable = true;
|
|
||||||
hosts.node.nvim-host.enable = true;
|
|
||||||
binName = "nvim";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.primary-editor-neovim =
|
flake.modules.homeManager.primary-editor-neovim =
|
||||||
{ pkgs, ... }:
|
{ pkgs, ... }:
|
||||||
{
|
{
|
||||||
imports = [ homeModules.neovim ];
|
imports = [ config.flake.modules.homeManager.neovim ];
|
||||||
|
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
EDITOR = "nvim";
|
EDITOR = "nvim";
|
||||||
VISUAL = "nvim";
|
VISUAL = "nvim";
|
||||||
};
|
};
|
||||||
xdg.mimeApps.defaultApplicationPackages = [ pkgs.neovim ];
|
xdg.mimeApps.defaultApplicationPackages = [ pkgs.neovim ];
|
||||||
|
xdg.mimeApps.defaultApplications."text/markdown" = "nvim.desktop";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
;; extends
|
||||||
|
|
||||||
|
(pair
|
||||||
|
key: [
|
||||||
|
(property_identifier) @_key
|
||||||
|
(string
|
||||||
|
(string_fragment) @_key)
|
||||||
|
]
|
||||||
|
value: (template_string) @injection.content
|
||||||
|
(#eq? @_key "sql")
|
||||||
|
(#offset! @injection.content 0 1 0 -1)
|
||||||
|
(#set! injection.include-children)
|
||||||
|
(#set! injection.language "sql"))
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
require("options")
|
require("options")
|
||||||
|
require("keymaps")
|
||||||
|
require("autocmds")
|
||||||
|
require("plugins.editing")
|
||||||
|
require("plugins.navigation")
|
||||||
|
require("plugins.diagnostics")
|
||||||
require("plugins.lsp")
|
require("plugins.lsp")
|
||||||
require("plugins.completion")
|
require("plugins.completion")
|
||||||
require("plugins.formatting")
|
require("plugins.formatting")
|
||||||
require("plugins.treesitter")
|
|
||||||
require("plugins.telescope")
|
|
||||||
require("plugins.ui")
|
require("plugins.ui")
|
||||||
require("plugins.core")
|
require("plugins.database")
|
||||||
|
require("plugins.http")
|
||||||
|
require("plugins.notes")
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
vim.api.nvim_create_autocmd({ "FocusGained", "BufEnter", "TermLeave" }, {
|
||||||
|
desc = "Reload externally changed files without discarding unsaved edits",
|
||||||
|
group = vim.api.nvim_create_augroup("lux-checktime", { clear = true }),
|
||||||
|
command = "checktime",
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("TextYankPost", {
|
||||||
|
desc = "Highlight yanked text",
|
||||||
|
group = vim.api.nvim_create_augroup("lux-highlight-yank", { clear = true }),
|
||||||
|
callback = function()
|
||||||
|
vim.hl.on_yank()
|
||||||
|
end,
|
||||||
|
})
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
vim.g.mapleader = " "
|
||||||
|
vim.g.maplocalleader = ","
|
||||||
|
|
||||||
|
for _, lhs in ipairs({ "grn", "gra", "grx", "grr", "gri", "grt", "gO" }) do
|
||||||
|
vim.keymap.del("n", lhs)
|
||||||
|
end
|
||||||
|
vim.keymap.del("x", "gra")
|
||||||
|
vim.keymap.del({ "i", "s" }, "<C-S>")
|
||||||
|
|
||||||
|
for _, key in ipairs({ "j", "k" }) do
|
||||||
|
vim.keymap.set({ "n", "x" }, key, function()
|
||||||
|
return vim.bo.buftype == "" and vim.v.count == 0 and "g" .. key or key
|
||||||
|
end, { expr = true, desc = "Move by displayed line, or logical line with a count" })
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.keymap.set("n", "<C-h>", "<C-w><C-h>", { desc = "Focus left window" })
|
||||||
|
vim.keymap.set("n", "<C-j>", "<C-w><C-j>", { desc = "Focus lower window" })
|
||||||
|
vim.keymap.set("n", "<C-k>", "<C-w><C-k>", { desc = "Focus upper window" })
|
||||||
|
vim.keymap.set("n", "<C-l>", "<C-w><C-l>", { desc = "Focus right window" })
|
||||||
|
vim.keymap.set("n", "gl", vim.diagnostic.open_float, { desc = "Show diagnostic details" })
|
||||||
|
vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Leave terminal mode" })
|
||||||
|
vim.keymap.set("n", "<leader>bd", "<cmd>bdelete<cr>", { desc = "Delete current buffer" })
|
||||||
|
vim.keymap.set("n", "[d", function()
|
||||||
|
vim.diagnostic.jump({ count = -1 })
|
||||||
|
end, { desc = "Previous diagnostic" })
|
||||||
|
vim.keymap.set("n", "]d", function()
|
||||||
|
vim.diagnostic.jump({ count = 1 })
|
||||||
|
end, { desc = "Next diagnostic" })
|
||||||
@@ -1,76 +1,32 @@
|
|||||||
vim.g.mapleader = " "
|
|
||||||
vim.g.maplocalleader = " "
|
|
||||||
|
|
||||||
vim.o.expandtab = true
|
vim.o.expandtab = true
|
||||||
vim.o.shiftwidth = 2
|
vim.o.shiftwidth = 2
|
||||||
vim.o.tabstop = 2
|
vim.o.tabstop = 2
|
||||||
vim.o.softtabstop = 2
|
vim.o.softtabstop = 2
|
||||||
|
|
||||||
vim.o.number = true
|
vim.o.number = true
|
||||||
-- vim.o.relativenumber = true
|
|
||||||
|
|
||||||
vim.o.mouse = "a"
|
|
||||||
|
|
||||||
vim.o.showmode = false
|
vim.o.showmode = false
|
||||||
vim.o.showtabline = 0
|
vim.o.showtabline = 0
|
||||||
|
|
||||||
vim.opt.shortmess:append("Wc")
|
|
||||||
|
|
||||||
vim.schedule(function()
|
|
||||||
vim.o.clipboard = "unnamedplus"
|
|
||||||
end)
|
|
||||||
|
|
||||||
vim.o.breakindent = true
|
|
||||||
|
|
||||||
vim.o.undofile = true
|
|
||||||
|
|
||||||
vim.o.ignorecase = true
|
|
||||||
vim.o.smartcase = true
|
|
||||||
|
|
||||||
vim.o.signcolumn = "yes"
|
vim.o.signcolumn = "yes"
|
||||||
|
vim.o.cursorline = true
|
||||||
|
vim.o.scrolloff = 10
|
||||||
|
|
||||||
|
vim.o.mouse = "a"
|
||||||
|
vim.o.clipboard = "unnamedplus"
|
||||||
|
vim.o.confirm = true
|
||||||
|
vim.o.timeoutlen = 300
|
||||||
vim.o.updatetime = 250
|
vim.o.updatetime = 250
|
||||||
|
|
||||||
vim.o.timeoutlen = 300
|
vim.o.breakindent = true
|
||||||
|
vim.o.linebreak = true
|
||||||
|
vim.o.undofile = true
|
||||||
|
vim.o.ignorecase = true
|
||||||
|
vim.o.smartcase = true
|
||||||
|
vim.o.inccommand = "split"
|
||||||
|
|
||||||
vim.o.splitright = true
|
vim.o.splitright = true
|
||||||
vim.o.splitbelow = true
|
vim.o.splitbelow = true
|
||||||
|
|
||||||
|
vim.opt.shortmess:append("Wc")
|
||||||
|
|
||||||
vim.o.list = true
|
vim.o.list = true
|
||||||
vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" }
|
vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" }
|
||||||
|
|
||||||
vim.o.inccommand = "split"
|
|
||||||
|
|
||||||
vim.o.cursorline = true
|
|
||||||
|
|
||||||
vim.o.scrolloff = 10
|
|
||||||
|
|
||||||
vim.o.confirm = true
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<Esc>", "<cmd>nohlsearch<CR>")
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>q", vim.diagnostic.setloclist, { desc = "Open diagnostic [Q]uickfix list" })
|
|
||||||
vim.keymap.set("n", "<leader>bd", "<cmd>bdelete<CR>", { desc = "[B]uffer [D]elete" })
|
|
||||||
|
|
||||||
vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<C-h>", "<C-w><C-h>", { desc = "Move focus to the left window" })
|
|
||||||
vim.keymap.set("n", "<C-l>", "<C-w><C-l>", { desc = "Move focus to the right window" })
|
|
||||||
vim.keymap.set("n", "<C-j>", "<C-w><C-j>", { desc = "Move focus to the lower window" })
|
|
||||||
vim.keymap.set("n", "<C-k>", "<C-w><C-k>", { desc = "Move focus to the upper window" })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "j", "gj", { silent = true })
|
|
||||||
vim.keymap.set("n", "k", "gk", { silent = true })
|
|
||||||
|
|
||||||
-- vim.keymap.set("n", "<C-S-h>", "<C-w>H", { desc = "Move window to the left" })
|
|
||||||
-- vim.keymap.set("n", "<C-S-l>", "<C-w>L", { desc = "Move window to the right" })
|
|
||||||
-- vim.keymap.set("n", "<C-S-j>", "<C-w>J", { desc = "Move window to the lower" })
|
|
||||||
-- vim.keymap.set("n", "<C-S-k>", "<C-w>K", { desc = "Move window to the upper" })
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("TextYankPost", {
|
|
||||||
desc = "Highlight when yanking (copying) text",
|
|
||||||
group = vim.api.nvim_create_augroup("lux-highlight-yank", { clear = true }),
|
|
||||||
callback = function()
|
|
||||||
vim.hl.on_yank()
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
@@ -1,19 +1,7 @@
|
|||||||
|
require("blink.compat").setup({})
|
||||||
|
|
||||||
require("lz.n").load({
|
require("lz.n").load({
|
||||||
{
|
{ "friendly-snippets" },
|
||||||
"friendly-snippets",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"luasnip",
|
|
||||||
event = { "InsertEnter", "CmdlineEnter" },
|
|
||||||
before = function()
|
|
||||||
require("lz.n").trigger_load("friendly-snippets")
|
|
||||||
end,
|
|
||||||
after = function()
|
|
||||||
require("luasnip.loaders.from_vscode").lazy_load()
|
|
||||||
-- Load custom lua snippets
|
|
||||||
require("luasnip.loaders.from_lua").load({ paths = { vim.fn.stdpath("config") .. "/snippets" } })
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"colorful-menu.nvim",
|
"colorful-menu.nvim",
|
||||||
event = { "InsertEnter", "CmdlineEnter" },
|
event = { "InsertEnter", "CmdlineEnter" },
|
||||||
@@ -24,41 +12,17 @@ require("lz.n").load({
|
|||||||
{
|
{
|
||||||
"blink.cmp",
|
"blink.cmp",
|
||||||
event = { "InsertEnter", "CmdlineEnter" },
|
event = { "InsertEnter", "CmdlineEnter" },
|
||||||
before = function()
|
|
||||||
-- Trigger lazydev so it's ready for blink source
|
|
||||||
require("lz.n").trigger_load({ "lazydev.nvim", "luasnip", "colorful-menu.nvim" })
|
|
||||||
end,
|
|
||||||
after = function()
|
after = function()
|
||||||
require("blink.cmp").setup({
|
require("blink.cmp").setup({
|
||||||
keymap = {
|
keymap = {
|
||||||
preset = "default",
|
|
||||||
|
|
||||||
-- [Up/Down]
|
|
||||||
["<C-j>"] = { "select_next", "fallback" },
|
["<C-j>"] = { "select_next", "fallback" },
|
||||||
["<C-k>"] = { "select_prev", "fallback" }, -- Overrides Signature Help
|
["<C-k>"] = { "select_prev", "fallback" },
|
||||||
|
["<Tab>"] = { "select_and_accept", "fallback" },
|
||||||
-- [Insert Suggestion]
|
|
||||||
["<tab>"] = { "select_and_accept", "fallback" },
|
|
||||||
|
|
||||||
-- [Remap Signature Help]
|
|
||||||
-- Since we took <C-k>, let's move signature help to <C-g> (optional)
|
|
||||||
["<C-g>"] = { "show_signature", "hide_signature", "fallback" },
|
["<C-g>"] = { "show_signature", "hide_signature", "fallback" },
|
||||||
},
|
},
|
||||||
|
|
||||||
appearance = {
|
|
||||||
nerd_font_variant = "mono",
|
|
||||||
},
|
|
||||||
|
|
||||||
completion = {
|
completion = {
|
||||||
documentation = {
|
documentation = { auto_show = true },
|
||||||
auto_show = true,
|
|
||||||
auto_show_delay_ms = 500,
|
|
||||||
window = {
|
|
||||||
border = "none",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
menu = {
|
menu = {
|
||||||
border = "none",
|
|
||||||
draw = {
|
draw = {
|
||||||
columns = { { "kind_icon" }, { "label", gap = 1 } },
|
columns = { { "kind_icon" }, { "label", gap = 1 } },
|
||||||
components = {
|
components = {
|
||||||
@@ -74,38 +38,30 @@ require("lz.n").load({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
cmdline = {
|
cmdline = {
|
||||||
completion = {
|
completion = { menu = { auto_show = true } },
|
||||||
menu = {
|
|
||||||
auto_show = true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
keymap = { preset = "inherit" },
|
keymap = { preset = "inherit" },
|
||||||
},
|
},
|
||||||
|
|
||||||
sources = {
|
sources = {
|
||||||
default = {
|
default = { "lsp", "path", "snippets" },
|
||||||
"lsp",
|
per_filetype = {
|
||||||
"path",
|
lua = { inherit_defaults = true, "lazydev" },
|
||||||
"snippets",
|
sql = { inherit_defaults = true, "vim-dadbod-completion" },
|
||||||
"lazydev",
|
mysql = { inherit_defaults = true, "vim-dadbod-completion" },
|
||||||
|
plsql = { inherit_defaults = true, "vim-dadbod-completion" },
|
||||||
},
|
},
|
||||||
providers = {
|
providers = {
|
||||||
lazydev = { module = "lazydev.integrations.blink", score_offset = 100 },
|
lazydev = {
|
||||||
|
module = "lazydev.integrations.blink",
|
||||||
|
score_offset = 100,
|
||||||
|
},
|
||||||
|
["vim-dadbod-completion"] = {
|
||||||
|
name = "vim-dadbod-completion",
|
||||||
|
module = "blink.compat.source",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
snippets = { preset = "luasnip" },
|
|
||||||
|
|
||||||
fuzzy = { implementation = "prefer_rust_with_warning" },
|
|
||||||
|
|
||||||
signature = {
|
|
||||||
enabled = true,
|
|
||||||
window = {
|
|
||||||
border = "none",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
signature = { enabled = true },
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
require("lz.n").load({
|
|
||||||
{
|
|
||||||
"mini.nvim",
|
|
||||||
event = { "BufReadPre", "BufNewFile" },
|
|
||||||
after = function()
|
|
||||||
require("mini.ai").setup({ n_lines = 500 })
|
|
||||||
require("mini.surround").setup()
|
|
||||||
require("mini.pairs").setup()
|
|
||||||
|
|
||||||
local files = require("mini.files")
|
|
||||||
files.setup()
|
|
||||||
vim.keymap.set("n", "<leader>e", function()
|
|
||||||
if not files.close() then
|
|
||||||
files.open(vim.api.nvim_buf_get_name(0))
|
|
||||||
end
|
|
||||||
end, { desc = "File [E]xplorer" })
|
|
||||||
|
|
||||||
local icons = require("mini.icons")
|
|
||||||
icons.setup()
|
|
||||||
icons.mock_nvim_web_devicons()
|
|
||||||
|
|
||||||
local hipatterns = require("mini.hipatterns")
|
|
||||||
hipatterns.setup({
|
|
||||||
highlighters = {
|
|
||||||
hex_color = hipatterns.gen_highlighter.hex_color(),
|
|
||||||
fixme = { pattern = "%f[%w]()FIXME()%f[%W]", group = "MiniHipatternsFixme" },
|
|
||||||
hack = { pattern = "%f[%w]()HACK()%f[%W]", group = "MiniHipatternsHack" },
|
|
||||||
todo = { pattern = "%f[%w]()TODO()%f[%W]", group = "MiniHipatternsTodo" },
|
|
||||||
note = { pattern = "%f[%w]()NOTE()%f[%W]", group = "MiniHipatternsNote" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
local indentscope = require("mini.indentscope")
|
|
||||||
indentscope.setup({
|
|
||||||
symbol = "│",
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("FileType", {
|
|
||||||
pattern = { "help", "Trouble" },
|
|
||||||
callback = function()
|
|
||||||
vim.b.miniindentscope_disable = true
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"guess-indent.nvim",
|
|
||||||
event = { "BufReadPre", "BufNewFile" },
|
|
||||||
after = function()
|
|
||||||
require("guess-indent").setup({})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"direnv.vim",
|
|
||||||
event = "BufEnter",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
local commands = {
|
||||||
|
"DBUI",
|
||||||
|
"DBUIToggle",
|
||||||
|
"DBUIAddConnection",
|
||||||
|
"DBUIFindBuffer",
|
||||||
|
"DBUIRenameBuffer",
|
||||||
|
"DBUILastQueryInfo",
|
||||||
|
}
|
||||||
|
local sql_filetypes = { "sql", "mysql", "plsql" }
|
||||||
|
local query_filetypes = { "sql", "mysql", "plsql", "javascript" }
|
||||||
|
|
||||||
|
require("lz.n").load({
|
||||||
|
{ "vim-dadbod" },
|
||||||
|
{ "vim-dadbod-completion", ft = sql_filetypes },
|
||||||
|
{
|
||||||
|
"vim-dadbod-ui",
|
||||||
|
cmd = commands,
|
||||||
|
ft = query_filetypes,
|
||||||
|
keys = {
|
||||||
|
{ "<leader>qo", "<cmd>DBUIToggle<cr>", desc = "Toggle database workspace" },
|
||||||
|
{
|
||||||
|
"<localleader>r",
|
||||||
|
"<Plug>(DBUI_ExecuteQuery)",
|
||||||
|
mode = { "n", "x" },
|
||||||
|
ft = query_filetypes,
|
||||||
|
remap = true,
|
||||||
|
desc = "Execute database query",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>s",
|
||||||
|
"<Plug>(DBUI_SaveQuery)",
|
||||||
|
ft = query_filetypes,
|
||||||
|
remap = true,
|
||||||
|
desc = "Save database query",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>p",
|
||||||
|
"<Plug>(DBUI_EditBindParameters)",
|
||||||
|
ft = query_filetypes,
|
||||||
|
remap = true,
|
||||||
|
desc = "Edit query parameters",
|
||||||
|
},
|
||||||
|
{ "<CR>", "<Plug>(DBUI_SelectLine)", ft = "dbui", remap = true, desc = "Open database item" },
|
||||||
|
{
|
||||||
|
"<localleader>a",
|
||||||
|
"<Plug>(DBUI_AddConnection)",
|
||||||
|
ft = "dbui",
|
||||||
|
remap = true,
|
||||||
|
desc = "Add database connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>d",
|
||||||
|
"<Plug>(DBUI_DeleteLine)",
|
||||||
|
ft = "dbui",
|
||||||
|
remap = true,
|
||||||
|
desc = "Delete database item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>n",
|
||||||
|
"<Plug>(DBUI_RenameLine)",
|
||||||
|
ft = "dbui",
|
||||||
|
remap = true,
|
||||||
|
desc = "Rename database item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>r",
|
||||||
|
"<Plug>(DBUI_Redraw)",
|
||||||
|
ft = "dbui",
|
||||||
|
remap = true,
|
||||||
|
desc = "Refresh database workspace",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>f",
|
||||||
|
"<Plug>(DBUI_JumpToForeignKey)",
|
||||||
|
ft = "dbout",
|
||||||
|
remap = true,
|
||||||
|
desc = "Follow foreign key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>l",
|
||||||
|
"<Plug>(DBUI_ToggleResultLayout)",
|
||||||
|
ft = "dbout",
|
||||||
|
remap = true,
|
||||||
|
desc = "Toggle result layout",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
before = function()
|
||||||
|
vim.g.db_ui_execute_on_save = 0
|
||||||
|
vim.g.db_ui_tmp_query_location = vim.fn.stdpath("data") .. "/db_ui/working"
|
||||||
|
vim.g.db_ui_disable_mappings = 1
|
||||||
|
vim.g.db_ui_show_help = 0
|
||||||
|
vim.g.dbs = {
|
||||||
|
["MySQL — TEST"] = vim.env.YOOKR_MYSQL_TEST_DATABASE_URL,
|
||||||
|
["MySQL — LIVE"] = vim.env.YOOKR_MYSQL_LIVE_DATABASE_URL,
|
||||||
|
["MongoDB — TEST"] = vim.env.YOOKR_MONGODB_TEST_DATABASE_URL,
|
||||||
|
["MongoDB — LIVE"] = vim.env.YOOKR_MONGODB_LIVE_DATABASE_URL,
|
||||||
|
}
|
||||||
|
if vim.env.DATABASE_URL and vim.env.DATABASE_URL ~= "" then
|
||||||
|
vim.g.db = vim.env.DATABASE_URL
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
vim.diagnostic.config({
|
||||||
|
virtual_text = true,
|
||||||
|
signs = {
|
||||||
|
text = {
|
||||||
|
[vim.diagnostic.severity.ERROR] = " ",
|
||||||
|
[vim.diagnostic.severity.WARN] = " ",
|
||||||
|
[vim.diagnostic.severity.INFO] = " ",
|
||||||
|
[vim.diagnostic.severity.HINT] = " ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
severity_sort = true,
|
||||||
|
float = { source = "if_many" },
|
||||||
|
})
|
||||||
|
|
||||||
|
require("lz.n").load({
|
||||||
|
{
|
||||||
|
"trouble.nvim",
|
||||||
|
cmd = "Trouble",
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<leader>dd",
|
||||||
|
"<cmd>Trouble diagnostics toggle<cr>",
|
||||||
|
desc = "Show workspace diagnostics",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>db",
|
||||||
|
"<cmd>Trouble diagnostics toggle filter.buf=0<cr>",
|
||||||
|
desc = "Show buffer diagnostics",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
after = function()
|
||||||
|
require("trouble").setup({})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
require("lz.n").load({
|
||||||
|
{
|
||||||
|
"mini.nvim",
|
||||||
|
-- Icons must exist before Oil opens an initial directory buffer.
|
||||||
|
lazy = false,
|
||||||
|
after = function()
|
||||||
|
require("mini.ai").setup({ n_lines = 500 })
|
||||||
|
require("mini.surround").setup()
|
||||||
|
require("mini.pairs").setup()
|
||||||
|
|
||||||
|
local icons = require("mini.icons")
|
||||||
|
icons.setup()
|
||||||
|
icons.mock_nvim_web_devicons()
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"guess-indent.nvim",
|
||||||
|
event = { "BufReadPre", "BufNewFile" },
|
||||||
|
after = function()
|
||||||
|
require("guess-indent").setup({})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nvim-treesitter",
|
||||||
|
event = "FileType",
|
||||||
|
after = function()
|
||||||
|
require("nvim-treesitter").setup({})
|
||||||
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
|
group = vim.api.nvim_create_augroup("lux-treesitter", { clear = true }),
|
||||||
|
callback = function(args)
|
||||||
|
if vim.bo[args.buf].buftype ~= "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if pcall(vim.treesitter.start, args.buf) then
|
||||||
|
vim.bo[args.buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nvim-treesitter-textobjects",
|
||||||
|
event = "FileType",
|
||||||
|
after = function()
|
||||||
|
require("nvim-treesitter-textobjects").setup({
|
||||||
|
select = { lookahead = true },
|
||||||
|
})
|
||||||
|
|
||||||
|
local select = require("nvim-treesitter-textobjects.select")
|
||||||
|
local move = require("nvim-treesitter-textobjects.move")
|
||||||
|
local objects = {
|
||||||
|
{ "if", "@function.inner", "Select inner function" },
|
||||||
|
{ "af", "@function.outer", "Select outer function" },
|
||||||
|
{ "ic", "@class.inner", "Select inner class" },
|
||||||
|
{ "ac", "@class.outer", "Select outer class" },
|
||||||
|
{ "ia", "@parameter.inner", "Select inner parameter" },
|
||||||
|
{ "aa", "@parameter.outer", "Select outer parameter" },
|
||||||
|
{ "ii", "@conditional.inner", "Select inner conditional" },
|
||||||
|
{ "ai", "@conditional.outer", "Select outer conditional" },
|
||||||
|
{ "il", "@loop.inner", "Select inner loop" },
|
||||||
|
{ "al", "@loop.outer", "Select outer loop" },
|
||||||
|
{ "ib", "@block.inner", "Select inner block" },
|
||||||
|
{ "ab", "@block.outer", "Select outer block" },
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, object in ipairs(objects) do
|
||||||
|
vim.keymap.set({ "o", "x" }, object[1], function()
|
||||||
|
select.select_textobject(object[2], "textobjects")
|
||||||
|
end, { desc = object[3] })
|
||||||
|
end
|
||||||
|
|
||||||
|
local motions = {
|
||||||
|
{ "[f", move.goto_previous_start, "@function.outer", "Previous function" },
|
||||||
|
{ "]f", move.goto_next_start, "@function.outer", "Next function" },
|
||||||
|
{ "[c", move.goto_previous_start, "@class.outer", "Previous class" },
|
||||||
|
{ "]c", move.goto_next_start, "@class.outer", "Next class" },
|
||||||
|
{ "[a", move.goto_previous_start, "@parameter.inner", "Previous parameter" },
|
||||||
|
{ "]a", move.goto_next_start, "@parameter.inner", "Next parameter" },
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, motion in ipairs(motions) do
|
||||||
|
vim.keymap.set({ "n", "x", "o" }, motion[1], function()
|
||||||
|
motion[2](motion[3], "textobjects")
|
||||||
|
end, { desc = motion[4] })
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rainbow-delimiters.nvim",
|
||||||
|
event = "FileType",
|
||||||
|
after = function()
|
||||||
|
require("rainbow-delimiters.setup").setup({})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nvim-ts-autotag",
|
||||||
|
ft = { "svelte", "astro", "html", "javascriptreact", "typescriptreact" },
|
||||||
|
after = function()
|
||||||
|
local enabled = { enable_close = true, enable_rename = true }
|
||||||
|
require("nvim-ts-autotag").setup({
|
||||||
|
opts = { enable_close = false, enable_rename = false },
|
||||||
|
per_filetype = {
|
||||||
|
svelte = enabled,
|
||||||
|
astro = enabled,
|
||||||
|
html = enabled,
|
||||||
|
javascriptreact = enabled,
|
||||||
|
typescriptreact = enabled,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"grug-far.nvim",
|
||||||
|
cmd = "GrugFar",
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<leader>cR",
|
||||||
|
function()
|
||||||
|
require("grug-far").open({
|
||||||
|
prefills = {
|
||||||
|
search = vim.fn.expand("<cword>"),
|
||||||
|
paths = require("project").root(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
desc = "Replace across project",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>cR",
|
||||||
|
function()
|
||||||
|
require("grug-far").with_visual_selection({
|
||||||
|
prefills = { paths = require("project").root() },
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
mode = "x",
|
||||||
|
desc = "Replace selection across project",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
local function format_options(bufnr)
|
local formatters_by_ft = {
|
||||||
local oxfmt = vim.lsp.get_clients({
|
lua = { "stylua" },
|
||||||
bufnr = bufnr,
|
nix = { "nixfmt" },
|
||||||
name = "oxfmt",
|
javascript = { "oxfmt" },
|
||||||
method = "textDocument/formatting",
|
javascriptreact = { "oxfmt" },
|
||||||
})[1]
|
typescript = { "oxfmt" },
|
||||||
|
typescriptreact = { "oxfmt" },
|
||||||
return {
|
json = { "oxfmt" },
|
||||||
lsp_format = "fallback",
|
jsonc = { "oxfmt" },
|
||||||
name = oxfmt and "oxfmt" or nil,
|
css = { "oxfmt" },
|
||||||
|
html = { "oxfmt" },
|
||||||
|
svelte = { "oxfmt" },
|
||||||
|
astro = { lsp_format = "fallback" },
|
||||||
|
python = { "ruff_fix", "ruff_format" },
|
||||||
|
rust = { "rustfmt" },
|
||||||
|
toml = { "taplo" },
|
||||||
|
yaml = { "yamlfmt" },
|
||||||
|
typst = { "typstyle" },
|
||||||
}
|
}
|
||||||
end
|
|
||||||
|
|
||||||
require("lz.n").load({
|
require("lz.n").load({
|
||||||
{
|
{
|
||||||
@@ -18,40 +25,22 @@ require("lz.n").load({
|
|||||||
cmd = "ConformInfo",
|
cmd = "ConformInfo",
|
||||||
keys = {
|
keys = {
|
||||||
{
|
{
|
||||||
"<leader>f",
|
"<leader>cf",
|
||||||
function()
|
function()
|
||||||
local options = format_options(vim.api.nvim_get_current_buf())
|
require("conform").format({ async = true })
|
||||||
options.async = true
|
|
||||||
require("conform").format(options)
|
|
||||||
end,
|
end,
|
||||||
mode = "",
|
mode = { "n", "x" },
|
||||||
desc = "[F]ormat buffer",
|
desc = "Format selection or buffer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
after = function()
|
after = function()
|
||||||
require("conform").setup({
|
require("conform").setup({
|
||||||
notify_on_error = true,
|
formatters_by_ft = formatters_by_ft,
|
||||||
format_on_save = function(bufnr)
|
format_on_save = function(bufnr)
|
||||||
local disable_filetypes = { c = true, cpp = true }
|
if formatters_by_ft[vim.bo[bufnr].filetype] then
|
||||||
if disable_filetypes[vim.bo[bufnr].filetype] then
|
return { timeout_ms = 500 }
|
||||||
return nil
|
|
||||||
else
|
|
||||||
local options = format_options(bufnr)
|
|
||||||
options.timeout_ms = 500
|
|
||||||
return options
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
formatters_by_ft = {
|
|
||||||
lua = { "stylua" },
|
|
||||||
python = { "ruff_fix", "ruff_format", "ruff_organize_imports" },
|
|
||||||
toml = { "taplo" },
|
|
||||||
yaml = { "yamlfmt" },
|
|
||||||
},
|
|
||||||
formatters = {
|
|
||||||
stylua = {
|
|
||||||
prepend_args = { "--indent-type", "Spaces", "--indent-width", "2" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
require("lz.n").load({
|
||||||
|
{
|
||||||
|
"kulala.nvim",
|
||||||
|
ft = { "http", "rest" },
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<localleader>r",
|
||||||
|
function()
|
||||||
|
require("kulala").run()
|
||||||
|
end,
|
||||||
|
mode = { "n", "x" },
|
||||||
|
ft = { "http", "rest" },
|
||||||
|
desc = "Execute HTTP request",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>a",
|
||||||
|
function()
|
||||||
|
require("kulala").run_all()
|
||||||
|
end,
|
||||||
|
ft = { "http", "rest" },
|
||||||
|
desc = "Execute all HTTP requests",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<localleader>e",
|
||||||
|
function()
|
||||||
|
require("kulala").set_selected_env()
|
||||||
|
end,
|
||||||
|
ft = { "http", "rest" },
|
||||||
|
desc = "Select HTTP environment",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>rr",
|
||||||
|
function()
|
||||||
|
require("kulala").scratchpad()
|
||||||
|
end,
|
||||||
|
desc = "Open HTTP scratchpad",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>rc",
|
||||||
|
function()
|
||||||
|
require("kulala").from_curl()
|
||||||
|
end,
|
||||||
|
desc = "Import cURL request",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>ro",
|
||||||
|
function()
|
||||||
|
require("kulala").open()
|
||||||
|
end,
|
||||||
|
desc = "Open HTTP request",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
after = function()
|
||||||
|
require("kulala").setup({
|
||||||
|
kulala_core = { path = require("nix-info").settings.kulalaCorePath },
|
||||||
|
treesitter = { enable = false },
|
||||||
|
session = { restore = false },
|
||||||
|
ui = { winbar = false },
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,20 +1,8 @@
|
|||||||
require("lz.n").load({
|
require("lz.n").load({
|
||||||
{
|
{ "nvim-navic" },
|
||||||
"typst-preview.nvim",
|
{ "SchemaStore.nvim" },
|
||||||
ft = "typst",
|
|
||||||
after = function()
|
|
||||||
-- Setup typst-preview
|
|
||||||
require("typst-preview").setup({
|
|
||||||
-- Optionally configure things here
|
|
||||||
dependencies_bin = require("nix-info").settings.typstPreviewDependencies,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>tp", "<cmd>TypstPreviewToggle<cr>", { desc = "[T]ypst [P]review Toggle" })
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"lazydev.nvim",
|
"lazydev.nvim",
|
||||||
cmd = "LazyDev",
|
|
||||||
ft = "lua",
|
ft = "lua",
|
||||||
after = function()
|
after = function()
|
||||||
require("lazydev").setup({
|
require("lazydev").setup({
|
||||||
@@ -27,165 +15,91 @@ require("lz.n").load({
|
|||||||
{
|
{
|
||||||
"nvim-lspconfig",
|
"nvim-lspconfig",
|
||||||
event = { "BufReadPre", "BufNewFile" },
|
event = { "BufReadPre", "BufNewFile" },
|
||||||
before = function()
|
|
||||||
require("lz.n").trigger_load("lazydev.nvim")
|
|
||||||
end,
|
|
||||||
after = function()
|
after = function()
|
||||||
local telescope_plugins = {
|
local highlight_group = vim.api.nvim_create_augroup("lux-lsp-highlight", { clear = true })
|
||||||
"project.nvim",
|
|
||||||
"telescope.nvim",
|
|
||||||
"telescope-fzf-native.nvim",
|
|
||||||
"telescope-ui-select.nvim",
|
|
||||||
}
|
|
||||||
|
|
||||||
local telescope_picker = function(picker)
|
|
||||||
return function()
|
|
||||||
require("lz.n").trigger_load(telescope_plugins)
|
|
||||||
require("telescope.builtin")[picker]()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local lsp_float = { border = "single" }
|
|
||||||
|
|
||||||
local navic_excluded_filetypes = {
|
|
||||||
[""] = true,
|
|
||||||
help = true,
|
|
||||||
minifiles = true,
|
|
||||||
TelescopePrompt = true,
|
|
||||||
Trouble = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function navic_enabled(bufnr)
|
|
||||||
return vim.bo[bufnr].buftype == "" and not navic_excluded_filetypes[vim.bo[bufnr].filetype]
|
|
||||||
end
|
|
||||||
|
|
||||||
_G.lux_navic_location = function()
|
|
||||||
local ok, navic = pcall(require, "nvim-navic")
|
|
||||||
if not ok or not navic.is_available() then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
return navic.get_location()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_winbar(winid, bufnr)
|
|
||||||
if not vim.api.nvim_win_is_valid(winid) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_float = vim.api.nvim_win_get_config(winid).relative ~= ""
|
|
||||||
local winbar = ""
|
|
||||||
if not is_float and vim.b[bufnr].lux_navic_attached and navic_enabled(bufnr) then
|
|
||||||
winbar = "%{%v:lua.lux_navic_location()%}"
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_set_option_value("winbar", winbar, { win = winid })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_winbars_for_buffer(bufnr)
|
|
||||||
for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
|
|
||||||
update_winbar(winid, bufnr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter", "WinEnter" }, {
|
|
||||||
group = vim.api.nvim_create_augroup("lux-navic-winbar", { clear = true }),
|
|
||||||
callback = function(args)
|
|
||||||
update_winbar(vim.api.nvim_get_current_win(), args.buf)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("LspAttach", {
|
vim.api.nvim_create_autocmd("LspAttach", {
|
||||||
group = vim.api.nvim_create_augroup("lux-lsp-attach", { clear = true }),
|
group = vim.api.nvim_create_augroup("lux-lsp-attach", { clear = true }),
|
||||||
callback = function(args)
|
callback = function(args)
|
||||||
local client = vim.lsp.get_client_by_id(args.data.client_id)
|
local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
|
||||||
local bufnr = args.buf
|
local bufnr = args.buf
|
||||||
|
local map = function(mode, lhs, rhs, desc)
|
||||||
local map = function(keys, func, desc, mode)
|
vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc })
|
||||||
mode = mode or "n"
|
|
||||||
vim.keymap.set(mode, keys, func, { buffer = bufnr, desc = "LSP: " .. desc })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
map("<leader>rn", vim.lsp.buf.rename, "[R]e[n]ame")
|
map("n", "gd", function()
|
||||||
map("<leader>ca", vim.lsp.buf.code_action, "[C]ode [A]ction", { "n", "x" })
|
require("telescope.builtin").lsp_definitions()
|
||||||
map("gD", vim.lsp.buf.declaration, "[G]oto [D]eclaration")
|
end, "Go to definition")
|
||||||
map("K", function()
|
map("n", "gD", vim.lsp.buf.declaration, "Go to declaration")
|
||||||
vim.lsp.buf.hover(lsp_float)
|
map("n", "gr", function()
|
||||||
end, "Hover Documentation")
|
require("telescope.builtin").lsp_references()
|
||||||
map("<C-s>", function()
|
end, "Find references")
|
||||||
vim.lsp.buf.signature_help(lsp_float)
|
map("n", "gI", function()
|
||||||
end, "Signature Help", { "i", "s" })
|
require("telescope.builtin").lsp_implementations()
|
||||||
|
end, "Go to implementation")
|
||||||
|
map("n", "gy", function()
|
||||||
|
require("telescope.builtin").lsp_type_definitions()
|
||||||
|
end, "Go to type definition")
|
||||||
|
map("n", "K", vim.lsp.buf.hover, "Show hover documentation")
|
||||||
|
map({ "n", "x" }, "<leader>ca", vim.lsp.buf.code_action, "Apply code action")
|
||||||
|
map("n", "<leader>cr", vim.lsp.buf.rename, "Rename symbol")
|
||||||
|
|
||||||
map("gd", telescope_picker("lsp_definitions"), "[G]oto [D]efinition")
|
if client:supports_method("textDocument/documentSymbol", bufnr) then
|
||||||
map("gr", telescope_picker("lsp_references"), "[G]oto [R]eferences")
|
map("n", "gO", function()
|
||||||
map("gI", telescope_picker("lsp_implementations"), "[G]oto [I]mplementation")
|
require("telescope.builtin").lsp_document_symbols()
|
||||||
map("<leader>D", telescope_picker("lsp_type_definitions"), "Type [D]efinition")
|
end, "Find document symbols")
|
||||||
map("<leader>ds", telescope_picker("lsp_document_symbols"), "[D]ocument [S]ymbols")
|
require("nvim-navic").attach(client, bufnr)
|
||||||
map("<leader>ws", telescope_picker("lsp_dynamic_workspace_symbols"), "[W]orkspace [S]ymbols")
|
|
||||||
|
|
||||||
if client and client:supports_method("textDocument/documentSymbol", bufnr) and navic_enabled(bufnr) then
|
|
||||||
require("lz.n").trigger_load("nvim-navic")
|
|
||||||
local ok, navic = pcall(require, "nvim-navic")
|
|
||||||
if ok then
|
|
||||||
navic.attach(client, bufnr)
|
|
||||||
vim.b[bufnr].lux_navic_attached = true
|
|
||||||
update_winbars_for_buffer(bufnr)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if client and client:supports_method("textDocument/documentHighlight", bufnr) then
|
if client:supports_method("textDocument/documentHighlight", bufnr) then
|
||||||
local highlight_augroup = vim.api.nvim_create_augroup("lux-lsp-highlight", { clear = false })
|
local autocmds = vim.api.nvim_get_autocmds({ group = highlight_group, buffer = bufnr })
|
||||||
|
if #autocmds == 0 then
|
||||||
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
|
vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
|
||||||
|
group = highlight_group,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
group = highlight_augroup,
|
|
||||||
callback = vim.lsp.buf.document_highlight,
|
callback = vim.lsp.buf.document_highlight,
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
|
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
|
||||||
|
group = highlight_group,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
group = highlight_augroup,
|
|
||||||
callback = vim.lsp.buf.clear_references,
|
callback = vim.lsp.buf.clear_references,
|
||||||
})
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if client:supports_method("textDocument/inlayHint", bufnr) then
|
||||||
|
map("n", "<leader>uh", function()
|
||||||
|
vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr })
|
||||||
|
end, "Toggle inlay hints")
|
||||||
|
end
|
||||||
|
|
||||||
|
if client.name == "tinymist" then
|
||||||
|
vim.api.nvim_buf_create_user_command(bufnr, "TinymistPreview", function()
|
||||||
|
client:exec_cmd({ command = "tinymist.startDefaultPreview" }, { bufnr = bufnr })
|
||||||
|
end, { desc = "Start Tinymist preview", force = true })
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("LspDetach", {
|
vim.api.nvim_create_autocmd("LspDetach", {
|
||||||
group = vim.api.nvim_create_augroup("lux-lsp-detach", { clear = true }),
|
group = vim.api.nvim_create_augroup("lux-lsp-detach", { clear = true }),
|
||||||
callback = function(event)
|
callback = function(args)
|
||||||
|
local supports_highlights = vim
|
||||||
|
.iter(vim.lsp.get_clients({
|
||||||
|
bufnr = args.buf,
|
||||||
|
method = "textDocument/documentHighlight",
|
||||||
|
}))
|
||||||
|
:any(function(client)
|
||||||
|
return client.id ~= args.data.client_id
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not supports_highlights then
|
||||||
vim.lsp.buf.clear_references()
|
vim.lsp.buf.clear_references()
|
||||||
vim.api.nvim_clear_autocmds({ group = "lux-lsp-highlight", buffer = event.buf })
|
vim.api.nvim_clear_autocmds({ group = highlight_group, buffer = args.buf })
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if client and client:supports_method("textDocument/inlayHint", bufnr) then
|
|
||||||
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
|
|
||||||
map("<leader>th", function()
|
|
||||||
vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }))
|
|
||||||
end, "[T]oggle Inlay [H]ints")
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.diagnostic.config({
|
|
||||||
severity_sort = true,
|
|
||||||
underline = true,
|
|
||||||
signs = {
|
|
||||||
text = {
|
|
||||||
[vim.diagnostic.severity.ERROR] = " ",
|
|
||||||
[vim.diagnostic.severity.WARN] = " ",
|
|
||||||
[vim.diagnostic.severity.INFO] = " ",
|
|
||||||
[vim.diagnostic.severity.HINT] = " ",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
virtual_text = false,
|
|
||||||
virtual_lines = {
|
|
||||||
current_line = true,
|
|
||||||
},
|
|
||||||
float = {
|
|
||||||
border = "single",
|
|
||||||
source = "if_many",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.lsp.config("lua_ls", {
|
vim.lsp.config("lua_ls", {
|
||||||
settings = {
|
settings = {
|
||||||
Lua = {
|
Lua = {
|
||||||
@@ -197,10 +111,8 @@ require("lz.n").load({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
vim.lsp.enable("lua_ls")
|
|
||||||
|
|
||||||
local settings = require("nix-info").settings
|
local settings = require("nix-info").settings
|
||||||
|
|
||||||
vim.lsp.config("nixd", {
|
vim.lsp.config("nixd", {
|
||||||
settings = {
|
settings = {
|
||||||
nixd = {
|
nixd = {
|
||||||
@@ -209,64 +121,53 @@ require("lz.n").load({
|
|||||||
nixos = { expr = settings.nixdExtras.nixos_options },
|
nixos = { expr = settings.nixdExtras.nixos_options },
|
||||||
["home-manager"] = { expr = settings.nixdExtras.home_manager_options },
|
["home-manager"] = { expr = settings.nixdExtras.home_manager_options },
|
||||||
},
|
},
|
||||||
formatting = { command = { "nixfmt" } },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
vim.lsp.enable("nixd")
|
|
||||||
|
|
||||||
vim.lsp.enable("dafny")
|
vim.lsp.config("jsonls", {
|
||||||
vim.lsp.enable("jsonls")
|
settings = {
|
||||||
|
json = {
|
||||||
|
schemas = require("schemastore").json.schemas(),
|
||||||
|
validate = { enable = true },
|
||||||
|
format = { enable = false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.lsp.config("yamlls", {
|
||||||
|
settings = {
|
||||||
|
yaml = {
|
||||||
|
schemas = require("schemastore").yaml.schemas(),
|
||||||
|
schemaStore = { enable = false, url = "" },
|
||||||
|
format = { enable = false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
vim.lsp.config("vtsls", {
|
vim.lsp.config("vtsls", {
|
||||||
settings = {
|
settings = {
|
||||||
vtsls = {
|
vtsls = { autoUseWorkspaceTsdk = true },
|
||||||
autoUseWorkspaceTsdk = true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
vim.lsp.enable("vtsls")
|
|
||||||
|
|
||||||
vim.lsp.enable("cssls")
|
vim.lsp.enable({
|
||||||
vim.lsp.enable("html")
|
"lua_ls",
|
||||||
vim.lsp.enable("emmet_language_server")
|
"nixd",
|
||||||
vim.lsp.enable("oxlint")
|
"jsonls",
|
||||||
|
"yamlls",
|
||||||
vim.lsp.config("oxfmt", {
|
"vtsls",
|
||||||
filetypes = vim.list_extend({ "svelte" }, vim.lsp.config.oxfmt.filetypes),
|
"cssls",
|
||||||
})
|
"html",
|
||||||
vim.lsp.enable("oxfmt")
|
"emmet_language_server",
|
||||||
|
"oxlint",
|
||||||
vim.lsp.enable("tailwindcss")
|
"tailwindcss",
|
||||||
vim.lsp.enable("rust_analyzer")
|
"rust_analyzer",
|
||||||
vim.lsp.enable("ty")
|
"ty",
|
||||||
vim.lsp.enable("ruff")
|
"ruff",
|
||||||
vim.lsp.enable("astro")
|
"astro",
|
||||||
vim.lsp.enable("svelte")
|
"svelte",
|
||||||
|
"tinymist",
|
||||||
vim.lsp.config("tinymist", {
|
|
||||||
settings = {
|
|
||||||
tinymist = {
|
|
||||||
formatterMode = "typstyle",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
vim.lsp.enable("tinymist")
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trouble.nvim",
|
|
||||||
cmd = "Trouble",
|
|
||||||
keys = {
|
|
||||||
{ "<leader>xx", "<cmd>Trouble diagnostics toggle<cr>", desc = "Diagnostics (Trouble)" },
|
|
||||||
{ "<leader>xX", "<cmd>Trouble diagnostics toggle filter.buf=0<cr>", desc = "Buffer Diagnostics (Trouble)" },
|
|
||||||
},
|
|
||||||
after = function()
|
|
||||||
require("trouble").setup({
|
|
||||||
focus = true,
|
|
||||||
preview = {
|
|
||||||
border = "single",
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
require("oil").setup({})
|
||||||
|
vim.keymap.set("n", "<leader>e", function()
|
||||||
|
local name = vim.api.nvim_buf_get_name(0)
|
||||||
|
require("oil").open(name == "" and vim.fn.getcwd() or vim.fs.dirname(name))
|
||||||
|
end, { desc = "Open file directory" })
|
||||||
|
|
||||||
|
require("lz.n").load({
|
||||||
|
{ "plenary.nvim" },
|
||||||
|
{ "telescope-fzf-native.nvim" },
|
||||||
|
{ "telescope-ui-select.nvim" },
|
||||||
|
{
|
||||||
|
"telescope.nvim",
|
||||||
|
event = "VimEnter",
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<leader>sf",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").find_files({ cwd = require("project").root() })
|
||||||
|
end,
|
||||||
|
desc = "Find project files",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sg",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").live_grep({ cwd = require("project").root() })
|
||||||
|
end,
|
||||||
|
desc = "Search project text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sw",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").grep_string({ cwd = require("project").root() })
|
||||||
|
end,
|
||||||
|
desc = "Search cursor word",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sb",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").current_buffer_fuzzy_find()
|
||||||
|
end,
|
||||||
|
desc = "Search current buffer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>so",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").oldfiles()
|
||||||
|
end,
|
||||||
|
desc = "Find recent files",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>ss",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").lsp_dynamic_workspace_symbols()
|
||||||
|
end,
|
||||||
|
desc = "Find workspace symbols",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sd",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").diagnostics()
|
||||||
|
end,
|
||||||
|
desc = "Find diagnostics",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sh",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").help_tags()
|
||||||
|
end,
|
||||||
|
desc = "Find help",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sc",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").commands()
|
||||||
|
end,
|
||||||
|
desc = "Find commands",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sk",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").keymaps()
|
||||||
|
end,
|
||||||
|
desc = "Find keymaps",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>sr",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").resume()
|
||||||
|
end,
|
||||||
|
desc = "Resume last search",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>bb",
|
||||||
|
function()
|
||||||
|
require("telescope.builtin").buffers()
|
||||||
|
end,
|
||||||
|
desc = "Find open buffers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
after = function()
|
||||||
|
local actions = require("telescope.actions")
|
||||||
|
local promote = function(prompt_bufnr)
|
||||||
|
actions.smart_send_to_qflist(prompt_bufnr)
|
||||||
|
vim.cmd("Trouble qflist open")
|
||||||
|
end
|
||||||
|
local picker_mappings = {
|
||||||
|
["<C-j>"] = actions.move_selection_next,
|
||||||
|
["<C-k>"] = actions.move_selection_previous,
|
||||||
|
["<C-q>"] = promote,
|
||||||
|
}
|
||||||
|
|
||||||
|
require("telescope").setup({
|
||||||
|
defaults = {
|
||||||
|
mappings = { i = picker_mappings, n = picker_mappings },
|
||||||
|
},
|
||||||
|
extensions = {
|
||||||
|
["ui-select"] = require("telescope.themes").get_dropdown(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require("telescope").load_extension("fzf")
|
||||||
|
require("telescope").load_extension("ui-select")
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
require("lz.n").load({
|
||||||
|
{
|
||||||
|
"zk-nvim",
|
||||||
|
cmd = { "ZkNew", "ZkNotes", "ZkTags", "ZkInsertLink", "ZkBacklinks", "ZkLinks" },
|
||||||
|
ft = "markdown",
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<leader>nn",
|
||||||
|
function()
|
||||||
|
local title = vim.trim(vim.fn.input("Title: "))
|
||||||
|
if title ~= "" then
|
||||||
|
require("zk").new({ notebook_path = vim.env.ZK_NOTEBOOK_DIR, title = title })
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
desc = "Create note",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>nf",
|
||||||
|
function()
|
||||||
|
require("zk").edit({ notebook_path = vim.env.ZK_NOTEBOOK_DIR }, { picker = "telescope" })
|
||||||
|
end,
|
||||||
|
desc = "Find notes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<leader>nt",
|
||||||
|
function()
|
||||||
|
require("zk.commands").get("ZkTags")({ notebook_path = vim.env.ZK_NOTEBOOK_DIR })
|
||||||
|
end,
|
||||||
|
desc = "Find note tags",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
after = function()
|
||||||
|
require("zk").setup({
|
||||||
|
picker = "telescope",
|
||||||
|
lsp = { config = { root_dir = vim.env.ZK_NOTEBOOK_DIR } },
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("FileType", {
|
||||||
|
group = vim.api.nvim_create_augroup("lux-notes-maps", { clear = true }),
|
||||||
|
pattern = "markdown",
|
||||||
|
callback = function(args)
|
||||||
|
local path = vim.api.nvim_buf_get_name(args.buf)
|
||||||
|
if require("zk.util").notebook_root(path) then
|
||||||
|
vim.keymap.set("n", "<leader>ni", "<cmd>ZkInsertLink<cr>", {
|
||||||
|
buffer = args.buf,
|
||||||
|
desc = "Insert note link",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
local telescope_plugins = {
|
|
||||||
"project.nvim",
|
|
||||||
"telescope.nvim",
|
|
||||||
"telescope-fzf-native.nvim",
|
|
||||||
"telescope-ui-select.nvim",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function load_telescope()
|
|
||||||
require("lz.n").trigger_load(telescope_plugins)
|
|
||||||
return require("telescope.builtin")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function map_picker(lhs, picker, desc, opts)
|
|
||||||
vim.keymap.set("n", lhs, function()
|
|
||||||
load_telescope()[picker](opts or {})
|
|
||||||
end, { desc = desc })
|
|
||||||
end
|
|
||||||
|
|
||||||
map_picker("<leader>sh", "help_tags", "[S]earch [H]elp")
|
|
||||||
map_picker("<leader>sk", "keymaps", "[S]earch [K]eymaps")
|
|
||||||
map_picker("<leader>sf", "find_files", "[S]earch [F]iles")
|
|
||||||
map_picker("<leader>ss", "builtin", "[S]earch [S]elect Telescope")
|
|
||||||
map_picker("<leader>sw", "grep_string", "[S]earch current [W]ord")
|
|
||||||
map_picker("<leader>sg", "live_grep", "[S]earch by [G]rep")
|
|
||||||
map_picker("<leader>sd", "diagnostics", "[S]earch [D]iagnostics")
|
|
||||||
map_picker("<leader>sr", "resume", "[S]earch [R]esume")
|
|
||||||
map_picker("<leader>s.", "oldfiles", '[S]earch Recent Files ("." for repeat)')
|
|
||||||
map_picker("<leader><leader>", "buffers", "[ ] Find existing buffers")
|
|
||||||
map_picker("<leader>sn", "find_files", "[S]earch [N]eovim files", { cwd = vim.fn.stdpath("config") })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>sp", function()
|
|
||||||
load_telescope()
|
|
||||||
require("telescope").extensions.projects.projects({})
|
|
||||||
end, { desc = "[S]earch [P]rojects" })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>/", function()
|
|
||||||
local builtin = load_telescope()
|
|
||||||
builtin.current_buffer_fuzzy_find(require("telescope.themes").get_dropdown({
|
|
||||||
winblend = 10,
|
|
||||||
previewer = false,
|
|
||||||
}))
|
|
||||||
end, { desc = "[/] Fuzzily search in current buffer" })
|
|
||||||
|
|
||||||
vim.keymap.set("n", "<leader>s/", function()
|
|
||||||
load_telescope().live_grep({
|
|
||||||
grep_open_files = true,
|
|
||||||
prompt_title = "Live Grep in Open Files",
|
|
||||||
})
|
|
||||||
end, { desc = "[S]earch [/] in Open Files" })
|
|
||||||
|
|
||||||
require("lz.n").load({
|
|
||||||
{
|
|
||||||
"project.nvim",
|
|
||||||
event = "VimEnter",
|
|
||||||
after = function()
|
|
||||||
require("project").setup({
|
|
||||||
manual_mode = false,
|
|
||||||
lsp = { enabled = true },
|
|
||||||
patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json", "flake.nix" },
|
|
||||||
show_hidden = true,
|
|
||||||
scope_chdir = "global",
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"telescope-fzf-native.nvim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"telescope-ui-select.nvim",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"telescope.nvim",
|
|
||||||
cmd = "Telescope",
|
|
||||||
before = function()
|
|
||||||
require("lz.n").trigger_load("project.nvim")
|
|
||||||
end,
|
|
||||||
after = function()
|
|
||||||
local actions = require("telescope.actions")
|
|
||||||
|
|
||||||
require("telescope").setup({
|
|
||||||
defaults = {
|
|
||||||
path_display = { "truncate" },
|
|
||||||
border = true,
|
|
||||||
borderchars = { "─", "│", "─", "│", "┌", "┐", "┘", "└" },
|
|
||||||
layout_strategy = "horizontal",
|
|
||||||
layout_config = {
|
|
||||||
prompt_position = "top",
|
|
||||||
},
|
|
||||||
sorting_strategy = "ascending",
|
|
||||||
mappings = {
|
|
||||||
i = {
|
|
||||||
["<C-k>"] = actions.move_selection_previous,
|
|
||||||
["<C-j>"] = actions.move_selection_next,
|
|
||||||
["<C-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
extensions = {
|
|
||||||
["ui-select"] = {
|
|
||||||
require("telescope.themes").get_dropdown(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
require("lz.n").trigger_load({ "telescope-fzf-native.nvim", "telescope-ui-select.nvim" })
|
|
||||||
pcall(require("telescope").load_extension, "projects")
|
|
||||||
pcall(require("telescope").load_extension, "fzf")
|
|
||||||
pcall(require("telescope").load_extension, "ui-select")
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
local ok, treesitter = pcall(require, "nvim-treesitter")
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
treesitter.setup({})
|
|
||||||
|
|
||||||
local group = vim.api.nvim_create_augroup("lux-treesitter", { clear = true })
|
|
||||||
local enableTreesitter = function(bufnr)
|
|
||||||
vim.schedule(function()
|
|
||||||
if not vim.api.nvim_buf_is_valid(bufnr) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if pcall(vim.treesitter.start, bufnr) then
|
|
||||||
vim.bo[bufnr].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter", "FileType" }, {
|
|
||||||
group = group,
|
|
||||||
pattern = "*",
|
|
||||||
callback = function(args)
|
|
||||||
enableTreesitter(args.buf)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
@@ -1,66 +1,49 @@
|
|||||||
require("lz.n").load({
|
require("lz.n").load({
|
||||||
{
|
|
||||||
"theme-loader",
|
|
||||||
event = "VimEnter",
|
|
||||||
load = function()
|
|
||||||
local settings = require("nix-info").settings
|
|
||||||
local theme_code = settings.themeSetup
|
|
||||||
|
|
||||||
local func, err = loadstring(theme_code)
|
|
||||||
if func then
|
|
||||||
func()
|
|
||||||
else
|
|
||||||
print("Error loading theme code: " .. err)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kulala.nvim",
|
|
||||||
after = function()
|
|
||||||
require("kulala").setup({
|
|
||||||
global_keymaps = true,
|
|
||||||
global_keymaps_prefix = "<leader>R",
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"lualine.nvim",
|
"lualine.nvim",
|
||||||
event = "VimEnter",
|
event = "VimEnter",
|
||||||
after = function()
|
after = function()
|
||||||
local filename = {
|
local navic = require("nvim-navic")
|
||||||
"filename",
|
local breadcrumb = function()
|
||||||
path = 1,
|
if not navic.is_available() then
|
||||||
symbols = {
|
return ""
|
||||||
modified = "●",
|
end
|
||||||
readonly = "",
|
|
||||||
unnamed = "[No Name]",
|
local location = navic.get_location()
|
||||||
},
|
if vim.fn.strdisplaywidth(location) <= 60 then
|
||||||
}
|
return location
|
||||||
|
end
|
||||||
|
|
||||||
|
while vim.fn.strdisplaywidth(location) > 59 do
|
||||||
|
location = vim.fn.strcharpart(location, 0, vim.fn.strchars(location) - 1)
|
||||||
|
end
|
||||||
|
return location .. "…"
|
||||||
|
end
|
||||||
|
|
||||||
require("lualine").setup({
|
require("lualine").setup({
|
||||||
options = {
|
options = {
|
||||||
icons_enabled = true,
|
theme = "auto",
|
||||||
globalstatus = false,
|
|
||||||
component_separators = "",
|
component_separators = "",
|
||||||
section_separators = "",
|
section_separators = "",
|
||||||
theme = "kanagawa",
|
globalstatus = true,
|
||||||
},
|
},
|
||||||
sections = {
|
sections = {
|
||||||
lualine_a = { "mode" },
|
lualine_a = { "mode" },
|
||||||
lualine_b = { "branch", "diff", "diagnostics" },
|
lualine_b = { "branch" },
|
||||||
lualine_c = { filename },
|
lualine_c = { { "filename", path = 1 }, breadcrumb },
|
||||||
|
lualine_x = {
|
||||||
lualine_x = { "lsp_status" },
|
{ "kulala", icon = "HTTP" },
|
||||||
lualine_y = { "progress" },
|
{
|
||||||
lualine_z = { "location" },
|
"db_ui#statusline",
|
||||||
|
type = "vim_fun",
|
||||||
|
cond = function()
|
||||||
|
return vim.fn.exists("*db_ui#statusline") == 1
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
inactive_sections = {
|
"diagnostics",
|
||||||
lualine_a = {},
|
},
|
||||||
lualine_b = {},
|
lualine_y = { "filetype" },
|
||||||
lualine_c = { filename },
|
lualine_z = { "location" },
|
||||||
lualine_x = {},
|
|
||||||
lualine_y = {},
|
|
||||||
lualine_z = {},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
@@ -75,88 +58,82 @@ require("lz.n").load({
|
|||||||
change = { text = "│" },
|
change = { text = "│" },
|
||||||
delete = { text = "_" },
|
delete = { text = "_" },
|
||||||
topdelete = { text = "‾" },
|
topdelete = { text = "‾" },
|
||||||
changedelete = { text = "~" },
|
|
||||||
untracked = { text = "┆" },
|
|
||||||
},
|
},
|
||||||
signs_staged_enable = true,
|
|
||||||
on_attach = function(bufnr)
|
on_attach = function(bufnr)
|
||||||
local gs = package.loaded.gitsigns
|
local gs = require("gitsigns")
|
||||||
local map = function(mode, lhs, rhs, desc, opts)
|
local map_hunk = function(lhs, direction, desc)
|
||||||
opts = opts or {}
|
vim.keymap.set("n", lhs, function()
|
||||||
opts.buffer = bufnr
|
|
||||||
opts.desc = desc
|
|
||||||
vim.keymap.set(mode, lhs, rhs, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
map("n", "]h", function()
|
|
||||||
if vim.wo.diff then
|
if vim.wo.diff then
|
||||||
return "]h"
|
return lhs
|
||||||
end
|
end
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
gs.nav_hunk("next")
|
gs.nav_hunk(direction)
|
||||||
end)
|
end)
|
||||||
return "<Ignore>"
|
return "<Ignore>"
|
||||||
end, "Next git hunk", { expr = true })
|
end, { buffer = bufnr, desc = desc, expr = true })
|
||||||
|
|
||||||
map("n", "[h", function()
|
|
||||||
if vim.wo.diff then
|
|
||||||
return "[h"
|
|
||||||
end
|
end
|
||||||
vim.schedule(function()
|
map_hunk("]h", "next", "Next git hunk")
|
||||||
gs.nav_hunk("prev")
|
map_hunk("[h", "prev", "Previous git hunk")
|
||||||
end)
|
vim.keymap.set("n", "<leader>gp", gs.preview_hunk, { buffer = bufnr, desc = "Preview git hunk" })
|
||||||
return "<Ignore>"
|
vim.keymap.set("n", "<leader>gs", gs.stage_hunk, { buffer = bufnr, desc = "Stage git hunk" })
|
||||||
end, "Previous git hunk", { expr = true })
|
|
||||||
|
|
||||||
map("n", "<leader>hp", gs.preview_hunk, "Git [H]unk [P]review")
|
|
||||||
map("n", "<leader>hs", gs.stage_hunk, "Git [H]unk [S]tage")
|
|
||||||
map("n", "<leader>hr", gs.reset_hunk, "Git [H]unk [R]eset")
|
|
||||||
map("n", "<leader>hu", gs.undo_stage_hunk, "Git [H]unk [U]ndo stage")
|
|
||||||
map("n", "<leader>hS", gs.stage_buffer, "Git [H]unk [S]tage buffer")
|
|
||||||
map("n", "<leader>hR", gs.reset_buffer, "Git [H]unk [R]eset buffer")
|
|
||||||
map("v", "<leader>hs", function()
|
|
||||||
gs.stage_hunk({ vim.fn.line("."), vim.fn.line("v") })
|
|
||||||
end, "Git [H]unk [S]tage")
|
|
||||||
map("v", "<leader>hr", function()
|
|
||||||
gs.reset_hunk({ vim.fn.line("."), vim.fn.line("v") })
|
|
||||||
end, "Git [H]unk [R]eset")
|
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"diffview.nvim",
|
||||||
|
cmd = { "DiffviewOpen", "DiffviewClose", "DiffviewRefresh", "DiffviewFileHistory" },
|
||||||
|
keys = {
|
||||||
|
{
|
||||||
|
"<leader>gd",
|
||||||
|
function()
|
||||||
|
vim.cmd("DiffviewOpen -C" .. vim.fn.fnameescape(require("project").root()))
|
||||||
|
end,
|
||||||
|
desc = "Review project changes",
|
||||||
|
},
|
||||||
|
{ "<leader>gq", "<cmd>DiffviewClose<cr>", desc = "Close git review" },
|
||||||
|
{ "<leader>gr", "<cmd>DiffviewRefresh<cr>", desc = "Refresh git review" },
|
||||||
|
},
|
||||||
|
after = function()
|
||||||
|
require("diffview").setup({})
|
||||||
|
end,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"zen-mode.nvim",
|
"zen-mode.nvim",
|
||||||
cmd = "ZenMode",
|
cmd = "ZenMode",
|
||||||
|
keys = {
|
||||||
|
{ "<leader>uz", "<cmd>ZenMode<cr>", desc = "Toggle zen mode" },
|
||||||
|
},
|
||||||
after = function()
|
after = function()
|
||||||
require("zen-mode").setup({
|
require("zen-mode").setup({})
|
||||||
window = {
|
end,
|
||||||
options = {
|
|
||||||
linebreak = true,
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"render-markdown.nvim",
|
||||||
|
ft = "markdown",
|
||||||
|
keys = {
|
||||||
|
{ "<leader>um", "<cmd>RenderMarkdown buf_toggle<cr>", desc = "Toggle Markdown rendering" },
|
||||||
},
|
},
|
||||||
})
|
after = function()
|
||||||
|
require("render-markdown").setup({})
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"which-key.nvim",
|
"which-key.nvim",
|
||||||
event = "VimEnter",
|
event = "VimEnter",
|
||||||
before = function()
|
|
||||||
require("lz.n").trigger_load("mini.nvim")
|
|
||||||
end,
|
|
||||||
after = function()
|
after = function()
|
||||||
require("which-key").setup({
|
require("which-key").setup({
|
||||||
preset = "modern",
|
preset = "helix",
|
||||||
delay = 200,
|
|
||||||
icons = {
|
|
||||||
mappings = true,
|
|
||||||
keys = {},
|
|
||||||
},
|
|
||||||
|
|
||||||
spec = {
|
spec = {
|
||||||
{ "<leader>b", group = "[B]uffer" },
|
{ "<leader>b", group = "Buffers" },
|
||||||
{ "<leader>s", group = "[S]earch" },
|
{ "<leader>c", group = "Code" },
|
||||||
{ "<leader>t", group = "[T]oggle" },
|
{ "<leader>d", group = "Diagnostics" },
|
||||||
{ "<leader>h", group = "Git [H]unk", mode = { "n", "v" } },
|
{ "<leader>g", group = "Git" },
|
||||||
|
{ "<leader>n", group = "Notes" },
|
||||||
|
{ "<leader>q", group = "Database" },
|
||||||
|
{ "<leader>r", group = "HTTP" },
|
||||||
|
{ "<leader>s", group = "Search" },
|
||||||
|
{ "<leader>u", group = "UI" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.root()
|
||||||
|
return vim.fs.root(0, { ".git" }) or vim.fn.getcwd()
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"Full lux module": {
|
||||||
|
"prefix": "luxmod",
|
||||||
|
"description": "Full lux module (NixOS and Home Manager)",
|
||||||
|
"body": [
|
||||||
|
"{ inputs, ... }:",
|
||||||
|
"{",
|
||||||
|
" lux.${1:moduleName} = {",
|
||||||
|
" nixos = { config, lib, pkgs, ... }: {",
|
||||||
|
" $2",
|
||||||
|
" };",
|
||||||
|
"",
|
||||||
|
" homeManager = { config, lib, pkgs, ... }: {",
|
||||||
|
" $3",
|
||||||
|
" };",
|
||||||
|
" };",
|
||||||
|
"}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Lux NixOS module": {
|
||||||
|
"prefix": "luxnixos",
|
||||||
|
"description": "Lux NixOS-only module",
|
||||||
|
"body": [
|
||||||
|
"{ inputs, ... }:",
|
||||||
|
"{",
|
||||||
|
" lux.${1:moduleName}.nixos = { config, lib, pkgs, ... }: {",
|
||||||
|
" $0",
|
||||||
|
" };",
|
||||||
|
"}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Lux Home Manager module": {
|
||||||
|
"prefix": "luxhm",
|
||||||
|
"description": "Lux Home Manager-only module",
|
||||||
|
"body": [
|
||||||
|
"{ inputs, ... }:",
|
||||||
|
"{",
|
||||||
|
" lux.${1:moduleName}.homeManager = { config, lib, pkgs, ... }: {",
|
||||||
|
" $0",
|
||||||
|
" };",
|
||||||
|
"}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Den inline aspect": {
|
||||||
|
"prefix": "denaspect",
|
||||||
|
"description": "Den inline aspect",
|
||||||
|
"body": [
|
||||||
|
"(",
|
||||||
|
" { host, user, ... }: {",
|
||||||
|
" $0",
|
||||||
|
" }",
|
||||||
|
")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
local ls = require("luasnip")
|
|
||||||
local s = ls.snippet
|
|
||||||
local t = ls.text_node
|
|
||||||
local i = ls.insert_node
|
|
||||||
|
|
||||||
return {
|
|
||||||
-- Full lux module (both nixos and homeManager)
|
|
||||||
s("luxmod", {
|
|
||||||
t({
|
|
||||||
"{ inputs, ... }:",
|
|
||||||
"{",
|
|
||||||
" lux."
|
|
||||||
}),
|
|
||||||
i(1, "moduleName"),
|
|
||||||
t({
|
|
||||||
" = {",
|
|
||||||
" nixos = { config, lib, pkgs, ... }: {",
|
|
||||||
" "
|
|
||||||
}),
|
|
||||||
i(2),
|
|
||||||
t({
|
|
||||||
"",
|
|
||||||
" };",
|
|
||||||
"",
|
|
||||||
" homeManager = { config, lib, pkgs, ... }: {",
|
|
||||||
" "
|
|
||||||
}),
|
|
||||||
i(3),
|
|
||||||
t({
|
|
||||||
"",
|
|
||||||
" };",
|
|
||||||
" };",
|
|
||||||
"}",
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
-- lux nixos only module
|
|
||||||
s("luxnixos", {
|
|
||||||
t({
|
|
||||||
"{ inputs, ... }:",
|
|
||||||
"{",
|
|
||||||
" lux."
|
|
||||||
}),
|
|
||||||
i(1, "moduleName"),
|
|
||||||
t({
|
|
||||||
".nixos = { config, lib, pkgs, ... }: {",
|
|
||||||
" "
|
|
||||||
}),
|
|
||||||
i(0),
|
|
||||||
t({
|
|
||||||
"",
|
|
||||||
" };",
|
|
||||||
"}",
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
-- lux homeManager only module
|
|
||||||
s("luxhm", {
|
|
||||||
t({
|
|
||||||
"{ inputs, ... }:",
|
|
||||||
"{",
|
|
||||||
" lux."
|
|
||||||
}),
|
|
||||||
i(1, "moduleName"),
|
|
||||||
t({
|
|
||||||
".homeManager = { config, lib, pkgs, ... }: {",
|
|
||||||
" "
|
|
||||||
}),
|
|
||||||
i(0),
|
|
||||||
t({
|
|
||||||
"",
|
|
||||||
" };",
|
|
||||||
"}",
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
|
|
||||||
-- den inline aspect
|
|
||||||
s("denaspect", {
|
|
||||||
t({
|
|
||||||
"(",
|
|
||||||
" { host, user, ... }: {",
|
|
||||||
" "
|
|
||||||
}),
|
|
||||||
i(0),
|
|
||||||
t({
|
|
||||||
"",
|
|
||||||
" }",
|
|
||||||
")"
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "lux-nix-snippets",
|
||||||
|
"contributes": {
|
||||||
|
"snippets": [
|
||||||
|
{
|
||||||
|
"language": "nix",
|
||||||
|
"path": "./nix.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,10 @@ in
|
|||||||
networking.networkmanager.enable = true;
|
networking.networkmanager.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.nixos.openvpn = {
|
flake.modules.nixos.openvpn =
|
||||||
programs.openvpn3.enable = true;
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
networking.networkmanager.plugins = [ pkgs.networkmanager-openvpn ];
|
||||||
services.resolved.enable = true;
|
services.resolved.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,251 +0,0 @@
|
|||||||
{
|
|
||||||
browser,
|
|
||||||
launcher,
|
|
||||||
shortcutCommands,
|
|
||||||
terminal,
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
# Application and utility launchers
|
|
||||||
"Mod+Return" = {
|
|
||||||
spawn = terminal;
|
|
||||||
_props.hotkey-overlay-title = "Terminal";
|
|
||||||
};
|
|
||||||
"Mod+B" = {
|
|
||||||
spawn = browser;
|
|
||||||
_props.hotkey-overlay-title = "Browser";
|
|
||||||
};
|
|
||||||
"Mod+Space" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = launcher;
|
|
||||||
_props.hotkey-overlay-title = "App Launcher";
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+Return" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = shortcutCommands.nixosTerminal;
|
|
||||||
_props.hotkey-overlay-title = "NixOS Config Terminal";
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+Shift+Return" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = shortcutCommands.nixosSwitch;
|
|
||||||
_props.hotkey-overlay-title = "NixOS Switch";
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+E" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = shortcutCommands.editSecrets;
|
|
||||||
_props.hotkey-overlay-title = "Edit Secrets";
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+C" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = shortcutCommands.pickColor;
|
|
||||||
_props.hotkey-overlay-title = "Pick Color";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Hardware media, volume, and brightness keys
|
|
||||||
"XF86AudioPlay" = {
|
|
||||||
spawn-sh = "playerctl play-pause";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioStop" = {
|
|
||||||
spawn-sh = "playerctl stop";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioPrev" = {
|
|
||||||
spawn-sh = "playerctl previous";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioNext" = {
|
|
||||||
spawn-sh = "playerctl next";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioRaiseVolume" = {
|
|
||||||
spawn-sh = "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioLowerVolume" = {
|
|
||||||
spawn-sh = "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioMute" = {
|
|
||||||
spawn-sh = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86AudioMicMute" = {
|
|
||||||
spawn-sh = "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86MonBrightnessUp" = {
|
|
||||||
spawn-sh = "brightnessctl s 5%+";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
"XF86MonBrightnessDown" = {
|
|
||||||
spawn-sh = "brightnessctl s 5%-";
|
|
||||||
_props.allow-when-locked = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Screenshots
|
|
||||||
"Mod+S".screenshot = { };
|
|
||||||
"Mod+Ctrl+S".screenshot-screen = { };
|
|
||||||
"Mod+Alt+S".screenshot-window = { };
|
|
||||||
|
|
||||||
# Session and compositor controls
|
|
||||||
"Mod+Shift+Slash".show-hotkey-overlay = { };
|
|
||||||
"Mod+Escape" = {
|
|
||||||
toggle-keyboard-shortcuts-inhibit = { };
|
|
||||||
_props.allow-inhibiting = false;
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+L" = {
|
|
||||||
spawn-sh = "loginctl lock-session";
|
|
||||||
_props.hotkey-overlay-title = "Lock Screen";
|
|
||||||
};
|
|
||||||
"Mod+Ctrl+Shift+Q".quit = { };
|
|
||||||
"Ctrl+Alt+Delete".quit = { };
|
|
||||||
"Mod+Shift+P".power-off-monitors = { };
|
|
||||||
|
|
||||||
# Overview and window close
|
|
||||||
"Mod+O" = {
|
|
||||||
toggle-overview = { };
|
|
||||||
_props.repeat = false;
|
|
||||||
};
|
|
||||||
"Mod+Q" = {
|
|
||||||
close-window = { };
|
|
||||||
_props.repeat = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Keyboard focus movement
|
|
||||||
"Mod+Left".focus-column-or-monitor-left = { };
|
|
||||||
"Mod+Down".focus-window-down = { };
|
|
||||||
"Mod+Up".focus-window-up = { };
|
|
||||||
"Mod+Right".focus-column-or-monitor-right = { };
|
|
||||||
"Mod+H".focus-column-or-monitor-left = { };
|
|
||||||
"Mod+J".focus-window-down = { };
|
|
||||||
"Mod+K".focus-window-up = { };
|
|
||||||
"Mod+L".focus-column-or-monitor-right = { };
|
|
||||||
|
|
||||||
# Keyboard window and column movement
|
|
||||||
"Mod+Alt+Left".move-column-left-or-to-monitor-left = { };
|
|
||||||
"Mod+Alt+Down".move-window-down-or-to-workspace-down = { };
|
|
||||||
"Mod+Alt+Up".move-window-up-or-to-workspace-up = { };
|
|
||||||
"Mod+Alt+Right".move-column-right-or-to-monitor-right = { };
|
|
||||||
"Mod+Alt+H".move-column-left-or-to-monitor-left = { };
|
|
||||||
"Mod+Alt+J".move-window-down-or-to-workspace-down = { };
|
|
||||||
"Mod+Alt+K".move-window-up-or-to-workspace-up = { };
|
|
||||||
"Mod+Alt+L".move-column-right-or-to-monitor-right = { };
|
|
||||||
|
|
||||||
# First and last column navigation
|
|
||||||
"Mod+Home".focus-column-first = { };
|
|
||||||
"Mod+End".focus-column-last = { };
|
|
||||||
"Mod+Alt+Home".move-column-to-first = { };
|
|
||||||
"Mod+Alt+End".move-column-to-last = { };
|
|
||||||
|
|
||||||
# Move focus, columns, and workspaces across monitors
|
|
||||||
"Mod+Tab".focus-monitor-next = { };
|
|
||||||
"Mod+A" = {
|
|
||||||
_props.repeat = false;
|
|
||||||
spawn = shortcutCommands.swapMonitorWorkspaces;
|
|
||||||
_props.hotkey-overlay-title = "Swap Monitor Workspaces";
|
|
||||||
};
|
|
||||||
"Mod+Alt+Tab".move-column-to-monitor-next = { };
|
|
||||||
"Mod+Shift+Tab".move-workspace-to-monitor-next = { };
|
|
||||||
|
|
||||||
"Mod+Shift+Left".focus-monitor-left = { };
|
|
||||||
"Mod+Shift+Down".focus-monitor-down = { };
|
|
||||||
"Mod+Shift+Up".focus-monitor-up = { };
|
|
||||||
"Mod+Shift+Right".focus-monitor-right = { };
|
|
||||||
|
|
||||||
# Linear workspace navigation
|
|
||||||
"Mod+Page_Down".focus-workspace-down = { };
|
|
||||||
"Mod+Page_Up".focus-workspace-up = { };
|
|
||||||
"Mod+U".focus-workspace-down = { };
|
|
||||||
"Mod+I".focus-workspace-up = { };
|
|
||||||
|
|
||||||
# Move columns between adjacent workspaces
|
|
||||||
"Mod+Alt+Page_Down".move-column-to-workspace-down = { };
|
|
||||||
"Mod+Alt+Page_Up".move-column-to-workspace-up = { };
|
|
||||||
"Mod+Alt+U".move-column-to-workspace-down = { };
|
|
||||||
"Mod+Alt+I".move-column-to-workspace-up = { };
|
|
||||||
|
|
||||||
# Reorder workspaces
|
|
||||||
"Mod+Shift+Page_Down".move-workspace-down = { };
|
|
||||||
"Mod+Shift+Page_Up".move-workspace-up = { };
|
|
||||||
"Mod+Shift+U".move-workspace-down = { };
|
|
||||||
"Mod+Shift+I".move-workspace-up = { };
|
|
||||||
|
|
||||||
# Mouse workspace navigation
|
|
||||||
"Mod+WheelScrollDown" = {
|
|
||||||
focus-workspace-down = { };
|
|
||||||
_props.cooldown-ms = 150;
|
|
||||||
};
|
|
||||||
"Mod+WheelScrollUp" = {
|
|
||||||
focus-workspace-up = { };
|
|
||||||
_props.cooldown-ms = 150;
|
|
||||||
};
|
|
||||||
"Mod+Alt+WheelScrollDown" = {
|
|
||||||
move-column-to-workspace-down = { };
|
|
||||||
_props.cooldown-ms = 150;
|
|
||||||
};
|
|
||||||
"Mod+Alt+WheelScrollUp" = {
|
|
||||||
move-column-to-workspace-up = { };
|
|
||||||
_props.cooldown-ms = 150;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Mouse column navigation and movement
|
|
||||||
"Mod+WheelScrollRight".focus-column-or-monitor-right = { };
|
|
||||||
"Mod+WheelScrollLeft".focus-column-or-monitor-left = { };
|
|
||||||
"Mod+Alt+WheelScrollRight".move-column-right-or-to-monitor-right = { };
|
|
||||||
"Mod+Alt+WheelScrollLeft".move-column-left-or-to-monitor-left = { };
|
|
||||||
"Mod+Shift+WheelScrollDown".focus-column-or-monitor-right = { };
|
|
||||||
"Mod+Shift+WheelScrollUp".focus-column-or-monitor-left = { };
|
|
||||||
|
|
||||||
# Direct workspace focus
|
|
||||||
"Mod+1".focus-workspace = 1;
|
|
||||||
"Mod+2".focus-workspace = 2;
|
|
||||||
"Mod+3".focus-workspace = 3;
|
|
||||||
"Mod+4".focus-workspace = 4;
|
|
||||||
"Mod+5".focus-workspace = 5;
|
|
||||||
"Mod+6".focus-workspace = 6;
|
|
||||||
"Mod+7".focus-workspace = 7;
|
|
||||||
"Mod+8".focus-workspace = 8;
|
|
||||||
"Mod+9".focus-workspace = 9;
|
|
||||||
|
|
||||||
# Move columns to numbered workspaces
|
|
||||||
"Mod+Ctrl+1".move-column-to-workspace = 1;
|
|
||||||
"Mod+Ctrl+2".move-column-to-workspace = 2;
|
|
||||||
"Mod+Ctrl+3".move-column-to-workspace = 3;
|
|
||||||
"Mod+Ctrl+4".move-column-to-workspace = 4;
|
|
||||||
"Mod+Ctrl+5".move-column-to-workspace = 5;
|
|
||||||
"Mod+Ctrl+6".move-column-to-workspace = 6;
|
|
||||||
"Mod+Ctrl+7".move-column-to-workspace = 7;
|
|
||||||
"Mod+Ctrl+8".move-column-to-workspace = 8;
|
|
||||||
"Mod+Ctrl+9".move-column-to-workspace = 9;
|
|
||||||
|
|
||||||
# Add and remove windows from columns
|
|
||||||
"Mod+BracketLeft".consume-or-expel-window-left = { };
|
|
||||||
"Mod+BracketRight".consume-or-expel-window-right = { };
|
|
||||||
"Mod+Comma".consume-window-into-column = { };
|
|
||||||
"Mod+Period".expel-window-from-column = { };
|
|
||||||
|
|
||||||
# Column and window presentation modes
|
|
||||||
"Mod+R".switch-preset-column-width = { };
|
|
||||||
"Mod+Shift+R".switch-preset-column-width-back = { };
|
|
||||||
"Mod+Ctrl+R".reset-window-height = { };
|
|
||||||
"Mod+Ctrl+Shift+R".switch-preset-window-height = { };
|
|
||||||
"Mod+F".maximize-column = { };
|
|
||||||
"Mod+Shift+F".fullscreen-window = { };
|
|
||||||
"Mod+M".maximize-window-to-edges = { };
|
|
||||||
"Mod+C".center-column = { };
|
|
||||||
|
|
||||||
# Manual column width and window height adjustments
|
|
||||||
"Mod+Shift+H".set-column-width = "-10%";
|
|
||||||
"Mod+Shift+L".set-column-width = "+10%";
|
|
||||||
"Mod+Shift+J".set-window-height = "+10%";
|
|
||||||
"Mod+Shift+K".set-window-height = "-10%";
|
|
||||||
"Mod+Minus".set-column-width = "-10%";
|
|
||||||
"Mod+Equal".set-column-width = "+10%";
|
|
||||||
"Mod+Shift+Minus".set-window-height = "-10%";
|
|
||||||
"Mod+Shift+Equal".set-window-height = "+10%";
|
|
||||||
|
|
||||||
# Floating and tabbed layout toggles
|
|
||||||
"Mod+Alt+V".toggle-window-floating = { };
|
|
||||||
"Mod+Shift+V".switch-focus-between-floating-and-tiling = { };
|
|
||||||
"Mod+W".toggle-column-tabbed-display = { };
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
nixosConfigDir = config.facts.account.nixosConfigurationPath;
|
|
||||||
mkTerminalScript =
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
workdir ? nixosConfigDir,
|
|
||||||
command ? null,
|
|
||||||
runtimeInputs ? [ ],
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
args = lib.optionals (command != null) [
|
|
||||||
"--"
|
|
||||||
"${pkgs.bash}/bin/bash"
|
|
||||||
"-lc"
|
|
||||||
command
|
|
||||||
];
|
|
||||||
argString = lib.concatMapStringsSep " " lib.escapeShellArg args;
|
|
||||||
in
|
|
||||||
pkgs.writeShellApplication {
|
|
||||||
inherit name runtimeInputs;
|
|
||||||
checkPhase = "";
|
|
||||||
text = lib.concatStringsSep "\n" [
|
|
||||||
"# shellcheck disable=SC2016"
|
|
||||||
"cd ${lib.escapeShellArg workdir}"
|
|
||||||
"exec ${lib.escapeShellArgs config.desktop.commands.terminal} ${argString}"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
rec {
|
|
||||||
scripts = {
|
|
||||||
nixosTerminal = mkTerminalScript {
|
|
||||||
name = "niri-shortcut-nixos-terminal";
|
|
||||||
};
|
|
||||||
|
|
||||||
nixosSwitch = mkTerminalScript {
|
|
||||||
name = "niri-shortcut-nixos-switch";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.nh
|
|
||||||
];
|
|
||||||
command = ''
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
log_dir="''${XDG_STATE_HOME:-$HOME/.local/state}/nixos-switch"
|
|
||||||
mkdir -p "$log_dir"
|
|
||||||
log="$log_dir/$(date +%Y%m%d-%H%M%S).log"
|
|
||||||
status_file="$(mktemp)"
|
|
||||||
|
|
||||||
printf 'Running nh os switch in %s\n\n' ${lib.escapeShellArg nixosConfigDir}
|
|
||||||
(
|
|
||||||
cd ${lib.escapeShellArg nixosConfigDir}
|
|
||||||
nh os switch
|
|
||||||
printf '%s' "$?" > "$status_file"
|
|
||||||
) 2>&1 | tee "$log"
|
|
||||||
|
|
||||||
status="$(cat "$status_file")"
|
|
||||||
rm -f "$status_file"
|
|
||||||
|
|
||||||
printf '\nLog: %s\n' "$log"
|
|
||||||
if [ "$status" -eq 0 ]; then
|
|
||||||
printf 'NixOS switch completed successfully.\n'
|
|
||||||
else
|
|
||||||
printf 'NixOS switch failed with exit code %s.\n' "$status"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf 'Press Enter to close...'
|
|
||||||
read -r _
|
|
||||||
exit "$status"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
editSecrets = mkTerminalScript {
|
|
||||||
name = "niri-shortcut-edit-secrets";
|
|
||||||
runtimeInputs = [ pkgs.sops ];
|
|
||||||
command = ''
|
|
||||||
sops edit ${lib.escapeShellArg "${nixosConfigDir}/modules/secrets/secrets.yaml"}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
pickColor = pkgs.writeShellApplication {
|
|
||||||
name = "niri-shortcut-pick-color";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.libnotify
|
|
||||||
pkgs.niri
|
|
||||||
pkgs.wl-clipboard
|
|
||||||
];
|
|
||||||
text = ''
|
|
||||||
color="$(niri msg pick-color)"
|
|
||||||
printf '%s' "$color" | wl-copy
|
|
||||||
notify-send 'Color picked' "$color"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
swapMonitorWorkspaces = pkgs.writeShellApplication {
|
|
||||||
name = "niri-shortcut-swap-monitor-workspaces";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.jq
|
|
||||||
pkgs.niri
|
|
||||||
];
|
|
||||||
text = ''
|
|
||||||
focused_output="$(niri msg --json focused-output | jq -r '.name // empty')"
|
|
||||||
if [ -z "$focused_output" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
niri msg action focus-monitor-next
|
|
||||||
|
|
||||||
target_output="$(niri msg --json focused-output | jq -r '.name // empty')"
|
|
||||||
if [ -z "$target_output" ] || [ "$target_output" = "$focused_output" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
target_workspace_idx="$(
|
|
||||||
niri msg --json workspaces \
|
|
||||||
| jq -r --arg output "$target_output" '
|
|
||||||
first(.[] | select(.output == $output and .is_active) | .idx) // empty
|
|
||||||
'
|
|
||||||
)"
|
|
||||||
if [ -z "$target_workspace_idx" ]; then
|
|
||||||
niri msg action focus-monitor "$focused_output"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
niri msg action focus-monitor "$focused_output"
|
|
||||||
niri msg action move-workspace-to-monitor "$target_output"
|
|
||||||
niri msg action focus-workspace "$target_workspace_idx"
|
|
||||||
niri msg action move-workspace-to-monitor "$focused_output"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
commands = lib.mapAttrs (_: lib.getExe) scripts;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
{
|
||||||
|
browser,
|
||||||
|
handy,
|
||||||
|
launcher,
|
||||||
|
swapMonitorWorkspaces,
|
||||||
|
terminal,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
spawnLocked = command: {
|
||||||
|
spawn-sh = command;
|
||||||
|
_props.allow-when-locked = true;
|
||||||
|
};
|
||||||
|
withCooldown = action: {
|
||||||
|
${action} = { };
|
||||||
|
_props.cooldown-ms = 150;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Application and utility launchers
|
||||||
|
"Mod+Return" = {
|
||||||
|
spawn = terminal;
|
||||||
|
_props.hotkey-overlay-title = "Terminal";
|
||||||
|
};
|
||||||
|
"Mod+B" = {
|
||||||
|
spawn = browser;
|
||||||
|
_props.hotkey-overlay-title = "Browser";
|
||||||
|
};
|
||||||
|
"Mod+Shift+N" = {
|
||||||
|
spawn-sh = "rename-workspace";
|
||||||
|
_props.hotkey-overlay-title = "Name workspace (empty clears)";
|
||||||
|
};
|
||||||
|
"Mod+N" = {
|
||||||
|
spawn-sh = "open-notes";
|
||||||
|
_props.hotkey-overlay-title = "Find or create notes";
|
||||||
|
};
|
||||||
|
"Mod+Space" = {
|
||||||
|
_props.repeat = false;
|
||||||
|
spawn = launcher;
|
||||||
|
_props.hotkey-overlay-title = "App Launcher";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Dictation
|
||||||
|
"Mod+D" = {
|
||||||
|
spawn = [
|
||||||
|
"systemctl"
|
||||||
|
"--user"
|
||||||
|
"start"
|
||||||
|
"handy-ptt.service"
|
||||||
|
];
|
||||||
|
_props.repeat = false;
|
||||||
|
_props.hotkey-overlay-title = "Hold to Dictate";
|
||||||
|
};
|
||||||
|
"Mod+Shift+D" = {
|
||||||
|
spawn-sh = "systemctl --user stop handy-ptt.service && ${handy} --toggle-transcription";
|
||||||
|
_props.repeat = false;
|
||||||
|
_props.hotkey-overlay-title = "Toggle Dictation";
|
||||||
|
};
|
||||||
|
"Mod+Ctrl+D" = {
|
||||||
|
spawn-sh = "systemctl --user stop handy-ptt.service && ${handy} --cancel";
|
||||||
|
_props.repeat = false;
|
||||||
|
_props.hotkey-overlay-title = "Cancel Dictation";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Hardware media, volume, and brightness keys
|
||||||
|
"XF86AudioPlay" = spawnLocked "playerctl play-pause";
|
||||||
|
"XF86AudioStop" = spawnLocked "playerctl stop";
|
||||||
|
"XF86AudioPrev" = spawnLocked "playerctl previous";
|
||||||
|
"XF86AudioNext" = spawnLocked "playerctl next";
|
||||||
|
"XF86AudioRaiseVolume" = spawnLocked "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
|
||||||
|
"XF86AudioLowerVolume" = spawnLocked "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
|
||||||
|
"XF86AudioMute" = spawnLocked "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
||||||
|
"XF86AudioMicMute" = spawnLocked "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
|
||||||
|
"XF86MonBrightnessUp" = spawnLocked "brightnessctl s 5%+";
|
||||||
|
"XF86MonBrightnessDown" = spawnLocked "brightnessctl s 5%-";
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
"Mod+S".screenshot = { };
|
||||||
|
|
||||||
|
# Session and compositor controls
|
||||||
|
"Mod+Escape" = {
|
||||||
|
toggle-keyboard-shortcuts-inhibit = { };
|
||||||
|
_props.allow-inhibiting = false;
|
||||||
|
};
|
||||||
|
"Mod+Ctrl+L" = {
|
||||||
|
spawn-sh = "loginctl lock-session";
|
||||||
|
_props.hotkey-overlay-title = "Lock Screen";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Overview and window close
|
||||||
|
"Mod+O" = {
|
||||||
|
toggle-overview = { };
|
||||||
|
_props.repeat = false;
|
||||||
|
};
|
||||||
|
"Mod+Q" = {
|
||||||
|
close-window = { };
|
||||||
|
_props.repeat = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Keyboard focus movement
|
||||||
|
"Mod+H".focus-column-or-monitor-left = { };
|
||||||
|
"Mod+J".focus-window-down = { };
|
||||||
|
"Mod+K".focus-window-up = { };
|
||||||
|
"Mod+L".focus-column-or-monitor-right = { };
|
||||||
|
|
||||||
|
# Keyboard window and column movement
|
||||||
|
"Mod+Alt+H".move-column-left-or-to-monitor-left = { };
|
||||||
|
"Mod+Alt+J".move-window-down-or-to-workspace-down = { };
|
||||||
|
"Mod+Alt+K".move-window-up-or-to-workspace-up = { };
|
||||||
|
"Mod+Alt+L".move-column-right-or-to-monitor-right = { };
|
||||||
|
|
||||||
|
# Move focus, columns, and workspaces across monitors
|
||||||
|
"Mod+Tab".focus-monitor-next = { };
|
||||||
|
"Mod+A" = {
|
||||||
|
_props.repeat = false;
|
||||||
|
spawn = swapMonitorWorkspaces;
|
||||||
|
_props.hotkey-overlay-title = "Swap Monitor Workspaces";
|
||||||
|
};
|
||||||
|
"Mod+Alt+Tab".move-column-to-monitor-next = { };
|
||||||
|
"Mod+Shift+Tab".move-workspace-to-monitor-next = { };
|
||||||
|
|
||||||
|
# Linear workspace navigation
|
||||||
|
"Mod+U".focus-workspace-down = { };
|
||||||
|
"Mod+I".focus-workspace-up = { };
|
||||||
|
|
||||||
|
# Move columns between adjacent workspaces
|
||||||
|
"Mod+Alt+U".move-column-to-workspace-down = { };
|
||||||
|
"Mod+Alt+I".move-column-to-workspace-up = { };
|
||||||
|
|
||||||
|
# Reorder workspaces
|
||||||
|
"Mod+Shift+U".move-workspace-down = { };
|
||||||
|
"Mod+Shift+I".move-workspace-up = { };
|
||||||
|
|
||||||
|
# Mouse workspace navigation
|
||||||
|
"Mod+WheelScrollDown" = withCooldown "focus-workspace-down";
|
||||||
|
"Mod+WheelScrollUp" = withCooldown "focus-workspace-up";
|
||||||
|
"Mod+Alt+WheelScrollDown" = withCooldown "move-column-to-workspace-down";
|
||||||
|
"Mod+Alt+WheelScrollUp" = withCooldown "move-column-to-workspace-up";
|
||||||
|
|
||||||
|
# Mouse column navigation and movement
|
||||||
|
"Mod+WheelScrollRight".focus-column-or-monitor-right = { };
|
||||||
|
"Mod+WheelScrollLeft".focus-column-or-monitor-left = { };
|
||||||
|
"Mod+Alt+WheelScrollRight".move-column-right-or-to-monitor-right = { };
|
||||||
|
"Mod+Alt+WheelScrollLeft".move-column-left-or-to-monitor-left = { };
|
||||||
|
"Mod+Shift+WheelScrollDown".focus-column-or-monitor-right = { };
|
||||||
|
"Mod+Shift+WheelScrollUp".focus-column-or-monitor-left = { };
|
||||||
|
|
||||||
|
# Add and remove windows from columns
|
||||||
|
"Mod+BracketLeft".consume-or-expel-window-left = { };
|
||||||
|
"Mod+BracketRight".consume-or-expel-window-right = { };
|
||||||
|
"Mod+Comma".consume-window-into-column = { };
|
||||||
|
"Mod+Period".expel-window-from-column = { };
|
||||||
|
|
||||||
|
# Column and window presentation modes
|
||||||
|
"Mod+R".switch-preset-column-width = { };
|
||||||
|
"Mod+F".maximize-column = { };
|
||||||
|
"Mod+Shift+F".fullscreen-window = { };
|
||||||
|
|
||||||
|
# Manual column width and window height adjustments
|
||||||
|
"Mod+Shift+H".set-column-width = "-10%";
|
||||||
|
"Mod+Shift+L".set-column-width = "+10%";
|
||||||
|
"Mod+Shift+J".set-window-height = "+10%";
|
||||||
|
"Mod+Shift+K".set-window-height = "-10%";
|
||||||
|
|
||||||
|
# Floating and tabbed layout toggles
|
||||||
|
"Mod+Alt+V".toggle-window-floating = { };
|
||||||
|
"Mod+Shift+V".switch-focus-between-floating-and-tiling = { };
|
||||||
|
"Mod+W".toggle-column-tabbed-display = { };
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
{ pkgs }:
|
||||||
|
pkgs.writeScriptBin "niri-shortcut-swap-monitor-workspaces" ''
|
||||||
|
#!${pkgs.python3}/bin/python3
|
||||||
|
import fcntl
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def disposable(workspace):
|
||||||
|
return workspace["active_window_id"] is None and workspace["name"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def swap(connection):
|
||||||
|
with connection.makefile("rw", encoding="utf-8") as stream:
|
||||||
|
def request(message):
|
||||||
|
stream.write(json.dumps(message) + "\n")
|
||||||
|
stream.flush()
|
||||||
|
reply = json.loads(stream.readline())
|
||||||
|
if "Err" in reply:
|
||||||
|
raise RuntimeError(reply["Err"])
|
||||||
|
return reply["Ok"]
|
||||||
|
|
||||||
|
def action(name, **arguments):
|
||||||
|
request({"Action": {name: arguments}})
|
||||||
|
|
||||||
|
workspaces = request("Workspaces")["Workspaces"]
|
||||||
|
source = next((ws for ws in workspaces if ws["is_focused"]), None)
|
||||||
|
active = {
|
||||||
|
ws["output"]: ws for ws in workspaces
|
||||||
|
if ws["output"] is not None and ws["is_active"]
|
||||||
|
}
|
||||||
|
if source is None or source["output"] not in active or len(active) < 2:
|
||||||
|
return
|
||||||
|
outputs = sorted(active)
|
||||||
|
target = active[outputs[(outputs.index(source["output"]) + 1) % len(outputs)]]
|
||||||
|
if source["active_window_id"] is None and target["active_window_id"] is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# IDs survive moves; numeric workspace positions do not.
|
||||||
|
moves = ((source, target), (target, source))
|
||||||
|
for workspace, destination in moves:
|
||||||
|
action(
|
||||||
|
"MoveWorkspaceToMonitor",
|
||||||
|
reference={"Id": workspace["id"]},
|
||||||
|
output=destination["output"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Activate the original monitor last to keep keyboard focus there.
|
||||||
|
for workspace, destination in moves:
|
||||||
|
current = request("Workspaces")["Workspaces"]
|
||||||
|
arriving = next((
|
||||||
|
ws for ws in current
|
||||||
|
if ws["id"] == workspace["id"] and ws["output"] == destination["output"]
|
||||||
|
), None)
|
||||||
|
if arriving is None and disposable(workspace):
|
||||||
|
# Niri may discard an unnamed empty workspace during the swap.
|
||||||
|
arriving = max((
|
||||||
|
ws for ws in current
|
||||||
|
if ws["output"] == destination["output"] and disposable(ws)
|
||||||
|
), key=lambda ws: ws["idx"], default=None)
|
||||||
|
if arriving is None:
|
||||||
|
raise RuntimeError("workspace or monitor disappeared during swap")
|
||||||
|
reference = {"Id": arriving["id"]}
|
||||||
|
# Focus empty workspaces before reordering so Niri retains them.
|
||||||
|
action("FocusWorkspace", reference=reference)
|
||||||
|
action("MoveWorkspaceToIndex", reference=reference, index=destination["idx"])
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
lock_path = os.path.join(os.environ["XDG_RUNTIME_DIR"], "niri-swap-workspaces.lock")
|
||||||
|
with open(lock_path, "a") as lock:
|
||||||
|
try:
|
||||||
|
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
except BlockingIOError:
|
||||||
|
return
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as connection:
|
||||||
|
connection.settimeout(5)
|
||||||
|
connection.connect(os.environ["NIRI_SOCKET"])
|
||||||
|
swap(connection)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except (OSError, ValueError, KeyError, RuntimeError) as error:
|
||||||
|
sys.exit(f"niri workspace swap: {error}")
|
||||||
|
''
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
kanagawa = import ../_kanagawa.nix;
|
|
||||||
hmModules = config.flake.modules.homeManager;
|
hmModules = config.flake.modules.homeManager;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -14,10 +13,6 @@ in
|
|||||||
|
|
||||||
programs.niri.enable = true;
|
programs.niri.enable = true;
|
||||||
|
|
||||||
services = {
|
|
||||||
gvfs.enable = true;
|
|
||||||
udisks2.enable = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.niri =
|
flake.modules.homeManager.niri =
|
||||||
@@ -28,13 +23,21 @@ in
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
palette = kanagawa.palette;
|
swapMonitorWorkspaces = import ./_support/shortcuts.nix { inherit pkgs; };
|
||||||
shortcuts = import ./_shortcuts.nix {
|
renameWorkspace = pkgs.writeShellApplication {
|
||||||
inherit
|
name = "rename-workspace";
|
||||||
config
|
runtimeInputs = [
|
||||||
lib
|
pkgs.niri
|
||||||
pkgs
|
pkgs.zenity
|
||||||
;
|
];
|
||||||
|
text = ''
|
||||||
|
name=$(zenity --entry --title="Workspace name" --text="Name this workspace (empty clears its name):") || exit 0
|
||||||
|
if [[ -n "$name" ]]; then
|
||||||
|
niri msg action set-workspace-name "$name"
|
||||||
|
else
|
||||||
|
niri msg action unset-workspace-name
|
||||||
|
fi
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
machine = config.facts.machine;
|
machine = config.facts.machine;
|
||||||
in
|
in
|
||||||
@@ -47,12 +50,14 @@ in
|
|||||||
playerctl
|
playerctl
|
||||||
brightnessctl
|
brightnessctl
|
||||||
wireplumber
|
wireplumber
|
||||||
|
wtype
|
||||||
]
|
]
|
||||||
++ lib.attrValues shortcuts.scripts;
|
++ [
|
||||||
|
swapMonitorWorkspaces
|
||||||
|
renameWorkspace
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark";
|
|
||||||
|
|
||||||
wayland.windowManager.niri = {
|
wayland.windowManager.niri = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
@@ -109,12 +114,7 @@ in
|
|||||||
focus-ring.off = { };
|
focus-ring.off = { };
|
||||||
default-column-width.proportion = 1. / 2.;
|
default-column-width.proportion = 1. / 2.;
|
||||||
|
|
||||||
border = {
|
border.width = 3;
|
||||||
width = 3;
|
|
||||||
active-color = palette.accents.blue;
|
|
||||||
inactive-color = palette.subtleBorder;
|
|
||||||
urgent-color = palette.accents.red;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
debug.honor-xdg-activation-with-invalid-serial = true;
|
debug.honor-xdg-activation-with-invalid-serial = true;
|
||||||
@@ -135,13 +135,14 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
binds = import ./_bindings.nix {
|
binds = import ./_support/bindings.nix {
|
||||||
inherit (config.desktop.commands)
|
inherit (config.desktop.commands)
|
||||||
browser
|
browser
|
||||||
launcher
|
launcher
|
||||||
terminal
|
terminal
|
||||||
;
|
;
|
||||||
shortcutCommands = shortcuts.commands;
|
swapMonitorWorkspaces = lib.getExe swapMonitorWorkspaces;
|
||||||
|
handy = lib.getExe pkgs.handy;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,20 +3,25 @@ let
|
|||||||
account = config.facts.account;
|
account = config.facts.account;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.nixos.nix =
|
flake.modules.nixos.nix = {
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
gc.automatic = true;
|
gc.automatic = true;
|
||||||
optimise.automatic = true;
|
optimise.automatic = true;
|
||||||
registry.nixpkgs.flake = inputs.nixpkgs;
|
|
||||||
channel.enable = false;
|
channel.enable = false;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
trusted-users = [ "@wheel" ];
|
trusted-users = [ "@wheel" ];
|
||||||
use-xdg-base-directories = true;
|
use-xdg-base-directories = true;
|
||||||
|
extra-substituters = [
|
||||||
|
"https://devenv.cachix.org"
|
||||||
|
"https://noctalia.cachix.org"
|
||||||
|
];
|
||||||
|
extra-trusted-public-keys = [
|
||||||
|
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
|
||||||
|
"noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4="
|
||||||
|
];
|
||||||
|
|
||||||
experimental-features = [
|
experimental-features = [
|
||||||
"nix-command"
|
"nix-command"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.noctalia-greeter = {
|
||||||
|
services.displayManager.noctalia-greeter.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
homeDir = config.home.homeDirectory;
|
homeDir = config.home.homeDirectory;
|
||||||
wallpaper = "${homeDir}/media/images/wallpapers/green-leaves.png";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
desktop.autostart = [ [ (lib.getExe config.programs.noctalia.package) ] ];
|
desktop.autostart = [ [ (lib.getExe config.programs.noctalia.package) ] ];
|
||||||
@@ -61,17 +60,7 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
desktop_widgets = {
|
desktop_widgets.enabled = false;
|
||||||
enabled = false;
|
|
||||||
schema_version = 2;
|
|
||||||
widget_order = [ ];
|
|
||||||
grid = {
|
|
||||||
cell_size = 16;
|
|
||||||
major_interval = 4;
|
|
||||||
visible = true;
|
|
||||||
};
|
|
||||||
widget = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
location.address = "Meterik, The Netherlands";
|
location.address = "Meterik, The Netherlands";
|
||||||
|
|
||||||
@@ -95,10 +84,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
shell = {
|
shell = {
|
||||||
font_family = "Lexend";
|
|
||||||
polkit_agent = true;
|
polkit_agent = true;
|
||||||
|
# Handy provides recording feedback; omit microphone activity popups/indicators.
|
||||||
|
privacy.mic_filter_regex = ".*";
|
||||||
screen_time_enabled = true;
|
screen_time_enabled = true;
|
||||||
settings_show_advanced = true;
|
|
||||||
animation.speed = 1.5;
|
animation.speed = 1.5;
|
||||||
screen_corners = {
|
screen_corners = {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
@@ -108,9 +97,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
theme = {
|
theme = {
|
||||||
builtin = "Kanagawa";
|
|
||||||
community_palette = "Kanagawa Paper";
|
|
||||||
wallpaper_scheme = "m3-content";
|
|
||||||
templates = {
|
templates = {
|
||||||
enable_builtin_templates = false;
|
enable_builtin_templates = false;
|
||||||
enable_community_templates = false;
|
enable_community_templates = false;
|
||||||
@@ -119,15 +105,12 @@
|
|||||||
|
|
||||||
wallpaper = {
|
wallpaper = {
|
||||||
directory = "${homeDir}/media/images/wallpapers";
|
directory = "${homeDir}/media/images/wallpapers";
|
||||||
default.path = wallpaper;
|
|
||||||
last.path = wallpaper;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
weather.refresh_minutes = 15;
|
weather.refresh_minutes = 15;
|
||||||
|
|
||||||
widget = {
|
widget = {
|
||||||
bluetooth.hide_when_no_connected_device = true;
|
bluetooth.hide_when_no_connected_device = true;
|
||||||
clock.format = "{:%H:%M}";
|
|
||||||
media = {
|
media = {
|
||||||
art_size = 90.0;
|
art_size = 90.0;
|
||||||
max_length = 800;
|
max_length = 800;
|
||||||
@@ -141,7 +124,11 @@
|
|||||||
volume.show_label = false;
|
volume.show_label = false;
|
||||||
network.show_label = false;
|
network.show_label = false;
|
||||||
battery.show_label = false;
|
battery.show_label = false;
|
||||||
workspaces.show_labels = false;
|
workspaces = {
|
||||||
|
show_labels = true;
|
||||||
|
label_source = "name";
|
||||||
|
max_label_chars = 16;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,17 @@ in
|
|||||||
|
|
||||||
[tool]
|
[tool]
|
||||||
fzf-preview = "bat --plain --color=always {-1}"
|
fzf-preview = "bat --plain --color=always {-1}"
|
||||||
|
|
||||||
|
[group.daily]
|
||||||
|
paths = ["daily"]
|
||||||
|
|
||||||
|
[group.daily.note]
|
||||||
|
filename = "{{format-date now '%Y-%m-%d'}}"
|
||||||
|
extension = "md"
|
||||||
|
template = "daily.md"
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
daily = 'nvim + "$(zk new --no-input --print-path "$ZK_NOTEBOOK_DIR/daily")"'
|
||||||
'';
|
'';
|
||||||
noteTemplate = ''
|
noteTemplate = ''
|
||||||
---
|
---
|
||||||
@@ -27,49 +38,43 @@ in
|
|||||||
|
|
||||||
# {{title}}
|
# {{title}}
|
||||||
'';
|
'';
|
||||||
|
openNotes = pkgs.writeShellApplication {
|
||||||
|
name = "open-notes";
|
||||||
|
text = ''
|
||||||
|
exec ${lib.getExe config.programs.kitty.package} --detach \
|
||||||
|
--directory "${home}/sync/notes" \
|
||||||
|
nvim '+autocmd VimEnter * ++once ZkNotes'
|
||||||
|
'';
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.packages = with pkgs; [
|
home.packages = [
|
||||||
ripgrep-all
|
openNotes
|
||||||
zk
|
(pkgs.makeDesktopItem {
|
||||||
|
name = "notes";
|
||||||
|
desktopName = "Notes";
|
||||||
|
icon = "text-editor";
|
||||||
|
exec = lib.getExe openNotes;
|
||||||
|
path = "${home}/sync/notes";
|
||||||
|
terminal = false;
|
||||||
|
})
|
||||||
|
pkgs.ripgrep-all
|
||||||
|
pkgs.zk
|
||||||
];
|
];
|
||||||
|
|
||||||
home.file = {
|
home.file = {
|
||||||
"sync/notes/.zk/config.toml".text = zkConfig;
|
"sync/notes/.zk/config.toml".text = zkConfig;
|
||||||
|
"sync/notes/.zk/templates/daily.md".text = "";
|
||||||
"sync/notes/.zk/templates/default.md".text = noteTemplate;
|
"sync/notes/.zk/templates/default.md".text = noteTemplate;
|
||||||
"sync/work/notes/.zk/config.toml".text = zkConfig;
|
|
||||||
"sync/work/notes/.zk/templates/default.md".text = noteTemplate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
home.activation.createNotesLayout = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
home.activation.createNotesLayout = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
${pkgs.coreutils}/bin/mkdir -p \
|
${pkgs.coreutils}/bin/mkdir -p \
|
||||||
"${home}/sync/notes/transcripts" \
|
"${home}/sync/notes/daily" \
|
||||||
"${home}/sync/recordings/inbox" \
|
"${home}/sync/notes/meetings" \
|
||||||
"${home}/sync/recordings/processing" \
|
"${home}/sync/notes/transcripts"
|
||||||
"${home}/sync/recordings/archive" \
|
|
||||||
"${home}/sync/recordings/failed" \
|
|
||||||
"${home}/sync/work/notes/meetings" \
|
|
||||||
"${home}/sync/work/notes/transcripts" \
|
|
||||||
"${home}/sync/work/recordings/inbox" \
|
|
||||||
"${home}/sync/work/recordings/processing" \
|
|
||||||
"${home}/sync/work/recordings/archive" \
|
|
||||||
"${home}/sync/work/recordings/failed"
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
programs.helix.languages = {
|
|
||||||
language-server.zk = {
|
|
||||||
command = lib.getExe pkgs.zk;
|
|
||||||
args = [ "lsp" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
language = [
|
|
||||||
{
|
|
||||||
name = "markdown";
|
|
||||||
roots = [ ".zk" ];
|
|
||||||
language-servers = [ "zk" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.nixos.notes =
|
flake.modules.nixos.notes =
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
account = config.facts.account;
|
||||||
|
personalPublicKeys = map (machine: machine.sshKeys.personal.publicKey) (
|
||||||
|
lib.filter (machine: machine.sshKeys ? personal) (builtins.attrValues config.facts.machines)
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.modules.nixos.sudo-ssh-agent-auth = {
|
||||||
|
security.pam = {
|
||||||
|
rssh.enable = true;
|
||||||
|
services.sudo.rssh = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.nixos.openssh = {
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
AllowUsers = [ account.name ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${account.name}.openssh.authorizedKeys.keys = personalPublicKeys;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -12,16 +12,13 @@ in
|
|||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
pinentryPackage = pkgs.pinentry-gnome3;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
programs.rbw = {
|
programs.rbw = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
base_url = facts.services.vaultwarden.origin;
|
base_url = facts.services.vaultwarden.origin;
|
||||||
email = account.emails.${account.primaryEmail}.address;
|
email = account.emails.${account.primaryEmail}.address;
|
||||||
pinentry = pinentryPackage;
|
pinentry = pkgs.pinentry-gnome3;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,16 +12,19 @@ in
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
mailspring = pkgs.mailspring.override {
|
||||||
|
commandLineArgs = "--password-store=gnome-libsecret";
|
||||||
|
};
|
||||||
mkOffice365Account =
|
mkOffice365Account =
|
||||||
{
|
{
|
||||||
address,
|
address,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
enable = true;
|
|
||||||
inherit address;
|
inherit address;
|
||||||
realName = account.realName;
|
realName = account.realName;
|
||||||
userName = address;
|
userName = address;
|
||||||
@@ -40,7 +43,6 @@ in
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
enable = true;
|
|
||||||
inherit address;
|
inherit address;
|
||||||
realName = account.realName;
|
realName = account.realName;
|
||||||
userName = address;
|
userName = address;
|
||||||
@@ -49,13 +51,11 @@ in
|
|||||||
authentication = "plain";
|
authentication = "plain";
|
||||||
host = "taylor.mxrouting.net";
|
host = "taylor.mxrouting.net";
|
||||||
port = 993;
|
port = 993;
|
||||||
tls.enable = true;
|
|
||||||
};
|
};
|
||||||
smtp = {
|
smtp = {
|
||||||
authentication = "plain";
|
authentication = "plain";
|
||||||
host = "taylor.mxrouting.net";
|
host = "taylor.mxrouting.net";
|
||||||
port = 465;
|
port = 465;
|
||||||
tls.enable = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
mkEmailAccount =
|
mkEmailAccount =
|
||||||
@@ -66,6 +66,9 @@ in
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
home.packages = [ mailspring ];
|
||||||
|
xdg.mimeApps.defaultApplicationPackages = [ mailspring ];
|
||||||
|
|
||||||
programs.thunderbird = {
|
programs.thunderbird = {
|
||||||
enable = true;
|
enable = true;
|
||||||
profiles.${config.home.username} = {
|
profiles.${config.home.username} = {
|
||||||
@@ -93,9 +96,6 @@ in
|
|||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
calendarsPath = "${config.xdg.dataHome}/calendars";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
imports = [ homeModules.sops ];
|
imports = [ homeModules.sops ];
|
||||||
|
|
||||||
@@ -126,16 +126,11 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
accounts.calendar = {
|
accounts.calendar = {
|
||||||
basePath = calendarsPath;
|
basePath = "${config.xdg.dataHome}/calendars";
|
||||||
accounts.radicale = {
|
accounts.radicale = {
|
||||||
primary = true;
|
primary = true;
|
||||||
primaryCollection = "personal";
|
primaryCollection = "personal";
|
||||||
|
|
||||||
local = {
|
|
||||||
type = "filesystem";
|
|
||||||
fileExt = ".ics";
|
|
||||||
};
|
|
||||||
|
|
||||||
remote = {
|
remote = {
|
||||||
url = "${facts.services.radicale.origin}/";
|
url = "${facts.services.radicale.origin}/";
|
||||||
type = "caldav";
|
type = "caldav";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
flake.modules.nixos.plymouth =
|
flake.modules.nixos.plymouth =
|
||||||
{ pkgs, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
boot = {
|
boot = {
|
||||||
consoleLogLevel = 3;
|
consoleLogLevel = 3;
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
plymouth = {
|
plymouth = {
|
||||||
enable = true;
|
enable = true;
|
||||||
theme = "spinner_alt";
|
|
||||||
themePackages = [ pkgs.adi1090x-plymouth-themes ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
let
|
|
||||||
homeModules = config.flake.modules.homeManager;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
flake.modules.nixos.podman = {
|
flake.modules.nixos.podman =
|
||||||
home-manager.sharedModules = [ homeModules.podman ];
|
{ lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
home-manager.sharedModules = [ config.flake.modules.homeManager.podman ];
|
||||||
|
|
||||||
virtualisation.podman.enable = true;
|
virtualisation.podman.enable = true;
|
||||||
|
virtualisation.containers.containersConf.settings.engine.compose_providers = [
|
||||||
|
(lib.getExe pkgs.podman-compose)
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.podman =
|
flake.modules.homeManager.podman =
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
printing = {
|
printing = {
|
||||||
enable = true;
|
enable = true;
|
||||||
browsed.enable = true;
|
browsed.enable = true;
|
||||||
drivers = with pkgs; [
|
drivers = [ pkgs.cnijfilter2 ];
|
||||||
cnijfilter2
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
avahi = {
|
avahi = {
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
let
|
|
||||||
service = config.facts.services.radicale;
|
|
||||||
nixosModules = config.flake.modules.nixos;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
flake.modules.nixos.radicale = {
|
flake.modules.nixos.radicale = {
|
||||||
imports = [ nixosModules.caddy ];
|
imports = [ config.flake.modules.nixos.caddy ];
|
||||||
|
|
||||||
services.radicale = {
|
services.radicale = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -17,15 +13,12 @@ in
|
|||||||
htpasswd_filename = "/var/lib/radicale/users";
|
htpasswd_filename = "/var/lib/radicale/users";
|
||||||
htpasswd_encryption = "bcrypt";
|
htpasswd_encryption = "bcrypt";
|
||||||
};
|
};
|
||||||
|
|
||||||
storage.filesystem_folder = "/var/lib/radicale/collections";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.caddy.virtualHosts.${service.domain}.extraConfig = ''
|
services.caddy.virtualHosts.${config.facts.services.radicale.domain}.extraConfig = ''
|
||||||
reverse_proxy 127.0.0.1:5232 {
|
reverse_proxy 127.0.0.1:5232 {
|
||||||
header_up X-Script-Name /
|
header_up X-Script-Name /
|
||||||
header_up X-Forwarded-For {remote}
|
|
||||||
header_up X-Remote-User {http.auth.user.id}
|
header_up X-Remote-User {http.auth.user.id}
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
flake.modules.nixos.sddm =
|
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
services.displayManager.sddm = {
|
|
||||||
enable = true;
|
|
||||||
enableHidpi = true;
|
|
||||||
wayland.enable = true;
|
|
||||||
wayland.compositor = "kwin";
|
|
||||||
theme = "${
|
|
||||||
pkgs.sddm-astronaut.override { embeddedTheme = "purple_leaves"; }
|
|
||||||
}/share/sddm/themes/sddm-astronaut-theme";
|
|
||||||
extraPackages = with pkgs; [
|
|
||||||
kdePackages.qtmultimedia
|
|
||||||
];
|
|
||||||
settings.Theme = {
|
|
||||||
CursorTheme = "phinger-cursors-light";
|
|
||||||
CursorSize = 24;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{ config, ... }:
|
|
||||||
let
|
|
||||||
service = config.facts.services.actual;
|
|
||||||
nixosModules = config.flake.modules.nixos;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.actual = {
|
|
||||||
imports = [ nixosModules.caddy ];
|
|
||||||
|
|
||||||
services.actual = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = false;
|
|
||||||
settings = {
|
|
||||||
hostname = "127.0.0.1";
|
|
||||||
port = 3000;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.caddy.virtualHosts.${service.domain}.extraConfig = "reverse_proxy 127.0.0.1:3000";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
account = config.facts.account;
|
|
||||||
personalPublicKeys =
|
|
||||||
machines:
|
|
||||||
map (machine: machine.sshKeys.personal.publicKey) (
|
|
||||||
lib.filter (machine: machine.sshKeys ? personal) (builtins.attrValues machines)
|
|
||||||
);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.sudo-ssh-agent-auth = {
|
|
||||||
security.pam = {
|
|
||||||
rssh.enable = true;
|
|
||||||
services.sudo.rssh = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.nixos.openssh =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
services.openssh = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
PermitRootLogin = "no";
|
|
||||||
PasswordAuthentication = false;
|
|
||||||
AllowUsers = [ account.name ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.${account.name}.openssh.authorizedKeys.keys = personalPublicKeys config.facts.machines;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
{
|
|
||||||
flake.modules.nixos.vps-insights =
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
localAddress = "127.0.0.1";
|
|
||||||
grafanaDataDir = config.services.grafana.dataDir;
|
|
||||||
lokiDataDir = config.services.loki.dataDir;
|
|
||||||
lokiUrl = "http://${localAddress}:3100";
|
|
||||||
prometheusUrl = "http://${localAddress}:9090";
|
|
||||||
hasCaddy = config.services.caddy.enable;
|
|
||||||
hasOpenSSH = config.services.openssh.enable;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
environment = {
|
|
||||||
etc."alloy/config.alloy".text = ''
|
|
||||||
loki.relabel "journal" {
|
|
||||||
forward_to = []
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__journal__systemd_unit"]
|
|
||||||
target_label = "unit"
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__journal_syslog_identifier"]
|
|
||||||
target_label = "syslog_identifier"
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__journal_priority_keyword"]
|
|
||||||
target_label = "level"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loki.source.journal "system" {
|
|
||||||
forward_to = [loki.write.local.receiver]
|
|
||||||
relabel_rules = loki.relabel.journal.rules
|
|
||||||
max_age = "24h"
|
|
||||||
labels = {
|
|
||||||
host = "${config.networking.hostName}",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loki.write "local" {
|
|
||||||
endpoint {
|
|
||||||
url = "${lokiUrl}/loki/api/v1/push"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
systemPackages = with pkgs; [
|
|
||||||
goaccess
|
|
||||||
lynis
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
services = {
|
|
||||||
# Keep local system logs available for Loki and manual inspection.
|
|
||||||
journald.extraConfig = ''
|
|
||||||
Storage=persistent
|
|
||||||
SystemMaxUse=1G
|
|
||||||
MaxRetentionSec=30day
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Detect and block common attacks against SSH and Caddy.
|
|
||||||
crowdsec = {
|
|
||||||
enable = true;
|
|
||||||
hub.collections = [ "crowdsecurity/linux" ] ++ lib.optional hasCaddy "crowdsecurity/caddy";
|
|
||||||
localConfig.acquisitions =
|
|
||||||
lib.optional hasOpenSSH {
|
|
||||||
source = "journalctl";
|
|
||||||
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
|
|
||||||
labels.type = "syslog";
|
|
||||||
}
|
|
||||||
++ lib.optional hasCaddy {
|
|
||||||
filenames = [ "/var/log/caddy/*.log" ];
|
|
||||||
labels.type = "caddy";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
crowdsec-firewall-bouncer = {
|
|
||||||
enable = true;
|
|
||||||
registerBouncer.bouncerName = "${config.networking.hostName}-firewall-bouncer";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Keep Grafana local and separate from Actual's port.
|
|
||||||
grafana = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
analytics.reporting_enabled = false;
|
|
||||||
server = {
|
|
||||||
http_addr = localAddress;
|
|
||||||
http_port = 3002;
|
|
||||||
};
|
|
||||||
security = {
|
|
||||||
admin_password = "$__file{${grafanaDataDir}/admin-password}";
|
|
||||||
secret_key = "$__file{${grafanaDataDir}/secret-key}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
provision.datasources.settings = {
|
|
||||||
prune = true;
|
|
||||||
datasources = [
|
|
||||||
{
|
|
||||||
name = "Prometheus";
|
|
||||||
type = "prometheus";
|
|
||||||
uid = "prometheus";
|
|
||||||
url = prometheusUrl;
|
|
||||||
isDefault = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name = "Loki";
|
|
||||||
type = "loki";
|
|
||||||
uid = "loki";
|
|
||||||
url = lokiUrl;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Store local logs in Loki and feed them from journald through Alloy.
|
|
||||||
loki = {
|
|
||||||
enable = true;
|
|
||||||
configuration = {
|
|
||||||
analytics.reporting_enabled = false;
|
|
||||||
auth_enabled = false;
|
|
||||||
|
|
||||||
server = {
|
|
||||||
http_listen_address = localAddress;
|
|
||||||
http_listen_port = 3100;
|
|
||||||
grpc_listen_address = localAddress;
|
|
||||||
grpc_listen_port = 9096;
|
|
||||||
};
|
|
||||||
|
|
||||||
common = {
|
|
||||||
path_prefix = lokiDataDir;
|
|
||||||
replication_factor = 1;
|
|
||||||
instance_interface_names = [ "lo" ];
|
|
||||||
ring = {
|
|
||||||
instance_addr = localAddress;
|
|
||||||
kvstore.store = "inmemory";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
schema_config.configs = [
|
|
||||||
{
|
|
||||||
from = "2025-01-01";
|
|
||||||
store = "tsdb";
|
|
||||||
object_store = "filesystem";
|
|
||||||
schema = "v13";
|
|
||||||
index = {
|
|
||||||
prefix = "index_";
|
|
||||||
period = "24h";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
storage_config.filesystem.directory = "${lokiDataDir}/chunks";
|
|
||||||
|
|
||||||
compactor = {
|
|
||||||
working_directory = "${lokiDataDir}/compactor";
|
|
||||||
retention_enabled = true;
|
|
||||||
delete_request_store = "filesystem";
|
|
||||||
};
|
|
||||||
|
|
||||||
limits_config.retention_period = "720h";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
alloy = {
|
|
||||||
enable = true;
|
|
||||||
extraFlags = [ "--server.http.listen-addr=${localAddress}:12345" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Collect basic VPS health metrics for Grafana.
|
|
||||||
prometheus = {
|
|
||||||
enable = true;
|
|
||||||
listenAddress = localAddress;
|
|
||||||
retentionTime = "30d";
|
|
||||||
scrapeConfigs = [
|
|
||||||
{
|
|
||||||
job_name = "prometheus";
|
|
||||||
static_configs = [
|
|
||||||
{
|
|
||||||
targets = [ "${localAddress}:9090" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
{
|
|
||||||
job_name = "node";
|
|
||||||
static_configs = [
|
|
||||||
{
|
|
||||||
targets = [ "${localAddress}:9100" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
exporters.node = {
|
|
||||||
enable = true;
|
|
||||||
listenAddress = localAddress;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.grafana.preStart = ''
|
|
||||||
umask 077
|
|
||||||
|
|
||||||
if [ ! -s ${grafanaDataDir}/admin-password ]; then
|
|
||||||
${pkgs.openssl}/bin/openssl rand -base64 32 > ${grafanaDataDir}/admin-password
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -s ${grafanaDataDir}/secret-key ]; then
|
|
||||||
${pkgs.openssl}/bin/openssl rand -hex 32 > ${grafanaDataDir}/secret-key
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -7,10 +7,6 @@
|
|||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
gardenctl = "${config.home.homeDirectory}/.garden/gardenctl";
|
|
||||||
uuidgen = lib.getExe' pkgs.util-linux "uuidgen";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
STARSHIP_CACHE = "${config.xdg.cacheHome}/starship";
|
STARSHIP_CACHE = "${config.xdg.cacheHome}/starship";
|
||||||
@@ -26,7 +22,6 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
dotDir = "${config.xdg.configHome}/zsh";
|
dotDir = "${config.xdg.configHome}/zsh";
|
||||||
|
|
||||||
enableCompletion = true;
|
|
||||||
completionInit = ''
|
completionInit = ''
|
||||||
autoload -U compinit
|
autoload -U compinit
|
||||||
compinit -C
|
compinit -C
|
||||||
@@ -54,24 +49,21 @@
|
|||||||
|
|
||||||
historySubstringSearch.enable = true;
|
historySubstringSearch.enable = true;
|
||||||
|
|
||||||
history = {
|
history.path = "${config.xdg.dataHome}/zsh_history";
|
||||||
ignoreDups = true;
|
|
||||||
save = 10000;
|
|
||||||
size = 10000;
|
|
||||||
path = "${config.xdg.dataHome}/zsh_history";
|
|
||||||
};
|
|
||||||
|
|
||||||
profileExtra = lib.optionalString (config.home.sessionPath != [ ]) ''
|
|
||||||
export PATH="$PATH''${PATH:+:}${lib.concatStringsSep ":" config.home.sessionPath}"
|
|
||||||
'';
|
|
||||||
|
|
||||||
initContent = ''
|
initContent = ''
|
||||||
source <(${gardenctl} completion zsh)
|
# Terminal/window switchers show the working directory and active command.
|
||||||
if [ -z "$GCTL_SESSION_ID" ] && [ -z "$TERM_SESSION_ID" ]; then
|
autoload -Uz add-zsh-hook
|
||||||
export GCTL_SESSION_ID=$(${uuidgen})
|
function terminal-title-precmd() {
|
||||||
fi
|
print -Pn '\e]2;%m: %~\a'
|
||||||
|
}
|
||||||
eval $(${gardenctl} kubectl-env zsh)
|
function terminal-title-preexec() {
|
||||||
|
local command="''${1//[[:cntrl:]]/}"
|
||||||
|
print -Pn '\e]2;%m: %~ — '
|
||||||
|
print -rn -- "$command"$'\a'
|
||||||
|
}
|
||||||
|
add-zsh-hook precmd terminal-title-precmd
|
||||||
|
add-zsh-hook preexec terminal-title-preexec
|
||||||
|
|
||||||
bindkey -v
|
bindkey -v
|
||||||
|
|
||||||
@@ -127,19 +119,8 @@
|
|||||||
|
|
||||||
programs.starship = {
|
programs.starship = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableZshIntegration = true;
|
|
||||||
settings = {
|
settings = {
|
||||||
add_newline = true;
|
format = "$nix_shell$hostname$directory$git_branch$git_state$line_break$character";
|
||||||
|
|
||||||
format = lib.concatStrings [
|
|
||||||
"$nix_shell"
|
|
||||||
"$hostname"
|
|
||||||
"$directory"
|
|
||||||
"$git_branch"
|
|
||||||
"$git_state"
|
|
||||||
"$line_break"
|
|
||||||
"$character"
|
|
||||||
];
|
|
||||||
|
|
||||||
directory = {
|
directory = {
|
||||||
truncation_length = 99;
|
truncation_length = 99;
|
||||||
@@ -156,8 +137,6 @@
|
|||||||
format = "([$state( $progress_current/$progress_total)]($style)) ";
|
format = "([$state( $progress_current/$progress_total)]($style)) ";
|
||||||
style = "bright-black";
|
style = "bright-black";
|
||||||
};
|
};
|
||||||
|
|
||||||
line_break.disabled = false;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,15 +145,9 @@
|
|||||||
extraOptions = [ "--group-directories-first" ];
|
extraOptions = [ "--group-directories-first" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.zoxide = {
|
programs.zoxide.enable = true;
|
||||||
enable = true;
|
|
||||||
enableZshIntegration = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.fzf = {
|
programs.fzf.enable = true;
|
||||||
enable = true;
|
|
||||||
enableZshIntegration = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.bottom.enable = true;
|
programs.bottom.enable = true;
|
||||||
|
|
||||||
|
|||||||
@@ -41,15 +41,6 @@ in
|
|||||||
devices = builtins.attrNames syncDevices;
|
devices = builtins.attrNames syncDevices;
|
||||||
ignorePatterns = [ "**/.zk" ];
|
ignorePatterns = [ "**/.zk" ];
|
||||||
};
|
};
|
||||||
bruno = {
|
|
||||||
path =
|
|
||||||
if config.facts.machine.name == "orion" then
|
|
||||||
"${config.home.homeDirectory}/bruno"
|
|
||||||
else
|
|
||||||
"${config.xdg.userDirs.projects}/bruno";
|
|
||||||
label = "bruno";
|
|
||||||
devices = builtins.attrNames syncDevices;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
devices = syncDevices;
|
devices = syncDevices;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
flake.modules.nixos.tailscale = {
|
||||||
|
services.tailscale.enable = true;
|
||||||
|
# Keep existing SSH exposure until tailnet enrollment and access are verified.
|
||||||
|
networking.firewall.interfaces.tailscale0.allowedTCPPorts = [ 22 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
{ lib, config, ... }:
|
{ lib, config, ... }:
|
||||||
let
|
let
|
||||||
kanagawa = import ./_kanagawa.nix;
|
|
||||||
homeModules = config.flake.modules.homeManager;
|
homeModules = config.flake.modules.homeManager;
|
||||||
|
in
|
||||||
kittySingleInstance =
|
{
|
||||||
pkgs:
|
flake.modules.homeManager.terminal-kitty =
|
||||||
pkgs.symlinkJoin {
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
kitty = pkgs.symlinkJoin {
|
||||||
name = "kitty-single";
|
name = "kitty-single";
|
||||||
paths = [ pkgs.kitty ];
|
paths = [ pkgs.kitty ];
|
||||||
meta = pkgs.kitty.meta;
|
meta = pkgs.kitty.meta;
|
||||||
@@ -13,91 +14,14 @@ let
|
|||||||
postBuild = ''
|
postBuild = ''
|
||||||
wrapProgram $out/bin/kitty --add-flags "-1"
|
wrapProgram $out/bin/kitty --add-flags "-1"
|
||||||
rm $out/share/applications/kitty.desktop
|
rm $out/share/applications/kitty.desktop
|
||||||
sed 's/Exec=kitty/Exec=kitty -1/g' ${pkgs.kitty}/share/applications/kitty.desktop > $out/share/applications/kitty.desktop
|
sed 's/^Exec=kitty/Exec=kitty -1/' ${pkgs.kitty}/share/applications/kitty.desktop > $out/share/applications/kitty.desktop
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.homeManager.terminal-foot =
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
palette = kanagawa.palette;
|
|
||||||
hex = lib.removePrefix "#";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
programs.foot = {
|
|
||||||
enable = true;
|
|
||||||
settings = {
|
|
||||||
main = {
|
|
||||||
font = "JetBrains Mono:style=Medium:size=11:fontfeatures=-liga:fontfeatures=-calt";
|
|
||||||
font-bold = "JetBrains Mono:style=Bold:size=11:fontfeatures=-liga:fontfeatures=-calt";
|
|
||||||
font-italic = "JetBrains Mono:style=Medium Italic:size=11:fontfeatures=-liga:fontfeatures=-calt";
|
|
||||||
font-bold-italic = "JetBrains Mono:style=Bold Italic:size=11:fontfeatures=-liga:fontfeatures=-calt";
|
|
||||||
gamma-correct-blending = "yes";
|
|
||||||
pad = "3x3";
|
|
||||||
};
|
|
||||||
|
|
||||||
tweak.box-drawing-base-thickness = 0.1;
|
|
||||||
|
|
||||||
bell.system = "no";
|
|
||||||
|
|
||||||
scrollback.lines = 10000;
|
|
||||||
|
|
||||||
text-bindings."\\x0a" = "Shift+Return Shift+KP_Enter";
|
|
||||||
|
|
||||||
colors-dark = {
|
|
||||||
background = hex palette.background;
|
|
||||||
foreground = hex palette.foreground;
|
|
||||||
selection-background = hex palette.selectionBackground;
|
|
||||||
selection-foreground = hex palette.selectionForeground;
|
|
||||||
urls = hex palette.url;
|
|
||||||
cursor = "${hex palette.background} ${hex palette.cursor}";
|
|
||||||
|
|
||||||
regular0 = hex palette.terminal.color0;
|
|
||||||
regular1 = hex palette.terminal.color1;
|
|
||||||
regular2 = hex palette.terminal.color2;
|
|
||||||
regular3 = hex palette.terminal.color3;
|
|
||||||
regular4 = hex palette.terminal.color4;
|
|
||||||
regular5 = hex palette.terminal.color5;
|
|
||||||
regular6 = hex palette.terminal.color6;
|
|
||||||
regular7 = hex palette.terminal.color7;
|
|
||||||
|
|
||||||
bright0 = hex palette.terminal.color8;
|
|
||||||
bright1 = hex palette.terminal.color9;
|
|
||||||
bright2 = hex palette.terminal.color10;
|
|
||||||
bright3 = hex palette.terminal.color11;
|
|
||||||
bright4 = hex palette.terminal.color12;
|
|
||||||
bright5 = hex palette.terminal.color13;
|
|
||||||
bright6 = hex palette.terminal.color14;
|
|
||||||
bright7 = hex palette.terminal.color15;
|
|
||||||
|
|
||||||
"16" = hex palette.terminal.color16;
|
|
||||||
"17" = hex palette.terminal.color17;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.terminal-kitty =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
theme = kanagawa;
|
|
||||||
palette = theme.palette;
|
|
||||||
kitty = kittySingleInstance pkgs;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
programs.kitty = {
|
programs.kitty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = kitty;
|
package = kitty;
|
||||||
font = {
|
|
||||||
name = "JetBrains Mono";
|
|
||||||
size = 11;
|
|
||||||
};
|
|
||||||
settings = {
|
settings = {
|
||||||
disable_ligatures = "always";
|
disable_ligatures = "always";
|
||||||
scrollback_lines = 10000;
|
scrollback_lines = 10000;
|
||||||
@@ -107,48 +31,6 @@ in
|
|||||||
update_check_interval = 0;
|
update_check_interval = 0;
|
||||||
};
|
};
|
||||||
keybindings."shift+enter" = "send_text all \\x0a";
|
keybindings."shift+enter" = "send_text all \\x0a";
|
||||||
extraConfig = ''
|
|
||||||
# Nerd Fonts v3.4.0
|
|
||||||
symbol_map U+e000-U+e00a,U+e0a0-U+e0a2,U+e0a3,U+e0b0-U+e0b3,U+e0b4-U+e0c8,U+e0ca,U+e0cc-U+e0d7,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b7,U+e700-U+e8ef,U+ea60-U+ec1e,U+ed00-U+efce,U+f000-U+f2ff,U+f300-U+f381,U+f400-U+f533,U+f0001-U+f1af0 Symbols Nerd Font Mono
|
|
||||||
|
|
||||||
## name: ${theme.displayName}
|
|
||||||
## license: MIT
|
|
||||||
## author: Tommaso Laurenzi
|
|
||||||
## upstream: https://github.com/rebelot/kanagawa.nvim/
|
|
||||||
|
|
||||||
background ${palette.background}
|
|
||||||
foreground ${palette.foreground}
|
|
||||||
selection_background ${palette.selectionBackground}
|
|
||||||
selection_foreground ${palette.selectionForeground}
|
|
||||||
url_color ${palette.url}
|
|
||||||
cursor ${palette.cursor}
|
|
||||||
|
|
||||||
active_tab_background ${palette.background}
|
|
||||||
active_tab_foreground ${palette.selectionForeground}
|
|
||||||
inactive_tab_background ${palette.background}
|
|
||||||
inactive_tab_foreground ${palette.muted}
|
|
||||||
|
|
||||||
color0 ${palette.terminal.color0}
|
|
||||||
color1 ${palette.terminal.color1}
|
|
||||||
color2 ${palette.terminal.color2}
|
|
||||||
color3 ${palette.terminal.color3}
|
|
||||||
color4 ${palette.terminal.color4}
|
|
||||||
color5 ${palette.terminal.color5}
|
|
||||||
color6 ${palette.terminal.color6}
|
|
||||||
color7 ${palette.terminal.color7}
|
|
||||||
|
|
||||||
color8 ${palette.terminal.color8}
|
|
||||||
color9 ${palette.terminal.color9}
|
|
||||||
color10 ${palette.terminal.color10}
|
|
||||||
color11 ${palette.terminal.color11}
|
|
||||||
color12 ${palette.terminal.color12}
|
|
||||||
color13 ${palette.terminal.color13}
|
|
||||||
color14 ${palette.terminal.color14}
|
|
||||||
color15 ${palette.terminal.color15}
|
|
||||||
|
|
||||||
color16 ${palette.terminal.color16}
|
|
||||||
color17 ${palette.terminal.color17}
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
systemd.user.services.kitty-daemon = {
|
systemd.user.services.kitty-daemon = {
|
||||||
Unit = {
|
Unit = {
|
||||||
@@ -166,19 +48,6 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules.homeManager.primary-terminal-foot =
|
|
||||||
{ config, lib, ... }:
|
|
||||||
{
|
|
||||||
imports = [ homeModules.terminal-foot ];
|
|
||||||
|
|
||||||
desktop.commands.terminal = [ (lib.getExe config.programs.foot.package) ];
|
|
||||||
|
|
||||||
xdg.terminal-exec = {
|
|
||||||
enable = true;
|
|
||||||
settings.default = [ "foot.desktop" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.primary-terminal-kitty =
|
flake.modules.homeManager.primary-terminal-kitty =
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
{ config, ... }:
|
|
||||||
let
|
|
||||||
hmModules = config.flake.modules.homeManager;
|
|
||||||
cursor = {
|
|
||||||
name = "phinger-cursors-light";
|
|
||||||
packageName = "phinger-cursors";
|
|
||||||
size = 24;
|
|
||||||
};
|
|
||||||
mkCursor = pkgs: cursor // { package = pkgs.${cursor.packageName}; };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.theme =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
cursorTheme = mkCursor pkgs;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
home-manager.sharedModules = [ hmModules.theme ];
|
|
||||||
environment.systemPackages = [ cursorTheme.package ];
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.theme =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
cursorTheme = mkCursor pkgs;
|
|
||||||
version = "unstable-2025-10-23";
|
|
||||||
src = pkgs.fetchFromGitHub {
|
|
||||||
owner = "Fausto-Korpsvart";
|
|
||||||
repo = "Kanagawa-GKT-Theme";
|
|
||||||
rev = "55ca4ba249eba21f861b9866b71ab41bb8930318";
|
|
||||||
hash = "sha256-UdMoMx2DoovcxSp/zBZ3PRv/Qpj+prd0uPm1gmdak2E=";
|
|
||||||
};
|
|
||||||
kanagawaGtkTheme = pkgs.stdenvNoCC.mkDerivation {
|
|
||||||
pname = "kanagawa-gtk-theme";
|
|
||||||
inherit src version;
|
|
||||||
|
|
||||||
nativeBuildInputs = [ pkgs.sassc ];
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
mkdir -p "$out/share/themes"
|
|
||||||
bash ./themes/install.sh \
|
|
||||||
--dest "$out/share/themes" \
|
|
||||||
--name Kanagawa \
|
|
||||||
--color dark
|
|
||||||
|
|
||||||
rm -r "$out/share/themes/Kanagawa-Dark/gtk-2.0"
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
home.pointerCursor = {
|
|
||||||
enable = true;
|
|
||||||
inherit (cursorTheme)
|
|
||||||
name
|
|
||||||
package
|
|
||||||
size
|
|
||||||
;
|
|
||||||
dotIcons.enable = false;
|
|
||||||
gtk.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
gtk = {
|
|
||||||
enable = true;
|
|
||||||
theme = {
|
|
||||||
name = "Kanagawa-Dark";
|
|
||||||
package = kanagawaGtkTheme;
|
|
||||||
};
|
|
||||||
gtk4.theme = null;
|
|
||||||
iconTheme = {
|
|
||||||
name = "Kanagawa";
|
|
||||||
package = pkgs.kanagawa-icon-theme.overrideAttrs (_: {
|
|
||||||
inherit src version;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
qt = {
|
|
||||||
enable = true;
|
|
||||||
platformTheme.name = "gtk3";
|
|
||||||
};
|
|
||||||
|
|
||||||
xdg.systemDirs.data = [
|
|
||||||
"${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}"
|
|
||||||
"${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,314 @@
|
|||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
palette = (import ./palette.nix).palette;
|
||||||
|
hex = lib.removePrefix "#";
|
||||||
|
wallpaper = ./assets/green-leaves.png;
|
||||||
|
cursor = {
|
||||||
|
name = "phinger-cursors-light";
|
||||||
|
size = 24;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.modules.nixos.theme =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
home-manager.sharedModules = [ config.flake.modules.homeManager.theme ];
|
||||||
|
environment.systemPackages = [ pkgs.phinger-cursors ];
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.theme =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
version = "unstable-2025-10-23";
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "Fausto-Korpsvart";
|
||||||
|
repo = "Kanagawa-GKT-Theme";
|
||||||
|
rev = "55ca4ba249eba21f861b9866b71ab41bb8930318";
|
||||||
|
hash = "sha256-UdMoMx2DoovcxSp/zBZ3PRv/Qpj+prd0uPm1gmdak2E=";
|
||||||
|
};
|
||||||
|
kanagawaGtkTheme = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "kanagawa-gtk-theme";
|
||||||
|
inherit src version;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ pkgs.sassc ];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p "$out/share/themes"
|
||||||
|
bash ./themes/install.sh \
|
||||||
|
--dest "$out/share/themes" \
|
||||||
|
--name Kanagawa \
|
||||||
|
--color dark
|
||||||
|
|
||||||
|
rm -r "$out/share/themes/Kanagawa-Dark/gtk-2.0"
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
home.pointerCursor = cursor // {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.phinger-cursors;
|
||||||
|
dotIcons.enable = false;
|
||||||
|
gtk.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
gtk = {
|
||||||
|
enable = true;
|
||||||
|
theme = {
|
||||||
|
name = "Kanagawa-Dark";
|
||||||
|
package = kanagawaGtkTheme;
|
||||||
|
};
|
||||||
|
gtk4.theme = null;
|
||||||
|
iconTheme = {
|
||||||
|
name = "Kanagawa";
|
||||||
|
package = pkgs.kanagawa-icon-theme.overrideAttrs (old: {
|
||||||
|
inherit src version;
|
||||||
|
postInstall = (old.postInstall or "") + ''
|
||||||
|
find "$out/share/icons" -name index.theme -exec \
|
||||||
|
sed -i 's/^Inherits=.*/Inherits=Adwaita,hicolor/' {} +
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
qt = {
|
||||||
|
enable = true;
|
||||||
|
platformTheme.name = "gtk3";
|
||||||
|
};
|
||||||
|
|
||||||
|
home.packages = [ pkgs.adwaita-icon-theme ];
|
||||||
|
|
||||||
|
xdg.systemDirs.data = [
|
||||||
|
"${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}"
|
||||||
|
"${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.nixos.limine =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
let
|
||||||
|
terminalPalette = palette.terminal;
|
||||||
|
mkPalette = colors: lib.concatStringsSep ";" (map hex colors);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
boot.loader.limine.style = {
|
||||||
|
wallpapers = [ pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath ];
|
||||||
|
wallpaperStyle = "centered";
|
||||||
|
backdrop = hex palette.secondaryBackground;
|
||||||
|
|
||||||
|
graphicalTerminal = {
|
||||||
|
# TT is transparency: let the boot wallpaper show through the terminal.
|
||||||
|
background = "FF${hex palette.background}";
|
||||||
|
foreground = hex palette.foreground;
|
||||||
|
brightForeground = hex palette.selectionForeground;
|
||||||
|
brightBackground = hex palette.selectionBackground;
|
||||||
|
palette = mkPalette [
|
||||||
|
terminalPalette.color0
|
||||||
|
terminalPalette.color1
|
||||||
|
terminalPalette.color2
|
||||||
|
terminalPalette.color3
|
||||||
|
terminalPalette.color4
|
||||||
|
terminalPalette.color5
|
||||||
|
terminalPalette.color6
|
||||||
|
terminalPalette.color7
|
||||||
|
];
|
||||||
|
brightPalette = mkPalette [
|
||||||
|
terminalPalette.color8
|
||||||
|
terminalPalette.color9
|
||||||
|
terminalPalette.color10
|
||||||
|
terminalPalette.color11
|
||||||
|
terminalPalette.color12
|
||||||
|
terminalPalette.color13
|
||||||
|
terminalPalette.color14
|
||||||
|
terminalPalette.color15
|
||||||
|
];
|
||||||
|
font = {
|
||||||
|
scale = "2x2";
|
||||||
|
spacing = 1;
|
||||||
|
};
|
||||||
|
margin = 64;
|
||||||
|
marginGradient = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface = {
|
||||||
|
branding = "NixOS - ${config.networking.hostName}";
|
||||||
|
brandingColor = hex palette.accents.blue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.nixos.noctalia-greeter = { pkgs, ... }: {
|
||||||
|
services.displayManager.noctalia-greeter = {
|
||||||
|
cursorTheme = {
|
||||||
|
package = pkgs.phinger-cursors;
|
||||||
|
name = cursor.name;
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
appearance = {
|
||||||
|
scheme = "Synced";
|
||||||
|
password_style = "default";
|
||||||
|
hide_logo = true;
|
||||||
|
power_buttons_position = "bottom-right";
|
||||||
|
scheme_selector_position = "hidden";
|
||||||
|
theme_mode = "dark";
|
||||||
|
corner_radius_scale = 1.0;
|
||||||
|
font_family = "Lexend";
|
||||||
|
|
||||||
|
palette = {
|
||||||
|
primary = palette.terminal.color2;
|
||||||
|
on_primary = palette.background;
|
||||||
|
secondary = palette.terminal.color3;
|
||||||
|
on_secondary = palette.background;
|
||||||
|
tertiary = palette.terminal.color4;
|
||||||
|
on_tertiary = palette.background;
|
||||||
|
error = palette.terminal.color1;
|
||||||
|
on_error = palette.background;
|
||||||
|
surface = palette.background;
|
||||||
|
on_surface = palette.selectionForeground;
|
||||||
|
surface_variant = "#2A2A37";
|
||||||
|
on_surface_variant = "#717C7C";
|
||||||
|
outline = "#363646";
|
||||||
|
shadow = palette.background;
|
||||||
|
hover = palette.terminal.color4;
|
||||||
|
on_hover = palette.background;
|
||||||
|
};
|
||||||
|
|
||||||
|
wallpaper = {
|
||||||
|
path = toString wallpaper;
|
||||||
|
fill_mode = "crop";
|
||||||
|
fill_color = palette.background;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor.size = cursor.size;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.nixos.plymouth = { pkgs, ... }: {
|
||||||
|
boot.plymouth = {
|
||||||
|
theme = "spinner_alt";
|
||||||
|
themePackages = [ pkgs.adi1090x-plymouth-themes ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.terminal-kitty = {
|
||||||
|
programs.kitty = {
|
||||||
|
settings = {
|
||||||
|
bold_font = "postscript_name=JetBrainsMono-Bold";
|
||||||
|
bold_italic_font = "postscript_name=JetBrainsMono-BoldItalic";
|
||||||
|
};
|
||||||
|
font = {
|
||||||
|
name = "JetBrains Mono";
|
||||||
|
size = 11;
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
# Nerd Fonts v3.4.0
|
||||||
|
symbol_map U+e000-U+e00a,U+e0a0-U+e0a2,U+e0a3,U+e0b0-U+e0b3,U+e0b4-U+e0c8,U+e0ca,U+e0cc-U+e0d7,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b7,U+e700-U+e8ef,U+ea60-U+ec1e,U+ed00-U+efce,U+f000-U+f2ff,U+f300-U+f381,U+f400-U+f533,U+f0001-U+f1af0 Symbols Nerd Font Mono
|
||||||
|
|
||||||
|
## name: Kanagawa Wave
|
||||||
|
## license: MIT
|
||||||
|
## author: Tommaso Laurenzi
|
||||||
|
## upstream: https://github.com/rebelot/kanagawa.nvim/
|
||||||
|
|
||||||
|
background ${palette.background}
|
||||||
|
foreground ${palette.foreground}
|
||||||
|
selection_background ${palette.selectionBackground}
|
||||||
|
selection_foreground ${palette.selectionForeground}
|
||||||
|
url_color ${palette.url}
|
||||||
|
cursor ${palette.cursor}
|
||||||
|
|
||||||
|
active_tab_background ${palette.background}
|
||||||
|
active_tab_foreground ${palette.selectionForeground}
|
||||||
|
inactive_tab_background ${palette.background}
|
||||||
|
inactive_tab_foreground ${palette.muted}
|
||||||
|
|
||||||
|
color0 ${palette.terminal.color0}
|
||||||
|
color1 ${palette.terminal.color1}
|
||||||
|
color2 ${palette.terminal.color2}
|
||||||
|
color3 ${palette.terminal.color3}
|
||||||
|
color4 ${palette.terminal.color4}
|
||||||
|
color5 ${palette.terminal.color5}
|
||||||
|
color6 ${palette.terminal.color6}
|
||||||
|
color7 ${palette.terminal.color7}
|
||||||
|
|
||||||
|
color8 ${palette.terminal.color8}
|
||||||
|
color9 ${palette.terminal.color9}
|
||||||
|
color10 ${palette.terminal.color10}
|
||||||
|
color11 ${palette.terminal.color11}
|
||||||
|
color12 ${palette.terminal.color12}
|
||||||
|
color13 ${palette.terminal.color13}
|
||||||
|
color14 ${palette.terminal.color14}
|
||||||
|
color15 ${palette.terminal.color15}
|
||||||
|
|
||||||
|
color16 ${palette.terminal.color16}
|
||||||
|
color17 ${palette.terminal.color17}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.niri = {
|
||||||
|
dconf.settings."org/gnome/desktop/interface".color-scheme = "prefer-dark";
|
||||||
|
wayland.windowManager.niri.settings.layout.border = {
|
||||||
|
active-color = palette.accents.blue;
|
||||||
|
inactive-color = palette.subtleBorder;
|
||||||
|
urgent-color = palette.accents.red;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.ai = {
|
||||||
|
xdg.configFile."herdr/config.toml".text = lib.mkAfter ''
|
||||||
|
[theme]
|
||||||
|
name = "kanagawa"
|
||||||
|
auto_switch = false
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.noctalia = {
|
||||||
|
programs.noctalia.settings = {
|
||||||
|
shell.font_family = "Lexend";
|
||||||
|
theme = {
|
||||||
|
builtin = "Kanagawa";
|
||||||
|
community_palette = "Kanagawa Paper";
|
||||||
|
wallpaper_scheme = "m3-content";
|
||||||
|
};
|
||||||
|
wallpaper = {
|
||||||
|
default.path = toString wallpaper;
|
||||||
|
last.path = toString wallpaper;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.neovim = { pkgs, ... }: {
|
||||||
|
wrappers.neovim.specs.theme = {
|
||||||
|
data = pkgs.vimPlugins.kanagawa-nvim;
|
||||||
|
before = [ "INIT_MAIN" ];
|
||||||
|
config = ''
|
||||||
|
require("kanagawa").setup({
|
||||||
|
dimInactive = true,
|
||||||
|
colors = {
|
||||||
|
theme = {
|
||||||
|
all = {
|
||||||
|
ui = { bg_gutter = "none" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
vim.cmd.colorscheme("kanagawa-wave")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.walker = {
|
||||||
|
programs.walker.themes.desktop.style = lib.mkAfter ''
|
||||||
|
@define-color window_bg_color ${palette.background};
|
||||||
|
@define-color accent_bg_color ${palette.selectionBackground};
|
||||||
|
@define-color theme_fg_color ${palette.foreground};
|
||||||
|
@define-color error_bg_color ${palette.terminal.color1};
|
||||||
|
@define-color error_fg_color ${palette.foreground};
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
|||||||
|
# Select a complete theme module: plugins, assets and behavior as well as colors.
|
||||||
|
{ imports = [ ./_support/kanagawa.nix ]; }
|
||||||
+35
-27
@@ -1,7 +1,4 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
let
|
|
||||||
homeModules = config.flake.modules.homeManager;
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
flake.modules.homeManager.transcription =
|
flake.modules.homeManager.transcription =
|
||||||
{
|
{
|
||||||
@@ -34,9 +31,10 @@ in
|
|||||||
executable = true;
|
executable = true;
|
||||||
text = ''
|
text = ''
|
||||||
#!${diarizationPython}/bin/python3
|
#!${diarizationPython}/bin/python3
|
||||||
${builtins.readFile ./_diarize-transcript.py}
|
${builtins.readFile ./diarize-transcript.py}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
whisper = pkgs.whisper-cpp;
|
||||||
prepareDiarizationModel = pkgs.writeShellApplication {
|
prepareDiarizationModel = pkgs.writeShellApplication {
|
||||||
name = "prepare-transcription-diarization-model";
|
name = "prepare-transcription-diarization-model";
|
||||||
runtimeInputs = with pkgs; [
|
runtimeInputs = with pkgs; [
|
||||||
@@ -72,18 +70,28 @@ in
|
|||||||
diarizeTranscript
|
diarizeTranscript
|
||||||
ffmpeg
|
ffmpeg
|
||||||
findutils
|
findutils
|
||||||
whisper-cpp-vulkan
|
whisper
|
||||||
];
|
];
|
||||||
text = builtins.readFile ./_transcribe-recordings.sh;
|
text = builtins.readFile ./transcribe-recordings.sh;
|
||||||
};
|
};
|
||||||
|
home = config.home.homeDirectory;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.packages = with pkgs; [
|
home.packages = [
|
||||||
ffmpeg
|
pkgs.ffmpeg
|
||||||
prepareDiarizationModel
|
prepareDiarizationModel
|
||||||
whisper-cpp-vulkan
|
whisper
|
||||||
];
|
];
|
||||||
|
|
||||||
|
home.activation.createTranscriptionLayout = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
${pkgs.coreutils}/bin/mkdir -p \
|
||||||
|
"${home}/sync/notes/transcripts" \
|
||||||
|
"${home}/sync/recordings/inbox" \
|
||||||
|
"${home}/sync/recordings/processing" \
|
||||||
|
"${home}/sync/recordings/archive" \
|
||||||
|
"${home}/sync/recordings/failed"
|
||||||
|
'';
|
||||||
|
|
||||||
systemd.user.services.transcribe-recordings = {
|
systemd.user.services.transcribe-recordings = {
|
||||||
Unit.Description = "Transcribe synchronized recordings with whisper.cpp";
|
Unit.Description = "Transcribe synchronized recordings with whisper.cpp";
|
||||||
|
|
||||||
@@ -91,7 +99,8 @@ in
|
|||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = lib.getExe transcribeRecordings;
|
ExecStart = lib.getExe transcribeRecordings;
|
||||||
Environment = [
|
Environment = [
|
||||||
"TRANSCRIBE_ENGINE_VERSION=${pkgs.whisper-cpp-vulkan.version}"
|
"TRANSCRIBE_ENGINE_ACCELERATION=CPU"
|
||||||
|
"TRANSCRIBE_ENGINE_VERSION=${whisper.version}"
|
||||||
"TRANSCRIBE_MODEL=${model}"
|
"TRANSCRIBE_MODEL=${model}"
|
||||||
''"TRANSCRIBE_MODEL_NAME=${modelName} multilingual"''
|
''"TRANSCRIBE_MODEL_NAME=${modelName} multilingual"''
|
||||||
"TRANSCRIBE_MODEL_SHA256=${modelSha256}"
|
"TRANSCRIBE_MODEL_SHA256=${modelSha256}"
|
||||||
@@ -99,26 +108,25 @@ in
|
|||||||
"TRANSCRIBE_DIARIZATION_MODEL=${diarizationModel}"
|
"TRANSCRIBE_DIARIZATION_MODEL=${diarizationModel}"
|
||||||
"TRANSCRIBE_DIARIZATION_MODEL_NAME=${diarizationModelName}"
|
"TRANSCRIBE_DIARIZATION_MODEL_NAME=${diarizationModelName}"
|
||||||
"TRANSCRIBE_DIARIZATION_MODEL_REVISION=${diarizationModelRevision}"
|
"TRANSCRIBE_DIARIZATION_MODEL_REVISION=${diarizationModelRevision}"
|
||||||
"TRANSCRIBE_SYNC_ROOT=${config.home.homeDirectory}/sync"
|
"TRANSCRIBE_SYNC_ROOT=${home}/sync"
|
||||||
];
|
];
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 60;
|
||||||
|
};
|
||||||
|
|
||||||
|
Install.WantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.paths.transcribe-recordings = {
|
||||||
|
Unit.Description = "Watch for synchronized recordings";
|
||||||
|
|
||||||
|
Path.PathChanged = "${home}/sync/recordings/inbox";
|
||||||
|
|
||||||
|
Install.WantedBy = [ "paths.target" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.user.timers.transcribe-recordings = {
|
flake.modules.nixos.transcription = {
|
||||||
Unit.Description = "Transcribe new recordings every two minutes";
|
home-manager.sharedModules = [ config.flake.modules.homeManager.transcription ];
|
||||||
|
|
||||||
Timer = {
|
|
||||||
OnCalendar = "*:0/2";
|
|
||||||
Persistent = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
Install.WantedBy = [ "timers.target" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.nixos.transcription =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
home-manager.sharedModules = [ homeModules.transcription ];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
+10
-12
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from itertools import groupby
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
@@ -25,9 +26,13 @@ def load_tokens(path: Path) -> list[Token]:
|
|||||||
|
|
||||||
tokens: list[Token] = []
|
tokens: list[Token] = []
|
||||||
pending = ""
|
pending = ""
|
||||||
|
# Whisper reserves all IDs from end-of-text onward for control tokens.
|
||||||
|
end_of_text = 50256 + int(transcript["model"]["multilingual"])
|
||||||
|
|
||||||
for segment in transcript["transcription"]:
|
for segment in transcript["transcription"]:
|
||||||
for raw_token in segment["tokens"]:
|
for raw_token in segment["tokens"]:
|
||||||
|
if raw_token["id"] >= end_of_text:
|
||||||
|
continue
|
||||||
text = raw_token["text"]
|
text = raw_token["text"]
|
||||||
offsets = raw_token.get("offsets")
|
offsets = raw_token.get("offsets")
|
||||||
|
|
||||||
@@ -81,19 +86,12 @@ def token_speaker(token: Token, turns: list[Turn]) -> str:
|
|||||||
|
|
||||||
def render(tokens: list[Token], turns: list[Turn]) -> str:
|
def render(tokens: list[Token], turns: list[Turn]) -> str:
|
||||||
speaker_numbers: dict[str, int] = {}
|
speaker_numbers: dict[str, int] = {}
|
||||||
blocks: list[tuple[int, str]] = []
|
|
||||||
|
|
||||||
for token in tokens:
|
|
||||||
speaker = token_speaker(token, turns)
|
|
||||||
number = speaker_numbers.setdefault(speaker, len(speaker_numbers) + 1)
|
|
||||||
|
|
||||||
if blocks and blocks[-1][0] == number:
|
|
||||||
blocks[-1] = number, blocks[-1][1] + token.text
|
|
||||||
else:
|
|
||||||
blocks.append((number, token.text))
|
|
||||||
|
|
||||||
rendered = []
|
rendered = []
|
||||||
for number, text in blocks:
|
for speaker, group in groupby(
|
||||||
|
tokens, key=lambda token: token_speaker(token, turns)
|
||||||
|
):
|
||||||
|
number = speaker_numbers.setdefault(speaker, len(speaker_numbers) + 1)
|
||||||
|
text = "".join(token.text for token in group)
|
||||||
text = re.sub(r"\s+", " ", text).strip()
|
text = re.sub(r"\s+", " ", text).strip()
|
||||||
if text:
|
if text:
|
||||||
rendered.append(f"**Speaker {number}:**\n\n{text}")
|
rendered.append(f"**Speaker {number}:**\n\n{text}")
|
||||||
+112
-49
@@ -17,48 +17,47 @@ move_no_clobber() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv -T --no-clobber -- "$source" "$destination"
|
mv -T --no-clobber -- "$source" "$destination" || return 1
|
||||||
! path_exists "$source" && path_exists "$destination"
|
! path_exists "$source" && path_exists "$destination"
|
||||||
}
|
}
|
||||||
|
|
||||||
fail_recording() {
|
fail_recording() (
|
||||||
local source=$1
|
local source=$1
|
||||||
local failed_dir=$2
|
local failed_dir=$2
|
||||||
local message=$3
|
local message=$3
|
||||||
local details=${4:-}
|
local details=${4:-}
|
||||||
local base target error_tmp suffix=0
|
local base target error_tmp suffix=0
|
||||||
|
|
||||||
base=$(basename "$source")
|
base=${source##*/}
|
||||||
target="$failed_dir/$base"
|
target="$failed_dir/$base"
|
||||||
rm -f -- "${source}.transcribe-state"
|
|
||||||
|
|
||||||
while path_exists "$target" || path_exists "${target}.error.txt"; do
|
while path_exists "$target" || path_exists "${target}.error.txt"; do
|
||||||
((suffix += 1))
|
((suffix += 1))
|
||||||
target="$failed_dir/$base.failed-$suffix"
|
target="$failed_dir/$base.failed-$suffix"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
error_tmp=$(mktemp "$failed_dir/.transcription-error.XXXXXX") || return 1
|
||||||
|
trap 'rm -f -- "$error_tmp"' EXIT
|
||||||
|
{
|
||||||
|
printf '%s\n' "$message" || return 1
|
||||||
|
if [[ -n "$details" && -s "$details" ]]; then
|
||||||
|
printf '\n' || return 1
|
||||||
|
cat -- "$details" || return 1
|
||||||
|
fi
|
||||||
|
} >"$error_tmp" || return 1
|
||||||
|
|
||||||
if ! move_no_clobber "$source" "$target"; then
|
if ! move_no_clobber "$source" "$target"; then
|
||||||
printf 'Could not quarantine %s: destination collision or move failure\n' "$source" >&2
|
printf 'Could not quarantine %s: destination collision or move failure\n' "$source" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
error_tmp=$(mktemp "$failed_dir/.transcription-error.XXXXXX")
|
|
||||||
{
|
|
||||||
printf '%s\n' "$message"
|
|
||||||
if [[ -n "$details" && -s "$details" ]]; then
|
|
||||||
printf '\n'
|
|
||||||
cat -- "$details"
|
|
||||||
fi
|
|
||||||
} >"$error_tmp"
|
|
||||||
|
|
||||||
if ! ln -- "$error_tmp" "${target}.error.txt"; then
|
if ! ln -- "$error_tmp" "${target}.error.txt"; then
|
||||||
printf 'Could not publish error sidecar for %s\n' "$target" >&2
|
printf 'Could not publish error sidecar for %s\n' "$target" >&2
|
||||||
rm -f -- "$error_tmp"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
rm -f -- "$error_tmp"
|
rm -f -- "${source}.transcribe-state" || return 1
|
||||||
printf 'Moved failed recording to %s\n' "$target" >&2
|
printf 'Moved failed recording to %s\n' "$target" >&2
|
||||||
}
|
)
|
||||||
|
|
||||||
recover_completed_recording() {
|
recover_completed_recording() {
|
||||||
local source=$1
|
local source=$1
|
||||||
@@ -71,15 +70,18 @@ recover_completed_recording() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ! path_exists "$transcript"; then
|
if ! path_exists "$transcript"; then
|
||||||
rm -f -- "$state_file"
|
rm -f -- "$state_file" || return 3
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! move_no_clobber "$source" "$archive"; then
|
if ! move_no_clobber "$source" "$archive"; then
|
||||||
|
if path_exists "$archive"; then
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
|
||||||
rm -f -- "$state_file"
|
rm -f -- "$state_file" || return 3
|
||||||
printf 'Recovered completed transcription and archived %s\n' "$archive"
|
printf 'Recovered completed transcription and archived %s\n' "$archive"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -92,10 +94,12 @@ process_recording() (
|
|||||||
local transcript_dir=$3
|
local transcript_dir=$3
|
||||||
local archive_dir=$4
|
local archive_dir=$4
|
||||||
local failed_dir=$5
|
local failed_dir=$5
|
||||||
local base stem transcript archive original_path recorded_at transcribed_at
|
local base stem transcript archive original_path recorded_at transcribed_at duration
|
||||||
local work_dir log_file raw_output rendered_output draft state_file recovery_status
|
local work_dir log_file raw_output rendered_output state_file recovery_status
|
||||||
|
local draft="" diarization_log
|
||||||
|
local diarization_succeeded=false
|
||||||
|
|
||||||
base=$(basename "$source")
|
base=${source##*/}
|
||||||
stem=${base%.*}
|
stem=${base%.*}
|
||||||
transcript="$transcript_dir/$stem.md"
|
transcript="$transcript_dir/$stem.md"
|
||||||
archive="$archive_dir/$base"
|
archive="$archive_dir/$base"
|
||||||
@@ -111,6 +115,10 @@ process_recording() (
|
|||||||
fail_recording "$source" "$failed_dir" "Archive collision after transcription: $archive"
|
fail_recording "$source" "$failed_dir" "Archive collision after transcription: $archive"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if [[ $recovery_status -ne 1 ]]; then
|
||||||
|
printf 'Could not recover %s; leaving state for retry\n' "$source" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if path_exists "$transcript"; then
|
if path_exists "$transcript"; then
|
||||||
fail_recording "$source" "$failed_dir" "Transcript already exists: $transcript"
|
fail_recording "$source" "$failed_dir" "Transcript already exists: $transcript"
|
||||||
@@ -121,14 +129,30 @@ process_recording() (
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
recorded_at=$(date --date="@$(stat -c %Y -- "$source")" --iso-8601=seconds)
|
recorded_at=$(stat -c %Y -- "$source")
|
||||||
|
recorded_at=$(date --date="@$recorded_at" --iso-8601=seconds)
|
||||||
transcribed_at=$(date --utc --iso-8601=seconds)
|
transcribed_at=$(date --utc --iso-8601=seconds)
|
||||||
work_dir=$(mktemp -d "$runtime_dir/work.XXXXXX")
|
work_dir=$(mktemp -d "$runtime_dir/work.XXXXXX")
|
||||||
trap 'rm -r -- "$work_dir"' EXIT
|
trap 'if [[ -n "$draft" ]]; then rm -f -- "$draft"; fi; rm -r -- "$work_dir"' EXIT
|
||||||
log_file="$work_dir/transcription.log"
|
log_file="$work_dir/transcription.log"
|
||||||
|
diarization_log="$work_dir/diarization.log"
|
||||||
raw_output="$work_dir/transcript"
|
raw_output="$work_dir/transcript"
|
||||||
rendered_output="$work_dir/transcript.md"
|
rendered_output="$work_dir/transcript.md"
|
||||||
|
|
||||||
|
if ! duration=$(ffprobe \
|
||||||
|
-v error \
|
||||||
|
-show_entries format=duration \
|
||||||
|
-of default=noprint_wrappers=1:nokey=1 \
|
||||||
|
"$source" 2>"$log_file"); then
|
||||||
|
fail_recording "$source" "$failed_dir" "Media validation failed" "$log_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ ! $duration =~ ^[0-9]+([.][0-9]+)?$ || -z ${duration//[.0]/} ]]; then
|
||||||
|
printf 'Invalid media duration: %s\n' "$duration" >"$log_file"
|
||||||
|
fail_recording "$source" "$failed_dir" "Media validation failed" "$log_file"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if ! ffmpeg \
|
if ! ffmpeg \
|
||||||
-nostdin \
|
-nostdin \
|
||||||
-hide_banner \
|
-hide_banner \
|
||||||
@@ -148,18 +172,27 @@ process_recording() (
|
|||||||
--file "$work_dir/audio.wav" \
|
--file "$work_dir/audio.wav" \
|
||||||
--language auto \
|
--language auto \
|
||||||
--output-json-full \
|
--output-json-full \
|
||||||
--output-file "$raw_output" >>"$log_file" 2>&1 || [[ ! -s "${raw_output}.json" ]]; then
|
--output-txt \
|
||||||
|
--output-file "$raw_output" >>"$log_file" 2>&1 \
|
||||||
|
|| [[ ! -s "${raw_output}.json" || ! -s "${raw_output}.txt" ]]; then
|
||||||
fail_recording "$source" "$failed_dir" "Whisper transcription failed" "$log_file"
|
fail_recording "$source" "$failed_dir" "Whisper transcription failed" "$log_file"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! diarize-transcript \
|
if [[ -f "$TRANSCRIBE_DIARIZATION_MODEL/.complete" && -f "$TRANSCRIBE_DIARIZATION_MODEL/config.yaml" ]] \
|
||||||
|
&& diarize-transcript \
|
||||||
--audio "$work_dir/audio.wav" \
|
--audio "$work_dir/audio.wav" \
|
||||||
--model "$TRANSCRIBE_DIARIZATION_MODEL" \
|
--model "$TRANSCRIBE_DIARIZATION_MODEL" \
|
||||||
--transcript "${raw_output}.json" \
|
--transcript "${raw_output}.json" \
|
||||||
--output "$rendered_output" >>"$log_file" 2>&1 || [[ ! -s "$rendered_output" ]]; then
|
--output "$rendered_output" >"$diarization_log" 2>&1 \
|
||||||
fail_recording "$source" "$failed_dir" "Speaker diarization failed" "$log_file"
|
&& [[ -s "$rendered_output" ]]; then
|
||||||
return 1
|
diarization_succeeded=true
|
||||||
|
else
|
||||||
|
printf 'Speaker diarization unavailable; publishing unlabeled transcript\n' >&2
|
||||||
|
if [[ -s "$diarization_log" ]]; then
|
||||||
|
cat -- "$diarization_log" >&2
|
||||||
|
fi
|
||||||
|
cp -- "${raw_output}.txt" "$rendered_output"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
draft=$(mktemp "$transcript_dir/.transcript.XXXXXX")
|
draft=$(mktemp "$transcript_dir/.transcript.XXXXXX")
|
||||||
@@ -168,12 +201,17 @@ process_recording() (
|
|||||||
printf -- '- Original recording: %s\n' "$original_path"
|
printf -- '- Original recording: %s\n' "$original_path"
|
||||||
printf -- '- Recording timestamp: %s\n' "$recorded_at"
|
printf -- '- Recording timestamp: %s\n' "$recorded_at"
|
||||||
printf -- '- Transcription timestamp: %s\n' "$transcribed_at"
|
printf -- '- Transcription timestamp: %s\n' "$transcribed_at"
|
||||||
printf -- '- Engine: whisper.cpp %s (Vulkan)\n' "$TRANSCRIBE_ENGINE_VERSION"
|
printf -- '- Duration: %s seconds\n' "$duration"
|
||||||
|
printf -- '- Engine: whisper.cpp %s (%s)\n' "$TRANSCRIBE_ENGINE_VERSION" "$TRANSCRIBE_ENGINE_ACCELERATION"
|
||||||
printf -- '- Model: %s\n' "$TRANSCRIBE_MODEL_NAME"
|
printf -- '- Model: %s\n' "$TRANSCRIBE_MODEL_NAME"
|
||||||
printf -- '- Model SHA-256: %s\n' "$TRANSCRIBE_MODEL_SHA256"
|
printf -- '- Model SHA-256: %s\n' "$TRANSCRIBE_MODEL_SHA256"
|
||||||
|
if [[ $diarization_succeeded == true ]]; then
|
||||||
printf -- '- Diarization engine: pyannote.audio %s (CPU)\n' "$TRANSCRIBE_DIARIZATION_ENGINE_VERSION"
|
printf -- '- Diarization engine: pyannote.audio %s (CPU)\n' "$TRANSCRIBE_DIARIZATION_ENGINE_VERSION"
|
||||||
printf -- '- Diarization model: %s\n' "$TRANSCRIBE_DIARIZATION_MODEL_NAME"
|
printf -- '- Diarization model: %s\n' "$TRANSCRIBE_DIARIZATION_MODEL_NAME"
|
||||||
printf -- '- Diarization model revision: %s\n\n' "$TRANSCRIBE_DIARIZATION_MODEL_REVISION"
|
printf -- '- Diarization model revision: %s\n\n' "$TRANSCRIBE_DIARIZATION_MODEL_REVISION"
|
||||||
|
else
|
||||||
|
printf -- '- Diarization: unavailable; unlabeled fallback published\n\n'
|
||||||
|
fi
|
||||||
cat -- "$rendered_output"
|
cat -- "$rendered_output"
|
||||||
} >"$draft"
|
} >"$draft"
|
||||||
chmod 0644 "$draft"
|
chmod 0644 "$draft"
|
||||||
@@ -196,37 +234,70 @@ process_recording() (
|
|||||||
printf 'Transcribed %s to %s\n' "$archive" "$transcript"
|
printf 'Transcribed %s to %s\n' "$archive" "$transcript"
|
||||||
)
|
)
|
||||||
|
|
||||||
process_scope() {
|
process_scope() (
|
||||||
local recordings_dir=$1
|
local recordings_dir=$1
|
||||||
local transcript_dir=$2
|
local transcript_dir=$2
|
||||||
local inbox_dir="$recordings_dir/inbox"
|
local inbox_dir="$recordings_dir/inbox"
|
||||||
local processing_dir="$recordings_dir/processing"
|
local processing_dir="$recordings_dir/processing"
|
||||||
local archive_dir="$recordings_dir/archive"
|
local archive_dir="$recordings_dir/archive"
|
||||||
local failed_dir="$recordings_dir/failed"
|
local failed_dir="$recordings_dir/failed"
|
||||||
local source processing_target recording_status scope_status=0
|
local source processing_target recording_status signature index listing scope_status=0
|
||||||
|
local -a sources signatures
|
||||||
|
|
||||||
if ! mkdir -p "$inbox_dir" "$processing_dir" "$archive_dir" "$failed_dir" "$transcript_dir"; then
|
mkdir -p "$inbox_dir" "$processing_dir" "$archive_dir" "$failed_dir" "$transcript_dir" || return 1
|
||||||
return 1
|
listing=$(mktemp "$runtime_dir/recordings.XXXXXX") || return 1
|
||||||
fi
|
trap 'rm -f -- "$listing"' EXIT
|
||||||
|
|
||||||
|
find "$processing_dir" -maxdepth 1 -type f -print0 | sort -z >"$listing" || return 1
|
||||||
while IFS= read -r -d '' source; do
|
while IFS= read -r -d '' source; do
|
||||||
if is_recording "$source"; then
|
if is_recording "$source"; then
|
||||||
|
# Keep this call outside conditionals so its subshell's errexit stays active.
|
||||||
process_recording "$source" "$inbox_dir" "$transcript_dir" "$archive_dir" "$failed_dir"
|
process_recording "$source" "$inbox_dir" "$transcript_dir" "$archive_dir" "$failed_dir"
|
||||||
recording_status=$?
|
recording_status=$?
|
||||||
if [[ $recording_status -ne 0 ]]; then
|
if [[ $recording_status -ne 0 ]]; then
|
||||||
scope_status=1
|
scope_status=1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(find "$processing_dir" -maxdepth 1 -type f -print0 | sort -z)
|
done <"$listing"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sources=()
|
||||||
|
signatures=()
|
||||||
|
|
||||||
|
find "$inbox_dir" -maxdepth 1 -type f -print0 | sort -z >"$listing" || return 1
|
||||||
while IFS= read -r -d '' source; do
|
while IFS= read -r -d '' source; do
|
||||||
if ! is_recording "$source"; then
|
if is_recording "$source"; then
|
||||||
|
if ! signature=$(stat -c '%s:%y:%i' -- "$source"); then
|
||||||
|
if path_exists "$source"; then return 1; fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
sources+=("$source")
|
||||||
|
signatures+=("$signature")
|
||||||
|
fi
|
||||||
|
done <"$listing"
|
||||||
|
|
||||||
|
if [[ ${#sources[@]} -eq 0 ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "${TRANSCRIBE_SETTLE_SECONDS:-300}" || return 1
|
||||||
|
|
||||||
|
for index in "${!sources[@]}"; do
|
||||||
|
source=${sources[$index]}
|
||||||
|
if [[ ! -f "$source" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if ! signature=$(stat -c '%s:%y:%i' -- "$source"); then
|
||||||
|
if path_exists "$source"; then return 1; fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ $signature != "${signatures[$index]}" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
processing_target="$processing_dir/$(basename "$source")"
|
processing_target="$processing_dir/${source##*/}"
|
||||||
if ! move_no_clobber "$source" "$processing_target"; then
|
if ! move_no_clobber "$source" "$processing_target"; then
|
||||||
fail_recording "$source" "$failed_dir" "Processing filename already exists: $processing_target"
|
fail_recording "$source" "$failed_dir" "Could not claim recording: destination collision or move failure: $processing_target" || return 1
|
||||||
scope_status=1
|
scope_status=1
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -235,22 +306,14 @@ process_scope() {
|
|||||||
if [[ $recording_status -ne 0 ]]; then
|
if [[ $recording_status -ne 0 ]]; then
|
||||||
scope_status=1
|
scope_status=1
|
||||||
fi
|
fi
|
||||||
done < <(find "$inbox_dir" -maxdepth 1 -type f -print0 | sort -z)
|
done
|
||||||
|
done
|
||||||
|
|
||||||
return "$scope_status"
|
return "$scope_status"
|
||||||
}
|
)
|
||||||
|
|
||||||
runtime_dir="${XDG_RUNTIME_DIR:?}/transcribe-recordings"
|
runtime_dir="${XDG_RUNTIME_DIR:?}/transcribe-recordings"
|
||||||
mkdir -p "$runtime_dir"
|
mkdir -p "$runtime_dir"
|
||||||
|
|
||||||
if [[ ! -f "$TRANSCRIBE_DIARIZATION_MODEL/.complete" || ! -f "$TRANSCRIBE_DIARIZATION_MODEL/config.yaml" ]]; then
|
|
||||||
printf 'Speaker diarization model is not installed; run prepare-transcription-diarization-model\n' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
set +o errexit
|
set +o errexit
|
||||||
process_scope "$TRANSCRIBE_SYNC_ROOT/recordings" "$TRANSCRIBE_SYNC_ROOT/notes/transcripts"
|
process_scope "$TRANSCRIBE_SYNC_ROOT/recordings" "$TRANSCRIBE_SYNC_ROOT/notes/transcripts"
|
||||||
personal_status=$?
|
|
||||||
process_scope "$TRANSCRIBE_SYNC_ROOT/work/recordings" "$TRANSCRIBE_SYNC_ROOT/work/notes/transcripts"
|
|
||||||
work_status=$?
|
|
||||||
exit "$((personal_status != 0 || work_status != 0))"
|
|
||||||
+1
-2
@@ -1,11 +1,10 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
let
|
let
|
||||||
service = config.facts.services.vaultwarden;
|
service = config.facts.services.vaultwarden;
|
||||||
nixosModules = config.flake.modules.nixos;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.nixos.vaultwarden = {
|
flake.modules.nixos.vaultwarden = {
|
||||||
imports = [ nixosModules.caddy ];
|
imports = [ config.flake.modules.nixos.caddy ];
|
||||||
|
|
||||||
services.vaultwarden = {
|
services.vaultwarden = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{ config, inputs, ... }:
|
|
||||||
let
|
|
||||||
homeModules = config.flake.modules.homeManager;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
flake.modules.nixos.walker = {
|
|
||||||
home-manager.sharedModules = [ homeModules.walker ];
|
|
||||||
|
|
||||||
nix.settings = {
|
|
||||||
extra-substituters = [
|
|
||||||
"https://walker.cachix.org"
|
|
||||||
"https://walker-git.cachix.org"
|
|
||||||
];
|
|
||||||
extra-trusted-public-keys = [
|
|
||||||
"walker.cachix.org-1:fG8q+uAaMqhsMxWjwvk0IMb4mFPFLqHjuvfwQxE4oJM="
|
|
||||||
"walker-git.cachix.org-1:vmC0ocfPWh0S/vRAQGtChuiZBTAe4wiKDeyyXM0/7pM="
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.walker =
|
|
||||||
{ lib, pkgs, ... }:
|
|
||||||
let
|
|
||||||
launcher = pkgs.writeShellApplication {
|
|
||||||
name = "walker-launcher";
|
|
||||||
runtimeInputs = [ pkgs.libressl.nc ];
|
|
||||||
text = ''
|
|
||||||
exec nc -U "$XDG_RUNTIME_DIR/walker/walker.sock"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [ inputs.walker.homeManagerModules.default ];
|
|
||||||
|
|
||||||
desktop.commands.launcher = [ (lib.getExe launcher) ];
|
|
||||||
|
|
||||||
programs.walker = {
|
|
||||||
enable = true;
|
|
||||||
runAsService = true;
|
|
||||||
|
|
||||||
config.providers.prefixes = [
|
|
||||||
{
|
|
||||||
provider = "bitwarden";
|
|
||||||
prefix = "?";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{ config, inputs, ... }:
|
||||||
|
{
|
||||||
|
flake.modules.nixos.walker = {
|
||||||
|
home-manager.sharedModules = [ config.flake.modules.homeManager.walker ];
|
||||||
|
|
||||||
|
nix.settings = {
|
||||||
|
extra-substituters = [
|
||||||
|
"https://walker.cachix.org"
|
||||||
|
"https://walker-git.cachix.org"
|
||||||
|
];
|
||||||
|
extra-trusted-public-keys = [
|
||||||
|
"walker.cachix.org-1:fG8q+uAaMqhsMxWjwvk0IMb4mFPFLqHjuvfwQxE4oJM="
|
||||||
|
"walker-git.cachix.org-1:vmC0ocfPWh0S/vRAQGtChuiZBTAe4wiKDeyyXM0/7pM="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.modules.homeManager.walker =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
walkerDefaults = builtins.fromTOML (builtins.readFile "${inputs.walker}/resources/config.toml");
|
||||||
|
|
||||||
|
launcher = pkgs.writeShellApplication {
|
||||||
|
name = "walker-launcher";
|
||||||
|
runtimeInputs = [ pkgs.libressl.nc ];
|
||||||
|
text = ''
|
||||||
|
exec nc -U "$XDG_RUNTIME_DIR/walker/walker.sock"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.walker.homeManagerModules.default ];
|
||||||
|
|
||||||
|
desktop.commands.launcher = [ (lib.getExe launcher) ];
|
||||||
|
programs.walker = {
|
||||||
|
enable = true;
|
||||||
|
runAsService = true;
|
||||||
|
|
||||||
|
config.theme = "desktop";
|
||||||
|
themes.desktop = {
|
||||||
|
layouts.layout =
|
||||||
|
lib.replaceStrings
|
||||||
|
[ ''<property name="height-request">570</property>'' ]
|
||||||
|
[ ''<property name="height-request">460</property>'' ]
|
||||||
|
(builtins.readFile "${inputs.walker}/resources/themes/default/layout.xml");
|
||||||
|
style = lib.mkBefore (
|
||||||
|
builtins.readFile "${inputs.walker}/resources/themes/default/style.css"
|
||||||
|
+ ''
|
||||||
|
.box-wrapper {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
.search-container, .item-box, .preview { border-radius: 6px; }
|
||||||
|
.input { padding: 8px 10px; }
|
||||||
|
.item-box { padding: 5px 8px; }
|
||||||
|
.large-icons { -gtk-icon-size: 24px; }
|
||||||
|
.item-image-text { font-size: 24px; }
|
||||||
|
.item-subtext { opacity: 0.75; }
|
||||||
|
.item-quick-activation { padding: 3px 5px; }
|
||||||
|
.keybinds { padding-top: 6px; }
|
||||||
|
.keybind-bind { opacity: 0.65; }
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
config.providers = {
|
||||||
|
default = walkerDefaults.providers.default;
|
||||||
|
prefixes = walkerDefaults.providers.prefixes ++ [
|
||||||
|
{
|
||||||
|
provider = "bitwarden";
|
||||||
|
prefix = "?";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
# Wardrowbe on Orion
|
||||||
|
|
||||||
|
[Wardrowbe](https://github.com/Anyesh/wardrowbe) is served through the existing
|
||||||
|
Caddy instance. The capability owns container images, routing, runtime secret
|
||||||
|
templates and startup ordering. Image references and settings live in
|
||||||
|
`default.nix`; endpoints live in the shared service facts.
|
||||||
|
|
||||||
|
Internal AI is disabled. Upload clothes and enter tags manually; automatic tagging
|
||||||
|
and AI outfit generation are unavailable. Local rotation and background removal
|
||||||
|
remain available. No AI provider key or notification provider is configured.
|
||||||
|
|
||||||
|
Tinyauth provides a cookie-based password gateway. After authenticating there,
|
||||||
|
Wardrowbe's development login accepts an email/name to select a profile. Use a
|
||||||
|
consistent email for each person. This is for a trusted household: gateway users
|
||||||
|
can select one another's profiles. It is not verified individual identity.
|
||||||
|
|
||||||
|
## Provision before building or activating Orion
|
||||||
|
|
||||||
|
Add these top-level keys with `sops edit modules/secrets/secrets.yaml`, using the
|
||||||
|
existing system-secret recipients. The keys are deliberately not provisioned by
|
||||||
|
the configuration change; Orion's SOPS build checks require them to exist.
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| `wardrowbe-postgres-password` | A new 64-character hexadecimal password |
|
||||||
|
| `wardrowbe-secret-key` | A separate new 64-character hexadecimal signing secret |
|
||||||
|
| `wardrowbe-nextauth-secret` | A separate new 64-character hexadecimal session secret |
|
||||||
|
| `wardrowbe-auth-users` | A single-line `kiri:bcrypt-hash` string |
|
||||||
|
|
||||||
|
Generate each of the first three separately with `openssl rand -hex 32`. If
|
||||||
|
OpenSSL is unavailable, use `nix run --quiet nixpkgs#openssl -- rand -hex 32`.
|
||||||
|
Keep values inside the encrypted document; do not paste them into Nix or chat.
|
||||||
|
Hexadecimal database passwords work directly in the application's connection URL.
|
||||||
|
|
||||||
|
On a workstation with Podman, create the gateway user interactively:
|
||||||
|
|
||||||
|
```console
|
||||||
|
podman run --rm -it ghcr.io/tinyauthapp/tinyauth:v5.2.0@sha256:220aa235c76393a73fece831d7ba967b8698d3907fd8e3c4c952f699c7b142a5 user create --interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
Choose username `kiri` and your password, and answer **No** to Docker formatting.
|
||||||
|
Put the emitted `username:hash` string
|
||||||
|
in `wardrowbe-auth-users` as a quoted YAML scalar. Additional gateway accounts can
|
||||||
|
be added as a comma-separated list on the same line. If Podman is unavailable,
|
||||||
|
prefix its arguments with `nix run --quiet nixpkgs#podman --` instead of `podman`.
|
||||||
|
This command only creates a hash; gateway sessions persist separately on Orion.
|
||||||
|
|
||||||
|
Create DNS records for `wardrobe.jelles.net` and `wardrobe-auth.jelles.net` pointing
|
||||||
|
at Orion. Publish an AAAA record only if Orion has working public IPv6. Existing
|
||||||
|
Caddy firewall access supplies HTTP/HTTPS and automatic certificates.
|
||||||
|
|
||||||
|
The private container network reserves `10.89.30.0/24`, with gateway
|
||||||
|
`10.89.30.1`. It must not overlap Orion's routes. If changing it, update both the
|
||||||
|
network declaration and Tinyauth's trusted-proxy address before first activation.
|
||||||
|
|
||||||
|
Add new source files to Git before using a Git-backed flake. From this repository,
|
||||||
|
activate explicitly:
|
||||||
|
|
||||||
|
```console
|
||||||
|
nixos-rebuild switch --quiet --target-host orion --sudo --flake .#orion
|
||||||
|
```
|
||||||
|
|
||||||
|
The initial activation downloads container images, including the image-processing
|
||||||
|
model. Allow time and disk space for those downloads. Containers start on boot;
|
||||||
|
PostgreSQL and Redis become healthy before the backend runs `alembic upgrade head`.
|
||||||
|
The frontend and workers wait for the backend's health check. A failed migration
|
||||||
|
prevents the API from starting. Each container has a 15-minute startup deadline.
|
||||||
|
|
||||||
|
## Operation
|
||||||
|
|
||||||
|
Run these commands on Orion. Container names and unit names correspond directly
|
||||||
|
to the declarations in `default.nix`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
sudo systemctl status podman-wardrowbe-backend.service
|
||||||
|
sudo journalctl -u podman-wardrowbe-backend.service -n 80 --no-pager
|
||||||
|
sudo podman ps --filter name=wardrowbe
|
||||||
|
sudo podman volume ls --filter name=wardrowbe
|
||||||
|
```
|
||||||
|
|
||||||
|
Persistent named volumes contain PostgreSQL, Redis's append-only queue, uploaded
|
||||||
|
images and Tinyauth's SQLite sessions. They are root-owned Podman state under
|
||||||
|
`/var/lib/containers/storage/volumes`, outside Nix. Container recreation and system
|
||||||
|
rollback preserve these volumes; do not remove them or prune volumes. The network
|
||||||
|
is also retained when services stop.
|
||||||
|
|
||||||
|
Only Caddy serves public traffic. Frontend, backend and gateway host ports bind to
|
||||||
|
localhost; database and queue ports remain inside the container network. Every
|
||||||
|
Wardrowbe route, including API and signed image URLs, passes through the gateway.
|
||||||
|
The gateway's own login endpoint is public. Gateway cookies are secure, HTTP-only
|
||||||
|
and scoped to `jelles.net` to support redirects between the two subdomains.
|
||||||
|
|
||||||
|
SOPS renders root-only mode-0400 environment files at activation. Changes to their
|
||||||
|
contents restart the consuming containers. Rotating session secrets signs users out.
|
||||||
|
Changing `POSTGRES_PASSWORD` alone does **not** change an existing database role's
|
||||||
|
password: coordinate the database password change with the encrypted value.
|
||||||
|
|
||||||
|
## Upgrade and recovery
|
||||||
|
|
||||||
|
Review upstream release notes and migrations, then update the image tags and
|
||||||
|
manifest digests together. Upgrade backend and frontend to the same release;
|
||||||
|
workers use the backend image. Image updates happen through explicit configuration
|
||||||
|
activation, with no automatic image updater. Review resource requirements before
|
||||||
|
enabling AI later.
|
||||||
|
|
||||||
|
If startup fails, inspect the failing container unit's journal. After correcting
|
||||||
|
the cause, start the affected units and any dependents whose startup was blocked.
|
||||||
|
To restart the complete application in dependency order:
|
||||||
|
|
||||||
|
```console
|
||||||
|
sudo systemctl restart podman-wardrowbe-postgres.service podman-wardrowbe-redis.service podman-wardrowbe-backend.service podman-wardrowbe-worker.service podman-wardrowbe-image-worker.service podman-wardrowbe-frontend.service podman-wardrowbe-auth.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Reverting a NixOS generation restores configuration and image references, not
|
||||||
|
database schema changes. Do not assume an older application can use a migrated
|
||||||
|
database; follow upstream recovery guidance. No backup system is added here.
|
||||||
|
|
||||||
|
## Manual acceptance after activation
|
||||||
|
|
||||||
|
- Open the wardrobe in a private browser window. It should require the gateway
|
||||||
|
password; unauthenticated API and image requests should also be blocked.
|
||||||
|
- Authenticate, select a Wardrowbe profile, complete onboarding and upload a photo.
|
||||||
|
Add tags manually and confirm the photo renders; try a rotation.
|
||||||
|
- With the gateway session active, open `/api/v1/capabilities` on the wardrobe
|
||||||
|
origin. Both internal AI capabilities should be `false`.
|
||||||
|
- Restart the application and confirm the profile, photo and tags remain present.
|
||||||
|
- Check that both public hostnames have valid HTTPS certificates and that the
|
||||||
|
existing Orion web services still work.
|
||||||
|
|
||||||
|
No tests, builds, activation or browser acceptance checks were performed while
|
||||||
|
preparing this capability.
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
nixosModules = config.flake.modules.nixos;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.modules.nixos.wardrowbe =
|
||||||
|
{ config, lib, ... }:
|
||||||
|
let
|
||||||
|
service = config.facts.services.wardrowbe;
|
||||||
|
auth = config.facts.services.wardrowbe-auth;
|
||||||
|
podman = lib.getExe config.virtualisation.podman.package;
|
||||||
|
# Multi-architecture manifest digests; updating a tag alone cannot change an image.
|
||||||
|
images = {
|
||||||
|
backend = "ghcr.io/anyesh/wardrowbe:backend-1.10.3@sha256:75fd6f85fb7b91ccbb227b52d616bab11b5130c4e22858579f45736419fb592d";
|
||||||
|
frontend = "ghcr.io/anyesh/wardrowbe:frontend-1.10.3@sha256:730cf52bd8a03b94473b129e9a988b3dae9818e9377fd88917029156ee7a2e17";
|
||||||
|
auth = "ghcr.io/tinyauthapp/tinyauth:v5.2.0@sha256:220aa235c76393a73fece831d7ba967b8698d3907fd8e3c4c952f699c7b142a5";
|
||||||
|
postgres = "docker.io/library/postgres:15-alpine@sha256:f7d23353e1b15400d22ebe31189f4d314b87a4c129cc400c8c2d8d4ca127bf81";
|
||||||
|
redis = "docker.io/library/redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499";
|
||||||
|
};
|
||||||
|
healthOptions = [
|
||||||
|
"--health-interval=10s"
|
||||||
|
"--health-timeout=5s"
|
||||||
|
"--health-start-period=30s"
|
||||||
|
"--health-retries=6"
|
||||||
|
"--health-on-failure=kill"
|
||||||
|
];
|
||||||
|
backendContainer = {
|
||||||
|
image = images.backend;
|
||||||
|
environment = {
|
||||||
|
# Profile selection is available only behind the password gateway.
|
||||||
|
DEBUG = "true";
|
||||||
|
AI_INTERNAL_ENABLED = "false";
|
||||||
|
AI_VISION_ENABLED = "false";
|
||||||
|
AI_TEXT_ENABLED = "false";
|
||||||
|
APP_URL = service.origin;
|
||||||
|
CORS_ORIGINS = builtins.toJSON [ service.origin ];
|
||||||
|
REDIS_URL = "redis://wardrowbe-redis:6379/0";
|
||||||
|
STORAGE_PATH = "/data/uploads";
|
||||||
|
LOG_LEVEL = "INFO";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.sops.templates."wardrowbe-backend.env".path ];
|
||||||
|
volumes = [ "wardrowbe-uploads:/data/uploads" ];
|
||||||
|
};
|
||||||
|
backendUnits = [
|
||||||
|
"podman-wardrowbe-backend.service"
|
||||||
|
"podman-wardrowbe-worker.service"
|
||||||
|
"podman-wardrowbe-image-worker.service"
|
||||||
|
];
|
||||||
|
containers = {
|
||||||
|
wardrowbe-postgres = {
|
||||||
|
image = images.postgres;
|
||||||
|
environment = {
|
||||||
|
POSTGRES_USER = "wardrobe";
|
||||||
|
POSTGRES_DB = "wardrobe";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.sops.templates."wardrowbe-postgres.env".path ];
|
||||||
|
volumes = [ "wardrowbe-postgres:/var/lib/postgresql/data" ];
|
||||||
|
podman.sdnotify = "healthy";
|
||||||
|
extraOptions = healthOptions ++ [
|
||||||
|
"--health-cmd=pg_isready -h 127.0.0.1 -U wardrobe -d wardrobe"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
wardrowbe-redis = {
|
||||||
|
image = images.redis;
|
||||||
|
cmd = [
|
||||||
|
"redis-server"
|
||||||
|
"--appendonly"
|
||||||
|
"yes"
|
||||||
|
];
|
||||||
|
volumes = [ "wardrowbe-redis:/data" ];
|
||||||
|
podman.sdnotify = "healthy";
|
||||||
|
extraOptions = healthOptions ++ [ "--health-cmd=redis-cli ping" ];
|
||||||
|
};
|
||||||
|
wardrowbe-backend = backendContainer // {
|
||||||
|
dependsOn = [
|
||||||
|
"wardrowbe-postgres"
|
||||||
|
"wardrowbe-redis"
|
||||||
|
];
|
||||||
|
ports = [ "127.0.0.1:8002:8000" ];
|
||||||
|
cmd = [
|
||||||
|
"sh"
|
||||||
|
"-c"
|
||||||
|
"alembic upgrade head && exec uvicorn app.main:app --host 0.0.0.0 --port 8000"
|
||||||
|
];
|
||||||
|
podman.sdnotify = "healthy";
|
||||||
|
extraOptions = healthOptions ++ [
|
||||||
|
"--health-cmd=curl --fail --silent http://127.0.0.1:8000/api/v1/health"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
wardrowbe-worker = backendContainer // {
|
||||||
|
dependsOn = [ "wardrowbe-backend" ];
|
||||||
|
cmd = [
|
||||||
|
"arq"
|
||||||
|
"app.workers.worker.WorkerSettings"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
wardrowbe-image-worker = backendContainer // {
|
||||||
|
dependsOn = [ "wardrowbe-backend" ];
|
||||||
|
cmd = [
|
||||||
|
"arq"
|
||||||
|
"app.workers.image_worker.ImageWorkerSettings"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
wardrowbe-frontend = {
|
||||||
|
image = images.frontend;
|
||||||
|
dependsOn = [ "wardrowbe-backend" ];
|
||||||
|
environment = {
|
||||||
|
BACKEND_URL = "http://wardrowbe-backend:8000";
|
||||||
|
NEXTAUTH_URL = service.origin;
|
||||||
|
DEV_MODE = "true";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.sops.templates."wardrowbe-frontend.env".path ];
|
||||||
|
ports = [ "127.0.0.1:3002:3000" ];
|
||||||
|
podman.sdnotify = "healthy";
|
||||||
|
extraOptions = healthOptions ++ [
|
||||||
|
"--health-cmd=node -e \"fetch('http://127.0.0.1:3000').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\""
|
||||||
|
];
|
||||||
|
};
|
||||||
|
wardrowbe-auth = {
|
||||||
|
image = images.auth;
|
||||||
|
environment = {
|
||||||
|
TINYAUTH_APPURL = auth.origin;
|
||||||
|
TINYAUTH_AUTH_SECURECOOKIE = "true";
|
||||||
|
TINYAUTH_AUTH_TRUSTEDPROXIES = "10.89.30.1";
|
||||||
|
TINYAUTH_LABELPROVIDER = "none";
|
||||||
|
TINYAUTH_ANALYTICS_ENABLED = "false";
|
||||||
|
TINYAUTH_DATABASE_PATH = "/data/tinyauth.db";
|
||||||
|
TINYAUTH_UI_TITLE = "Wardrobe login";
|
||||||
|
};
|
||||||
|
environmentFiles = [ config.sops.templates."wardrowbe-auth.env".path ];
|
||||||
|
volumes = [ "wardrowbe-auth:/data" ];
|
||||||
|
ports = [ "127.0.0.1:3003:3000" ];
|
||||||
|
podman.sdnotify = "healthy";
|
||||||
|
extraOptions = healthOptions ++ [ "--health-cmd=tinyauth healthcheck" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
nixosModules.caddy
|
||||||
|
nixosModules.sops
|
||||||
|
];
|
||||||
|
|
||||||
|
sops.secrets = {
|
||||||
|
wardrowbe-postgres-password = { };
|
||||||
|
wardrowbe-secret-key = { };
|
||||||
|
wardrowbe-nextauth-secret = { };
|
||||||
|
wardrowbe-auth-users = { };
|
||||||
|
};
|
||||||
|
sops.templates = {
|
||||||
|
"wardrowbe-postgres.env" = {
|
||||||
|
content = ''
|
||||||
|
POSTGRES_PASSWORD=${config.sops.placeholder.wardrowbe-postgres-password}
|
||||||
|
'';
|
||||||
|
restartUnits = [ "podman-wardrowbe-postgres.service" ];
|
||||||
|
};
|
||||||
|
"wardrowbe-backend.env" = {
|
||||||
|
content = ''
|
||||||
|
DATABASE_URL=postgresql+asyncpg://wardrobe:${config.sops.placeholder.wardrowbe-postgres-password}@wardrowbe-postgres:5432/wardrobe
|
||||||
|
SECRET_KEY=${config.sops.placeholder.wardrowbe-secret-key}
|
||||||
|
'';
|
||||||
|
restartUnits = backendUnits;
|
||||||
|
};
|
||||||
|
"wardrowbe-frontend.env" = {
|
||||||
|
content = ''
|
||||||
|
NEXTAUTH_SECRET=${config.sops.placeholder.wardrowbe-nextauth-secret}
|
||||||
|
'';
|
||||||
|
restartUnits = [ "podman-wardrowbe-frontend.service" ];
|
||||||
|
};
|
||||||
|
"wardrowbe-auth.env" = {
|
||||||
|
content = ''
|
||||||
|
TINYAUTH_AUTH_USERS=${config.sops.placeholder.wardrowbe-auth-users}
|
||||||
|
'';
|
||||||
|
restartUnits = [ "podman-wardrowbe-auth.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers = {
|
||||||
|
backend = "podman";
|
||||||
|
containers = lib.mapAttrs (
|
||||||
|
_: container:
|
||||||
|
{
|
||||||
|
networks = [ "wardrowbe" ];
|
||||||
|
}
|
||||||
|
// container
|
||||||
|
) containers;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services = {
|
||||||
|
wardrowbe-network = {
|
||||||
|
description = "Wardrowbe container network";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
${podman} network exists wardrowbe || \
|
||||||
|
${podman} network create --subnet 10.89.30.0/24 --gateway 10.89.30.1 wardrowbe
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// lib.mapAttrs' (
|
||||||
|
name: _:
|
||||||
|
lib.nameValuePair "podman-${name}" {
|
||||||
|
requires = [ "wardrowbe-network.service" ];
|
||||||
|
after = [ "wardrowbe-network.service" ];
|
||||||
|
serviceConfig.TimeoutStartSec = lib.mkForce 900;
|
||||||
|
}
|
||||||
|
) containers;
|
||||||
|
|
||||||
|
services.caddy.virtualHosts = {
|
||||||
|
${auth.domain}.extraConfig = "reverse_proxy 127.0.0.1:3003";
|
||||||
|
${service.domain}.extraConfig = ''
|
||||||
|
request_body {
|
||||||
|
max_size 50MB
|
||||||
|
}
|
||||||
|
route {
|
||||||
|
forward_auth 127.0.0.1:3003 {
|
||||||
|
uri /api/auth/caddy
|
||||||
|
}
|
||||||
|
handle /api/auth/* {
|
||||||
|
reverse_proxy 127.0.0.1:3002
|
||||||
|
}
|
||||||
|
handle /api/* {
|
||||||
|
reverse_proxy 127.0.0.1:8002
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
reverse_proxy 127.0.0.1:3002
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Loaded 100 of 128 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user