@marsaude/devtools-shell 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# @marsaude/devtools-shell
|
|
2
|
+
|
|
3
|
+
A **content-agnostic, draggable floating-action-button shell** for dev-only tools.
|
|
4
|
+
|
|
5
|
+
It ships only the _invólucro_: a FAB you can drag anywhere on screen (mouse +
|
|
6
|
+
touch, position persisted across reloads, clamped to the viewport) that opens a
|
|
7
|
+
container. **The panel content is yours** — plugged in from the outside. The
|
|
8
|
+
shell has zero knowledge of auth, APIs, or any business domain.
|
|
9
|
+
|
|
10
|
+
- Angular standalone + Signals + `@if`/`@for` control flow. No NgModules.
|
|
11
|
+
- Drag/animations ported verbatim from the `DevTools FAB` prototype — plain
|
|
12
|
+
Pointer Events, no external drag library.
|
|
13
|
+
- Auto-mounts itself (`createComponent + ApplicationRef.attachView` from an
|
|
14
|
+
`APP_BOOTSTRAP_LISTENER`) — no tag to place in a template.
|
|
15
|
+
|
|
16
|
+
**Shell behaviour (all domain-free):** drag + snap-to-edge + persisted position;
|
|
17
|
+
idle → collapse into a thin edge grip (tap to restore); tap → radial speed-dial
|
|
18
|
+
of the registered actions (or open directly when there's a single action); pick
|
|
19
|
+
an action → its content renders in a side-drawer / bottom-sheet.
|
|
20
|
+
|
|
21
|
+
**Toast:** inject `DevtoolsToastService` anywhere and call `show('…')` to flash a
|
|
22
|
+
message inside the shell layer (no Material dependency):
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { DevtoolsToastService } from '@marsaude/devtools-shell';
|
|
26
|
+
private readonly toast = inject(DevtoolsToastService);
|
|
27
|
+
this.toast.show('Usuário gerado');
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm i @marsaude/devtools-shell
|
|
36
|
+
# peers (already present in this workspace):
|
|
37
|
+
npm i @angular/core @angular/common
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Material Symbols are used for glyphs. Load them once in the host app if you want
|
|
41
|
+
the icons to render:
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<link
|
|
45
|
+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined"
|
|
46
|
+
rel="stylesheet"
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Mount the shell
|
|
51
|
+
|
|
52
|
+
Add `provideDevtools()` to your application providers.
|
|
53
|
+
|
|
54
|
+
### Standalone bootstrap
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
58
|
+
import { provideDevtools } from '@marsaude/devtools-shell';
|
|
59
|
+
import { environment } from './environments/environment';
|
|
60
|
+
|
|
61
|
+
bootstrapApplication(AppComponent, {
|
|
62
|
+
providers: [
|
|
63
|
+
provideDevtools({
|
|
64
|
+
enabled: !environment.production, // Layer-1 gate (see below)
|
|
65
|
+
title: 'DevTools',
|
|
66
|
+
actions: [],
|
|
67
|
+
}),
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### NgModule app (this repo)
|
|
73
|
+
|
|
74
|
+
`provideDevtools()` returns `EnvironmentProviders`, valid in `@NgModule.providers`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
@NgModule({
|
|
78
|
+
// ...
|
|
79
|
+
providers: [
|
|
80
|
+
provideDevtools({ enabled: !environment.production }),
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
export class AppModule {}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
That's all — a draggable FAB appears, and clicking it opens an (empty) panel.
|
|
87
|
+
|
|
88
|
+
## Pass the interface later (the extension point)
|
|
89
|
+
|
|
90
|
+
The panel content is supplied as **actions**. Each action is a standalone
|
|
91
|
+
component (or a `TemplateRef`) — the shell just renders it.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { provideDevtools } from '@marsaude/devtools-shell';
|
|
95
|
+
import { MyUserGeneratorPanel } from './devtools/user-generator-panel';
|
|
96
|
+
|
|
97
|
+
provideDevtools({
|
|
98
|
+
enabled: !environment.production,
|
|
99
|
+
actions: [
|
|
100
|
+
{ id: 'gen', label: 'Gerador', icon: 'groups', content: MyUserGeneratorPanel },
|
|
101
|
+
{ id: 'login', label: 'Logar', icon: 'login', content: MyLoginPanel },
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **One action** → it opens directly in the container.
|
|
107
|
+
- **Multiple actions** → the shell renders a tab switcher in the panel header.
|
|
108
|
+
- Your panel component is rendered with `NgComponentOutlet`; it can inject its
|
|
109
|
+
own services normally.
|
|
110
|
+
|
|
111
|
+
### Two doors for content
|
|
112
|
+
|
|
113
|
+
1. **Action registry (primary).** Shown above. This is the recommended path
|
|
114
|
+
because the shell auto-mounts onto `document.body` — it has no place in your
|
|
115
|
+
template, so there is nowhere to project into.
|
|
116
|
+
2. **Content projection (secondary).** Only when you place the component
|
|
117
|
+
yourself instead of using `provideDevtools` auto-mount:
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<devtools-shell>
|
|
121
|
+
<my-panel />
|
|
122
|
+
</devtools-shell>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`<ng-content>` is rendered when no actions are registered.
|
|
126
|
+
|
|
127
|
+
> **Why the registry is primary:** auto-mounting via `createComponent` (the
|
|
128
|
+
> pattern reused from the original DevTools boot flow) means the shell lives
|
|
129
|
+
> outside any consumer template. `<ng-content>` requires the consumer to host
|
|
130
|
+
> the tag, which contradicts auto-mount. The token-based registry decouples
|
|
131
|
+
> _where the shell lives_ from _who supplies the content_.
|
|
132
|
+
|
|
133
|
+
## Re-skinning
|
|
134
|
+
|
|
135
|
+
The shell ships the dark theme from the original mockup, exposed as CSS custom
|
|
136
|
+
properties on the host element. Override them anywhere:
|
|
137
|
+
|
|
138
|
+
```css
|
|
139
|
+
[data-devtools-shell-host] devtools-shell {
|
|
140
|
+
--dts-bg: #14151b;
|
|
141
|
+
--dts-accent: #ffc454;
|
|
142
|
+
--dts-fg: #eceef3;
|
|
143
|
+
--dts-font: system-ui, sans-serif;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Production gating
|
|
150
|
+
|
|
151
|
+
**The shell must never reach a production bundle.** Two layers:
|
|
152
|
+
|
|
153
|
+
### Layer 1 — runtime flag (always on)
|
|
154
|
+
|
|
155
|
+
Pass `enabled: !environment.production`. When `false`, `provideDevtools()`
|
|
156
|
+
returns _no_ providers: nothing mounts, no actions register.
|
|
157
|
+
|
|
158
|
+
### Layer 2 — build-time elimination (recommended)
|
|
159
|
+
|
|
160
|
+
Layer 1 still leaves the shell _imported_. To drop it from the bundle entirely,
|
|
161
|
+
isolate the wiring in one file and swap it via `fileReplacements`.
|
|
162
|
+
|
|
163
|
+
`src/app/devtools/devtools.providers.ts` (dev):
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { EnvironmentProviders } from '@angular/core';
|
|
167
|
+
import { provideDevtools } from '@marsaude/devtools-shell';
|
|
168
|
+
import { UserGeneratorPanel } from './user-generator-panel';
|
|
169
|
+
|
|
170
|
+
export function devtoolsProviders(): EnvironmentProviders[] {
|
|
171
|
+
return [
|
|
172
|
+
provideDevtools({
|
|
173
|
+
enabled: true,
|
|
174
|
+
actions: [{ id: 'gen', label: 'Gerador', icon: 'groups', content: UserGeneratorPanel }],
|
|
175
|
+
}),
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`src/app/devtools/devtools.providers.prod.ts` (prod no-op — imports nothing):
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
export function devtoolsProviders() {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`angular.json` (production configuration):
|
|
189
|
+
|
|
190
|
+
```jsonc
|
|
191
|
+
"fileReplacements": [
|
|
192
|
+
{ "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" },
|
|
193
|
+
{ "replace": "src/app/devtools/devtools.providers.ts", "with": "src/app/devtools/devtools.providers.prod.ts" }
|
|
194
|
+
]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Use it in bootstrap:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { devtoolsProviders } from './app/devtools/devtools.providers';
|
|
201
|
+
// providers: [ ...devtoolsProviders() ]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Because the prod file imports neither `@marsaude/devtools-shell` nor your panel
|
|
205
|
+
components, the bundler tree-shakes the whole shell out of the production build.
|
|
206
|
+
|
|
207
|
+
### Verification checklist
|
|
208
|
+
|
|
209
|
+
- [ ] `provideDevtools({ enabled: !environment.production })` — Layer 1 in place.
|
|
210
|
+
- [ ] Wiring isolated in `devtools.providers.ts` with a `.prod.ts` no-op twin.
|
|
211
|
+
- [ ] `fileReplacements` entry for the providers file added to the **production**
|
|
212
|
+
configuration in `angular.json`.
|
|
213
|
+
- [ ] `environment.prod.ts` actually has `production: true` for the real prod env.
|
|
214
|
+
- [ ] Run a prod build and confirm the shell is gone:
|
|
215
|
+
`ng build --configuration production` then
|
|
216
|
+
`grep -r "devtools-shell\|data-devtools-shell-host" dist/` returns nothing.
|
|
217
|
+
- [ ] Load the prod bundle: no FAB on screen, no `[data-devtools-shell-host]`
|
|
218
|
+
element in the DOM.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Build & publish (public npm — scope `@marsaude`)
|
|
223
|
+
|
|
224
|
+
Published as a **public** scoped package on npmjs (free). You must be logged in
|
|
225
|
+
as a user that owns the `@marsaude` scope.
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# one-time: authenticate against npmjs
|
|
229
|
+
npm login # npm whoami → marsaude
|
|
230
|
+
|
|
231
|
+
# build + publish in one step (from the workspace root)
|
|
232
|
+
npm run publish:lib
|
|
233
|
+
# → runs `ng build devtools-shell` then
|
|
234
|
+
# `npm publish ./dist/devtools-shell --access public`
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Manual equivalent:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
npm run build:lib # → dist/devtools-shell
|
|
241
|
+
npm publish ./dist/devtools-shell --access public # note the ./ prefix
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Bump the version in `projects/devtools-shell/package.json` before each release
|
|
245
|
+
(`0.1.0` → `0.1.1` …). `peerDependencies`: `@angular/core`, `@angular/common`
|
|
246
|
+
(^21).
|
|
247
|
+
|
|
248
|
+
> `--access public` is required for the first publish of a scoped package on the
|
|
249
|
+
> free npm plan (private/restricted needs a paid npm plan → `E402`).
|