@polymorphism-tech/morph-spec 9.7.0 → 10.0.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/CLAUDE.md +11 -11
- package/README.md +4 -4
- package/bin/morph-spec.js +41 -44
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +24 -30
- package/framework/MORPH.md +47 -123
- package/framework/agents.json +6 -374
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-archive.md +4 -3
- package/framework/commands/morph-hotfix.md +8 -30
- package/framework/commands/morph-infra.md +2 -2
- package/framework/commands/morph-media.md +10 -9
- package/framework/commands/morph-review.md +15 -0
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +100 -102
- package/framework/evals/review-checklist.md +46 -54
- package/framework/hooks/README.md +4 -4
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/hooks/claude-code/core/README.md +1 -1
- package/framework/hooks/claude-code/core/denylist.json +4 -4
- package/framework/hooks/claude-code/core/gate-guard.js +9 -0
- package/framework/hooks/claude-code/core/masked-exit.js +49 -0
- package/framework/hooks/claude-code/core/state-sync.js +4 -1
- package/framework/hooks/claude-code/core/telemetry-log.js +1 -2
- package/framework/hooks/claude-code/core/test-budget.js +10 -70
- package/framework/hooks/claude-code/core/validate-completion.js +11 -24
- package/framework/hooks/claude-code/core/worktree-context.js +5 -22
- package/framework/hooks/claude-code/core/worktree-create.js +6 -45
- package/framework/hooks/claude-code/core/worktree-env-inject.js +2 -10
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/gate-event.js +2 -2
- package/framework/hooks/shared/graph-refresh.js +20 -3
- package/framework/hooks/shared/heavy-command.js +32 -1
- package/framework/hooks/shared/heavy-config.js +1 -1
- package/framework/hooks/shared/heavy-slots.js +41 -4
- package/framework/hooks/shared/next-step.js +63 -24
- package/framework/hooks/shared/phase-utils.js +6 -9
- package/framework/hooks/shared/report-progress.js +86 -0
- package/framework/hooks/shared/state-reader.js +3 -2
- package/framework/hooks/statusline.js +332 -0
- package/framework/phases.json +32 -10
- package/framework/rules/ai-agents-standards.md +9 -1
- package/framework/rules/csharp-standards.md +4 -0
- package/framework/rules/ef-standards.md +20 -0
- package/framework/rules/frontend-standards.md +1 -1
- package/framework/rules/implement-report.md +25 -0
- package/framework/rules/infrastructure-standards.md +2 -0
- package/framework/rules/nextjs-standards.md +7 -0
- package/framework/rules/plan-grammar.md +53 -0
- package/framework/rules/ui-design.md +27 -0
- package/framework/schemas/config.schema.json +12 -19
- package/framework/schemas/feature.schema.json +7 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/create-umbraco-lp/references/umbraco-lp-patterns.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +2 -2
- package/framework/skills/core/morph-apply/SKILL.md +38 -166
- package/framework/skills/core/morph-apply/references/debugging.md +8 -0
- package/framework/skills/core/morph-apply/references/hotfix.md +42 -0
- package/framework/skills/core/morph-apply/references/machine.md +29 -0
- package/framework/skills/core/{morph-media/SKILL.md → morph-apply/references/media.md} +11 -74
- package/framework/skills/core/morph-apply/references/review.md +65 -0
- package/framework/skills/core/morph-plan/SKILL.md +42 -108
- package/framework/skills/core/morph-plan/references/critique.md +40 -0
- package/framework/skills/core/morph-plan/references/interview.md +25 -0
- package/framework/skills/core/morph-uiux/SKILL.md +47 -64
- package/framework/skills/core/morph-uiux/surfaces.json +32 -0
- package/framework/standards/STANDARDS.json +32 -5
- package/framework/standards/STANDARDS.meta.json +5 -1
- package/framework/standards/ai-agents/agent-session.md +2 -3
- package/framework/standards/ai-agents/context-providers.md +2 -2
- package/framework/standards/ai-agents/durable-workflows-hitl.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +2 -2
- package/framework/standards/ai-agents/rag-custom-pgvector.md +6 -6
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/tool-adapters.md +2 -2
- package/framework/standards/architecture/stack-baseline.md +47 -0
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -4
- package/framework/standards/frontend/design-system/ai-image-generation.md +51 -12
- package/framework/standards/frontend/design-system/ai-video-generation.md +50 -4
- package/framework/standards/frontend/design-system/art-direction.md +2 -2
- package/framework/standards/frontend/scroll-driven/frame-scrub.md +12 -2
- package/framework/standards/frontend/scroll-driven/scroll-components.md +76 -4
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +1 -2
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/context/ARCHITECTURE.md +1 -1
- package/framework/templates/docs/plan.md +12 -12
- package/framework/templates/docs/ui-mockups.md +17 -105
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/e2e/e2e.js +88 -23
- package/src/commands/graph/diff.js +74 -0
- package/src/commands/graph/status.js +1 -1
- package/src/commands/observability/cost.js +2 -4
- package/src/commands/observability/telemetry.js +1 -1
- package/src/commands/project/doctor.js +23 -39
- package/src/commands/project/fleet.js +5 -6
- package/src/commands/project/init.js +63 -36
- package/src/commands/project/stack-scan.js +3 -42
- package/src/commands/project/stack-set.js +26 -0
- package/src/commands/project/status.js +12 -13
- package/src/commands/project/update.js +39 -3
- package/src/commands/project/worktree.js +27 -16
- package/src/commands/state/advance.js +17 -17
- package/src/commands/state/approve.js +0 -6
- package/src/commands/state/archive.js +27 -1
- package/src/commands/state/auto-approval.js +1 -1
- package/src/commands/state/create.js +0 -5
- package/src/commands/state/finish.js +43 -1
- package/src/commands/state/gate-check.js +37 -30
- package/src/commands/state/gate-decision.js +3 -2
- package/src/commands/state/next.js +1 -0
- package/src/commands/state/recap.js +67 -0
- package/src/commands/state/report-check.js +39 -146
- package/src/commands/validation/plan-check.js +16 -11
- package/src/commands/validation/validate-feature.js +3 -3
- package/src/commands/validation/verify.js +6 -6
- package/src/core/paths/output-schema.js +3 -3
- package/src/core/state/state-manager.js +2 -4
- package/src/dashboard/app/components/CommandPalette.jsx +2 -5
- package/src/dashboard/app/components/GatePauses.jsx +116 -0
- package/src/dashboard/app/components/SessionMap.jsx +77 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/Activity.jsx +4 -0
- package/src/dashboard/app/pages/AgentMap.jsx +1 -2
- package/src/dashboard/app/pages/FeatureDetail.jsx +129 -186
- package/src/dashboard/app/pages/Features.jsx +7 -7
- package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
- package/src/dashboard/app/pages/Portfolio.jsx +40 -5
- package/src/dashboard/scanner/activity-scanner.js +29 -0
- package/src/dashboard/scanner/agents-scanner.js +11 -14
- package/src/dashboard/scanner/features-scanner.js +45 -24
- package/src/dashboard/scanner/project-api.js +11 -2
- package/src/dashboard/scanner/skills-scanner.js +11 -1
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/e2e/applicability.js +3 -1
- package/src/lib/git/tree-state.js +1 -16
- package/src/lib/graph/diff-tree.js +127 -0
- package/src/lib/graph/graphify-runtime.js +12 -5
- package/src/lib/heavy/gate.js +22 -1
- package/src/lib/installers/memory-installer.js +2 -2
- package/src/lib/installers/rules-installer.js +57 -0
- package/src/lib/installers/stack-assets.js +17 -0
- package/src/lib/installers/stack-sync.js +219 -0
- package/src/lib/plan/checks.js +55 -110
- package/src/lib/plan/critique.js +28 -0
- package/src/lib/plan/media.js +42 -0
- package/src/lib/plan/plan-html.js +125 -0
- package/src/lib/plan/plan-parser.js +34 -45
- package/src/lib/recap/piper.js +36 -0
- package/src/lib/recap/recap.js +232 -0
- package/src/lib/review-reports.js +33 -7
- package/src/lib/risk-detector.js +90 -11
- package/src/lib/specs/screen-checklist.js +33 -48
- package/src/lib/stack/dotnet-build-ignores.js +8 -9
- package/src/lib/tasks/test-nodes.js +24 -6
- package/src/lib/tasks/test-runner.js +40 -1
- package/src/lib/telemetry/metrics.js +27 -29
- package/src/lib/validators/reuse/reuse-validator.js +1 -9
- package/src/lib/validators/verify-runner.js +50 -19
- package/src/lib/worktree/fleet-scanner.js +7 -20
- package/src/lib/worktree/worktree-audit.js +0 -37
- package/src/scripts/setup-infra.js +15 -50
- package/src/utils/agents-installer.js +27 -201
- package/src/utils/hooks-installer.js +11 -95
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +0 -23
- package/framework/hooks/claude-code/statusline.py +0 -699
- package/framework/hooks/claude-code/statusline.sh +0 -11
- package/framework/hooks/shared/agent-reap.js +0 -74
- package/framework/hooks/shared/slice-progress.js +0 -110
- package/framework/skills/core/morph-hotfix/SKILL.md +0 -133
- package/framework/skills/core/morph-machine/SKILL.md +0 -46
- package/framework/skills/core/morph-review/SKILL.md +0 -129
- package/framework/skills/core/morph-standards/SKILL.md +0 -52
- package/framework/standards/PROVENANCE.json +0 -119
- package/src/commands/project/worktree-reap.js +0 -34
- package/src/commands/state/handoff.js +0 -65
- package/src/commands/validation/mutate.js +0 -263
- package/src/commands/validation/scout-check.js +0 -28
- package/src/lib/generators/handoff-generator.js +0 -172
- package/src/lib/mutation/adapters.js +0 -79
- package/src/lib/mutation/commands.js +0 -61
- package/src/lib/mutation/dotnet-introspection.js +0 -281
- package/src/lib/mutation/manifest.js +0 -144
- package/src/lib/mutation/plan.js +0 -44
- package/src/lib/mutation/report.js +0 -68
- package/src/lib/mutation/runner.js +0 -301
- package/src/lib/mutation/schemata.js +0 -60
- package/src/lib/mutation/snapshot.js +0 -65
- package/src/lib/mutation/subset.js +0 -109
- package/src/lib/mutation/trx.js +0 -50
- package/src/lib/scout/scout-check.js +0 -293
- package/src/lib/validators/traceability-check.js +0 -103
- package/src/lib/worktree/agent-reaper.js +0 -247
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> **Keywords:** pinned gallery, horizontal scroll, modal, lightbox, parallax, count-up, reveal, scroll progress, accordion
|
|
6
6
|
> **Load When:** ui-designer active, or building a scroll-driven marketing page
|
|
7
7
|
|
|
8
|
-
**Verified against:** GSAP 3.13 (ScrollTrigger, matchMedia, context) + React 18 + Lenis 1.x, shipped to production. Last-verified: 2026-
|
|
8
|
+
**Verified against:** GSAP 3.13 (ScrollTrigger, matchMedia, context) + React 18 + Lenis 1.x, shipped to production. Last-verified: 2026-10-27.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -114,6 +114,19 @@ useEffect(() => {
|
|
|
114
114
|
|
|
115
115
|
**Render overlays outside any pinned section.** A pin applies a `transform` to its container, which becomes the containing block for `position: fixed` and breaks the overlay. Put the modal in a fragment as a sibling of the section.
|
|
116
116
|
|
|
117
|
+
The markup: the backdrop closes on click, the panel stops propagation so a click inside does not close it, and the dialog carries its role.
|
|
118
|
+
|
|
119
|
+
```jsx
|
|
120
|
+
{active !== null && (
|
|
121
|
+
<div className={`lmodal${shown ? ' in' : ''}`} onClick={close} role="dialog" aria-modal="true">
|
|
122
|
+
<div className="lmodal__panel" data-lenis-prevent onClick={(e) => e.stopPropagation()}>
|
|
123
|
+
<button className="lmodal__close" onClick={close} aria-label="Close">×</button>
|
|
124
|
+
{/* content */}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
```
|
|
129
|
+
|
|
117
130
|
**Mark the scrollable panel `data-lenis-prevent`.** Stopping the smooth scroller is not enough: while stopped it calls `preventDefault()` on any `touchmove` that reaches it, so a panel with `overflow-y: auto` scrolls with the wheel and is frozen under a finger. Whatever sits at the bottom of the panel, usually the CTA, becomes unreachable on a phone. See `frontend/scroll-driven/smooth-scroll.md`, Section 3.
|
|
118
131
|
|
|
119
132
|
```jsx
|
|
@@ -199,6 +212,8 @@ Format for the locale at write time (the decimal comma above is pt-BR). Never an
|
|
|
199
212
|
|
|
200
213
|
## Section 7: Persistent Elements
|
|
201
214
|
|
|
215
|
+
These live at the app root, outside every section, and repeat on almost every marketing page.
|
|
216
|
+
|
|
202
217
|
**Slim translucent nav** that gains a background once scrolled:
|
|
203
218
|
|
|
204
219
|
```jsx
|
|
@@ -208,6 +223,19 @@ useEffect(() => {
|
|
|
208
223
|
window.addEventListener('scroll', onScroll, { passive: true })
|
|
209
224
|
return () => window.removeEventListener('scroll', onScroll)
|
|
210
225
|
}, [])
|
|
226
|
+
return (
|
|
227
|
+
<nav className={`nav${solid ? ' nav--solid' : ''}`}>
|
|
228
|
+
<a className="nav__logo" href="#top"><span>{BRAND}</span></a>
|
|
229
|
+
<div className="nav__links">{/* anchors */}</div>
|
|
230
|
+
<a className="btn btn-accent" href="#contact">{CTA}</a>
|
|
231
|
+
</nav>
|
|
232
|
+
)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```css
|
|
236
|
+
.nav { position: fixed; top: 0; inset-inline: 0; z-index: 100; display: flex; align-items: center;
|
|
237
|
+
justify-content: space-between; padding: 16px clamp(16px,5vw,48px); transition: background .3s, backdrop-filter .3s; }
|
|
238
|
+
.nav--solid { background: rgba(14,11,12,.7); backdrop-filter: blur(10px); border-bottom: 1px solid var(--hair); }
|
|
211
239
|
```
|
|
212
240
|
|
|
213
241
|
**Scroll progress bar** that prefers the smooth scroller when present:
|
|
@@ -225,20 +253,64 @@ useEffect(() => {
|
|
|
225
253
|
}, [])
|
|
226
254
|
```
|
|
227
255
|
|
|
256
|
+
```jsx
|
|
257
|
+
return <div className="scroll-progress" ref={ref} />
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
.scroll-progress { position: fixed; top: 0; left: 0; z-index: 101; height: 3px; width: 100%;
|
|
262
|
+
transform: scaleX(0); transform-origin: 0 50%; background: linear-gradient(90deg, var(--accent), var(--accent-soft)); }
|
|
263
|
+
```
|
|
264
|
+
|
|
228
265
|
Transform-based progress (`scaleX` with `transform-origin: 0 50%`) stays on the compositor. Animating `width` does not.
|
|
229
266
|
|
|
267
|
+
**Floating chat button.** A fixed exit channel to the business's messaging app, always within reach. It is also the fallback when the lead form fails. For WhatsApp, a `wa.me` deep link with a prefilled message:
|
|
268
|
+
|
|
269
|
+
```jsx
|
|
270
|
+
<a className="chat-float" href={`https://wa.me/${PHONE}?text=${encodeURIComponent(MESSAGE)}`}
|
|
271
|
+
target="_blank" rel="noopener noreferrer" aria-label="Chat on WhatsApp">
|
|
272
|
+
<svg viewBox="0 0 24 24" width="26" height="26" aria-hidden="true">{/* icon path */}</svg>
|
|
273
|
+
</a>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```css
|
|
277
|
+
.chat-float { position: fixed; right: 20px; bottom: 20px; z-index: 90; width: 56px; height: 56px;
|
|
278
|
+
display: grid; place-items: center; border-radius: 50%; background: #25d366; color: #fff;
|
|
279
|
+
box-shadow: 0 8px 24px rgba(0,0,0,.35); transition: transform .25s; }
|
|
280
|
+
.chat-float:hover { transform: translateY(-3px) scale(1.05); }
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Keep its z-index below the nav and overlays, and check that it does not cover a CTA on mobile.
|
|
284
|
+
|
|
230
285
|
---
|
|
231
286
|
|
|
232
287
|
## Section 8: Accordion
|
|
233
288
|
|
|
234
|
-
Use native `<details>`. It provides toggle, keyboard support and accessibility for free.
|
|
289
|
+
Use native `<details>`. It provides toggle, keyboard support and accessibility for free. On a landing page the FAQ sits right before the form, to clear objections.
|
|
290
|
+
|
|
291
|
+
```jsx
|
|
292
|
+
const FAQ = [{ q: '...', a: '...' }]
|
|
293
|
+
return (
|
|
294
|
+
<section className="section faq" id="faq">
|
|
295
|
+
<h2>{TITLE}</h2>
|
|
296
|
+
{FAQ.map((f) => (
|
|
297
|
+
<details className="faq__item" key={f.q}>
|
|
298
|
+
<summary>{f.q}<span className="faq__icon" aria-hidden="true">+</span></summary>
|
|
299
|
+
<p>{f.a}</p>
|
|
300
|
+
</details>
|
|
301
|
+
))}
|
|
302
|
+
</section>
|
|
303
|
+
)
|
|
304
|
+
```
|
|
235
305
|
|
|
236
306
|
```css
|
|
307
|
+
.faq__item { border-bottom: 1px solid var(--hair); padding: 18px 0; }
|
|
237
308
|
.faq__item summary { cursor: pointer; list-style: none; display: flex;
|
|
238
|
-
justify-content: space-between; align-items: center; }
|
|
309
|
+
justify-content: space-between; align-items: center; font-family: var(--serif); font-size: 1.15rem; }
|
|
239
310
|
.faq__item summary::-webkit-details-marker { display: none; }
|
|
240
|
-
.faq__item .faq__icon { transition: transform .3s; }
|
|
311
|
+
.faq__item .faq__icon { transition: transform .3s; color: var(--accent); }
|
|
241
312
|
.faq__item[open] .faq__icon { transform: rotate(45deg); }
|
|
313
|
+
.faq__item p { margin: 14px 0 0; color: var(--fg-dim); max-width: 70ch; }
|
|
242
314
|
```
|
|
243
315
|
|
|
244
316
|
Native `<details>` cannot animate its own height. If height animation is required, drive the inner content with GSAP rather than replacing the element with a custom widget.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<templateSet group="MORPH Templates">
|
|
2
|
+
<template name="morph-pascal" value="{{pascalCase $FEATURE_NAME$}}" description="Convert to PascalCase (UserAuth)" toReformat="false" toShortenFQNames="true">
|
|
3
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
4
|
+
<context>
|
|
5
|
+
<option name="OTHER" value="true" />
|
|
6
|
+
</context>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<template name="morph-camel" value="{{camelCase $FEATURE_NAME$}}" description="Convert to camelCase (userAuth)" toReformat="false" toShortenFQNames="true">
|
|
10
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
11
|
+
<context>
|
|
12
|
+
<option name="OTHER" value="true" />
|
|
13
|
+
</context>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<template name="morph-snake" value="{{snakeCase $FEATURE_NAME$}}" description="Convert to snake_case (user_auth)" toReformat="false" toShortenFQNames="true">
|
|
17
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
18
|
+
<context>
|
|
19
|
+
<option name="OTHER" value="true" />
|
|
20
|
+
</context>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<template name="morph-title" value="{{titleCase $FEATURE_NAME$}}" description="Convert to Title Case (User Auth)" toReformat="false" toShortenFQNames="true">
|
|
24
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
25
|
+
<context>
|
|
26
|
+
<option name="OTHER" value="true" />
|
|
27
|
+
</context>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<template name="morph-kebab" value="{{kebabCase $FEATURE_NAME$}}" description="Convert to kebab-case (user-auth)" toReformat="false" toShortenFQNames="true">
|
|
31
|
+
<variable name="FEATURE_NAME" expression="" defaultValue=""FEATURE_NAME"" alwaysStopAt="true" />
|
|
32
|
+
<context>
|
|
33
|
+
<option name="OTHER" value="true" />
|
|
34
|
+
</context>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<template name="morph-if" value="{{#if $CONDITION$}} $END$ {{/if}}" description="Handlebars if block" toReformat="false" toShortenFQNames="true">
|
|
38
|
+
<variable name="CONDITION" expression="" defaultValue=""condition"" alwaysStopAt="true" />
|
|
39
|
+
<context>
|
|
40
|
+
<option name="OTHER" value="true" />
|
|
41
|
+
</context>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<template name="morph-if-else" value="{{#if $CONDITION$}} $TRUE_BRANCH$ {{else}} $FALSE_BRANCH$ {{/if}}" description="Handlebars if-else block" toReformat="false" toShortenFQNames="true">
|
|
45
|
+
<variable name="CONDITION" expression="" defaultValue=""condition"" alwaysStopAt="true" />
|
|
46
|
+
<variable name="TRUE_BRANCH" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
47
|
+
<variable name="FALSE_BRANCH" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
48
|
+
<context>
|
|
49
|
+
<option name="OTHER" value="true" />
|
|
50
|
+
</context>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<template name="morph-each" value="{{#each $ITEMS$}} {{this.$PROPERTY$}} {{/each}}" description="Handlebars each loop" toReformat="false" toShortenFQNames="true">
|
|
54
|
+
<variable name="ITEMS" expression="" defaultValue=""items"" alwaysStopAt="true" />
|
|
55
|
+
<variable name="PROPERTY" expression="" defaultValue=""name"" alwaysStopAt="true" />
|
|
56
|
+
<context>
|
|
57
|
+
<option name="OTHER" value="true" />
|
|
58
|
+
</context>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<template name="morph-service" value="namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services; public class {{pascalCase FEATURE_NAME}}Service( I{{pascalCase FEATURE_NAME}}Repository repository, ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service { public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default) { $END$ } }" description="MORPH .NET Service class template" toReformat="false" toShortenFQNames="true">
|
|
62
|
+
<context>
|
|
63
|
+
<option name="C_SHARP" value="true" />
|
|
64
|
+
</context>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<template name="morph-repository" value="public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository { private readonly DbSet<{{pascalCase FEATURE_NAME}}> _dbSet = context.Set<{{pascalCase FEATURE_NAME}}>(); public async Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(int id, CancellationToken cancellationToken = default) { $END$ } }" description="MORPH .NET Repository class template" toReformat="false" toShortenFQNames="true">
|
|
68
|
+
<context>
|
|
69
|
+
<option name="C_SHARP" value="true" />
|
|
70
|
+
</context>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<template name="morph-component" value="@page "/{{kebabCase FEATURE_NAME}}" @inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service @inject ILogger<{{pascalCase FEATURE_NAME}}Page> Logger <PageTitle>{{pascalCase FEATURE_NAME}}</PageTitle> <div class="container-fluid"> $END$ </div> @code { }" description="MORPH Blazor component template" toReformat="false" toShortenFQNames="true">
|
|
74
|
+
<context>
|
|
75
|
+
<option name="HTML" value="true" />
|
|
76
|
+
</context>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<template name="morph-migration" value="public partial class Add{{pascalCase FEATURE_NAME}} : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "{{pascalCase FEATURE_NAME}}s", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), $END$ }, constraints: table => { table.PrimaryKey("PK_{{pascalCase FEATURE_NAME}}s", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable(name: "{{pascalCase FEATURE_NAME}}s"); } }" description="MORPH EF Core migration template" toReformat="false" toShortenFQNames="true">
|
|
80
|
+
<context>
|
|
81
|
+
<option name="C_SHARP" value="true" />
|
|
82
|
+
</context>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<template name="morph-ts-contract" value="export interface {{pascalCase FEATURE_NAME}} { id: number; $FIELD1$ } export interface Create{{pascalCase FEATURE_NAME}}Request { $FIELD2$ } export interface Update{{pascalCase FEATURE_NAME}}Request { $END$ }" description="MORPH TypeScript contract template" toReformat="false" toShortenFQNames="true">
|
|
86
|
+
<variable name="FIELD1" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
87
|
+
<variable name="FIELD2" expression="" defaultValue="""" alwaysStopAt="true" />
|
|
88
|
+
<context>
|
|
89
|
+
<option name="TypeScript" value="true" />
|
|
90
|
+
</context>
|
|
91
|
+
</template>
|
|
92
|
+
</templateSet>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"MORPH Template - PascalCase": {
|
|
3
|
+
"prefix": "morph-pascal",
|
|
4
|
+
"body": ["{{pascalCase ${1:FEATURE_NAME}}}"],
|
|
5
|
+
"description": "Convert to PascalCase (UserAuth)"
|
|
6
|
+
},
|
|
7
|
+
"MORPH Template - camelCase": {
|
|
8
|
+
"prefix": "morph-camel",
|
|
9
|
+
"body": ["{{camelCase ${1:FEATURE_NAME}}}"],
|
|
10
|
+
"description": "Convert to camelCase (userAuth)"
|
|
11
|
+
},
|
|
12
|
+
"MORPH Template - snake_case": {
|
|
13
|
+
"prefix": "morph-snake",
|
|
14
|
+
"body": ["{{snakeCase ${1:FEATURE_NAME}}}"],
|
|
15
|
+
"description": "Convert to snake_case (user_auth)"
|
|
16
|
+
},
|
|
17
|
+
"MORPH Template - Title Case": {
|
|
18
|
+
"prefix": "morph-title",
|
|
19
|
+
"body": ["{{titleCase ${1:FEATURE_NAME}}}"],
|
|
20
|
+
"description": "Convert to Title Case (User Auth)"
|
|
21
|
+
},
|
|
22
|
+
"MORPH Template - kebab-case": {
|
|
23
|
+
"prefix": "morph-kebab",
|
|
24
|
+
"body": ["{{kebabCase ${1:FEATURE_NAME}}}"],
|
|
25
|
+
"description": "Convert to kebab-case (user-auth)"
|
|
26
|
+
},
|
|
27
|
+
"MORPH Template - UPPER_SNAKE_CASE": {
|
|
28
|
+
"prefix": "morph-upper-snake",
|
|
29
|
+
"body": ["{{upperSnakeCase ${1:FEATURE_NAME}}}"],
|
|
30
|
+
"description": "Convert to UPPER_SNAKE_CASE (USER_AUTH)"
|
|
31
|
+
},
|
|
32
|
+
"MORPH Template - Conditional If": {
|
|
33
|
+
"prefix": "morph-if",
|
|
34
|
+
"body": [
|
|
35
|
+
"{{#if ${1:condition}}}",
|
|
36
|
+
" $2",
|
|
37
|
+
"{{/if}}"
|
|
38
|
+
],
|
|
39
|
+
"description": "Handlebars if block"
|
|
40
|
+
},
|
|
41
|
+
"MORPH Template - Conditional If-Else": {
|
|
42
|
+
"prefix": "morph-if-else",
|
|
43
|
+
"body": [
|
|
44
|
+
"{{#if ${1:condition}}}",
|
|
45
|
+
" $2",
|
|
46
|
+
"{{else}}",
|
|
47
|
+
" $3",
|
|
48
|
+
"{{/if}}"
|
|
49
|
+
],
|
|
50
|
+
"description": "Handlebars if-else block"
|
|
51
|
+
},
|
|
52
|
+
"MORPH Template - Each Loop": {
|
|
53
|
+
"prefix": "morph-each",
|
|
54
|
+
"body": [
|
|
55
|
+
"{{#each ${1:items}}}",
|
|
56
|
+
" {{this.$2}}",
|
|
57
|
+
"{{/each}}"
|
|
58
|
+
],
|
|
59
|
+
"description": "Handlebars each loop"
|
|
60
|
+
},
|
|
61
|
+
"MORPH Template - Unless": {
|
|
62
|
+
"prefix": "morph-unless",
|
|
63
|
+
"body": [
|
|
64
|
+
"{{#unless ${1:condition}}}",
|
|
65
|
+
" $2",
|
|
66
|
+
"{{/unless}}"
|
|
67
|
+
],
|
|
68
|
+
"description": "Handlebars unless block"
|
|
69
|
+
},
|
|
70
|
+
"MORPH Template - .NET Service Class": {
|
|
71
|
+
"prefix": "morph-service",
|
|
72
|
+
"body": [
|
|
73
|
+
"namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;",
|
|
74
|
+
"",
|
|
75
|
+
"public class {{pascalCase FEATURE_NAME}}Service(",
|
|
76
|
+
" I{{pascalCase FEATURE_NAME}}Repository repository,",
|
|
77
|
+
" ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service",
|
|
78
|
+
"{",
|
|
79
|
+
" public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
|
|
80
|
+
" {",
|
|
81
|
+
" $1",
|
|
82
|
+
" }",
|
|
83
|
+
"}"
|
|
84
|
+
],
|
|
85
|
+
"description": "MORPH .NET Service class template"
|
|
86
|
+
},
|
|
87
|
+
"MORPH Template - .NET Repository Class": {
|
|
88
|
+
"prefix": "morph-repository",
|
|
89
|
+
"body": [
|
|
90
|
+
"public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository",
|
|
91
|
+
"{",
|
|
92
|
+
" private readonly DbSet<{{pascalCase FEATURE_NAME}}> _dbSet = context.Set<{{pascalCase FEATURE_NAME}}>();",
|
|
93
|
+
"",
|
|
94
|
+
" public async Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
|
|
95
|
+
" {",
|
|
96
|
+
" $1",
|
|
97
|
+
" }",
|
|
98
|
+
"}"
|
|
99
|
+
],
|
|
100
|
+
"description": "MORPH .NET Repository class template"
|
|
101
|
+
},
|
|
102
|
+
"MORPH Template - Blazor Component": {
|
|
103
|
+
"prefix": "morph-component",
|
|
104
|
+
"body": [
|
|
105
|
+
"@page \"/{{kebabCase FEATURE_NAME}}\"",
|
|
106
|
+
"@inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service",
|
|
107
|
+
"@inject ILogger<{{pascalCase FEATURE_NAME}}Page> Logger",
|
|
108
|
+
"",
|
|
109
|
+
"<PageTitle>{{pascalCase FEATURE_NAME}}</PageTitle>",
|
|
110
|
+
"",
|
|
111
|
+
"<div class=\"container-fluid\">",
|
|
112
|
+
" $1",
|
|
113
|
+
"</div>",
|
|
114
|
+
"",
|
|
115
|
+
"@code {",
|
|
116
|
+
" $2",
|
|
117
|
+
"}"
|
|
118
|
+
],
|
|
119
|
+
"description": "MORPH Blazor component template"
|
|
120
|
+
},
|
|
121
|
+
"MORPH Template - EF Core Migration": {
|
|
122
|
+
"prefix": "morph-migration",
|
|
123
|
+
"body": [
|
|
124
|
+
"public partial class Add{{pascalCase FEATURE_NAME}} : Migration",
|
|
125
|
+
"{",
|
|
126
|
+
" protected override void Up(MigrationBuilder migrationBuilder)",
|
|
127
|
+
" {",
|
|
128
|
+
" migrationBuilder.CreateTable(",
|
|
129
|
+
" name: \"{{pascalCase FEATURE_NAME}}s\",",
|
|
130
|
+
" columns: table => new",
|
|
131
|
+
" {",
|
|
132
|
+
" Id = table.Column<int>(type: \"int\", nullable: false)",
|
|
133
|
+
" .Annotation(\"SqlServer:Identity\", \"1, 1\"),",
|
|
134
|
+
" $1",
|
|
135
|
+
" },",
|
|
136
|
+
" constraints: table =>",
|
|
137
|
+
" {",
|
|
138
|
+
" table.PrimaryKey(\"PK_{{pascalCase FEATURE_NAME}}s\", x => x.Id);",
|
|
139
|
+
" });",
|
|
140
|
+
" }",
|
|
141
|
+
"",
|
|
142
|
+
" protected override void Down(MigrationBuilder migrationBuilder)",
|
|
143
|
+
" {",
|
|
144
|
+
" migrationBuilder.DropTable(name: \"{{pascalCase FEATURE_NAME}}s\");",
|
|
145
|
+
" }",
|
|
146
|
+
"}"
|
|
147
|
+
],
|
|
148
|
+
"description": "MORPH EF Core migration template"
|
|
149
|
+
},
|
|
150
|
+
"MORPH Template - Hangfire Job": {
|
|
151
|
+
"prefix": "morph-job",
|
|
152
|
+
"body": [
|
|
153
|
+
"public class {{pascalCase FEATURE_NAME}}ProcessorJob(",
|
|
154
|
+
" I{{pascalCase FEATURE_NAME}}Service service,",
|
|
155
|
+
" ILogger<{{pascalCase FEATURE_NAME}}ProcessorJob> logger) : I{{pascalCase FEATURE_NAME}}ProcessorJob",
|
|
156
|
+
"{",
|
|
157
|
+
" [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]",
|
|
158
|
+
" [Queue(\"default\")]",
|
|
159
|
+
" [JobDisplayName(\"{{pascalCase FEATURE_NAME}} Processing - ID: {0}\")]",
|
|
160
|
+
" public async Task ExecuteAsync(int id, CancellationToken cancellationToken)",
|
|
161
|
+
" {",
|
|
162
|
+
" $1",
|
|
163
|
+
" }",
|
|
164
|
+
"}"
|
|
165
|
+
],
|
|
166
|
+
"description": "MORPH Hangfire job template"
|
|
167
|
+
},
|
|
168
|
+
"MORPH Template - TypeScript Contract": {
|
|
169
|
+
"prefix": "morph-ts-contract",
|
|
170
|
+
"body": [
|
|
171
|
+
"export interface {{pascalCase FEATURE_NAME}} {",
|
|
172
|
+
" id: number;",
|
|
173
|
+
" $1",
|
|
174
|
+
"}",
|
|
175
|
+
"",
|
|
176
|
+
"export interface Create{{pascalCase FEATURE_NAME}}Request {",
|
|
177
|
+
" $2",
|
|
178
|
+
"}",
|
|
179
|
+
"",
|
|
180
|
+
"export interface Update{{pascalCase FEATURE_NAME}}Request {",
|
|
181
|
+
" $3",
|
|
182
|
+
"}"
|
|
183
|
+
],
|
|
184
|
+
"description": "MORPH TypeScript contract template"
|
|
185
|
+
}
|
|
186
|
+
}
|