diff --git a/Docs/API/API.md b/Docs/API/API.md deleted file mode 100644 index f72b572..0000000 --- a/Docs/API/API.md +++ /dev/null @@ -1,158 +0,0 @@ -# Governance API - -This page shows the practical entry points used by the governance examples. - -## What this API is for - -Use the governance API when execution is not immediate and you need one of these steps first: - -- request creation -- approval workflow -- stale-version resolution -- governed execution -- request and decision history queries - -The core package owns direct mutation execution. Governance wraps that execution with a request -model and stateful decision history. - -## Request creation - -Use `MutationRequestFactory` when you want to create a request from explicit strings and values: - -```csharp -var request = MutationRequestFactory.PendingApproval( - stateId: "tenant-42:roles", - stateType: "IamRoleState", - mutationType: "GrantRoleMutation", - intent: new MutationIntent - { - OperationName = "GrantRole", - Category = "Security", - Description = "Grant elevated role to tenant operator" - }, - context: MutationContext.User("requester-1", "Requester One", "Incident escalation"), - expectedStateVersion: "v10", - approvalRequirements: - [ - MutationApprovalRequirement.SingleActorStep("security-lead"), - MutationApprovalRequirement.SingleActorStep("platform-owner") - ]); -``` - -If you already have concrete CLR types, prefer the generic overloads such as: - -- `MutationRequestFactory.Approved()` -- `MutationRequestFactory.PendingApproval()` - -They remove repeated `stateType` and `mutationType` strings at the call site. - -### Factory choices - -- `Pending(...)` for a request that should enter the lifecycle without approval requirements -- `PendingApproval(...)` for a request that must collect one or more approval steps -- `Approved(...)` for a request that is terminally approved at creation time - -Use the generic overloads when the CLR types already exist. Use the string-based overloads when the -request metadata comes from an external system or serialized payload. - -## Decision history - -Use `MutationRequestDecision` when you need to record a lifecycle, approval, or version-resolution decision and the category is already known: - -```csharp -var decision = MutationRequestDecision.Lifecycle( - MutationRequestLifecycleDecisionType.Approved, - MutationContext.System("governance-runtime", "Approved by governance")); -``` - -The category-specific helpers are: - -- `MutationRequestDecision.Lifecycle(...)` -- `MutationRequestDecision.Approval(...)` -- `MutationRequestDecision.VersionResolution(...)` - -Use the low-level `MutationRequestDecision.Create(...)` only when the decision type is already dynamic and you do not know the category up front. - -### Decision categories - -- lifecycle decisions describe request state transitions such as submitted, pending, approved, rejected, executed, canceled, or expired -- approval decisions describe workflow-level approval actions such as requested, approved, rejected, or quorum satisfied -- version-resolution decisions describe how the runtime handled stale or matching versions before execution - -The helper methods exist so call sites stay readable when the category is already known. - -## Governed execution - -Use `IGovernanceExecutionManager.ExecuteApproved(...)` when the runtime should resolve an approved request and execute the mutation: - -```csharp -var execution = await executionManager.ExecuteApproved( - request.RequestId, - mutation, - currentState, - governanceContext: MutationContext.Service("governance-runtime", "Execute approved governance request"), - strategy: VersionedRequestResolutionStrategy.RejectStale); -``` - -If your state implements `IVersionedState`, use the overload that accepts `currentState` directly and reads `state.Version` for both version selectors. - -### Typical execution flow - -1. create or load a request -2. approve the request if needed -3. resolve the request against the current state version -4. execute the underlying mutation through the core engine -5. record the terminal request decision - -That flow is what the `GovernedExecution` example demonstrates end to end. - -## Common operations - -### Create a pending approval request - -```csharp -var request = MutationRequestFactory.PendingApproval( - stateId: "tenant-42:roles", - stateType: "IamRoleState", - mutationType: "GrantRoleMutation", - intent: new MutationIntent - { - OperationName = "GrantRole", - Category = "Security", - Description = "Grant elevated role to tenant operator" - }, - context: MutationContext.User("requester-1", "Requester One", "Incident escalation"), - expectedStateVersion: "v10", - approvalRequirements: - [ - MutationApprovalRequirement.SingleActorStep("security-lead"), - MutationApprovalRequirement.SingleActorStep("platform-owner") - ]); -``` - -### Record an approval decision - -```csharp -var decision = MutationRequestDecision.Approval( - MutationRequestApprovalDecisionType.Approved, - MutationContext.User("approver-1", "Approver One", "Approved after review")); -``` - -### Execute an already approved request - -```csharp -var result = await executionManager.ExecuteApproved( - request.RequestId, - mutation, - currentState, - governanceContext: MutationContext.Service("governance-runtime", "Execute approved governance request"), - strategy: VersionedRequestResolutionStrategy.RejectStale); -``` - -## Where to look next - -- `README.md` for the package overview -- `Examples/Governance/*/README.md` for runnable scenarios -- `src/Governance/Abstractions/Requests/Factory/MutationRequestFactory.cs` for request creation -- `src/Governance/Abstractions/Requests/Decisions/MutationRequestDecision.cs` for decision helpers -- `src/Governance/Runtime/Execution/Orchestration/GovernanceExecutionManager.cs` for governed execution diff --git a/Docs/API/Redis.md b/Docs/API/Redis.md deleted file mode 100644 index 5716b2b..0000000 --- a/Docs/API/Redis.md +++ /dev/null @@ -1,136 +0,0 @@ -# Redis Governance Provider API - -This page covers the Redis-backed provider package `ModularityKit.Mutator.Governance.Redis`. - -It is the storage and query backend for governed requests. The package implements the public -request store and query store contracts while keeping Redis-specific persistence details internal. - -## What this API is for - -Use the Redis provider when you want the governance model from `ModularityKit.Mutator.Governance` -but need a persistent backing store instead of in-memory state. - -Typical reasons: - -- you want request persistence across process restarts -- you want Redis-backed query projections for approval queues and decision history -- you want the same governance API surface with a different storage backend -- you want to share governed request state across multiple runtime instances - -## Primary APIs - -### Dependency injection - -- `RedisGovernanceServiceCollectionExtensions.AddRedisGovernanceStore(...)` - -Registers the Redis-backed governance store and the query store against an existing -`IConnectionMultiplexer`. - -```csharp -using Microsoft.Extensions.DependencyInjection; -using ModularityKit.Mutator.Governance.Redis; -using StackExchange.Redis; - -var services = new ServiceCollection(); -var multiplexer = await ConnectionMultiplexer.ConnectAsync("localhost:6379"); - -services.AddRedisGovernanceStore( - multiplexer, - options => options.KeyPrefix = "modularitykit:governance"); -``` - -The extension method registers the provider-facing storage and query services against DI. - -### Configuration details - -`RedisMutationRequestStoreOptions.KeyPrefix` scopes all Redis keys created by the provider. - -Use a dedicated prefix per environment or application if you need isolation between deployments. -The examples use `modularitykit:governance` as a readable default. - -### Configuration - -- `RedisMutationRequestStoreOptions` - -Current option: - -- `KeyPrefix` - Redis key prefix used for all request data, indexes, and query projections - -### Storage facade - -- `RedisMutationRequestStore` - -Implements: - -- `IMutationRequestStore` -- `IMutationRequestQueryStore` - -Use it through DI rather than constructing it directly unless you are testing internals. - -### What gets stored - -The provider stores the data needed for: - -- request documents -- decision and approval projections -- query indexes and queue membership -- optimistic concurrency state for request writes - -The provider does not change the governance model. It only persists the same model through Redis. - -### Keyspace and key helpers - -- `RedisMutationRequestKeyspace` -- `RedisMutationRequestDocumentKeyFactory` - -These types define the Redis naming and partitioning rules for request documents, indexes, and -query views. - -## What the provider does - -The Redis provider handles: - -- request persistence -- query projection -- optimistic concurrency on request writes -- Redis index maintenance -- candidate selection for query execution - -It does not change the governance model itself. It only replaces the in-memory storage backend with -Redis-specific persistence and read paths. - -## Common usage - -### Register the provider - -```csharp -services.AddRedisGovernanceStore( - multiplexer, - options => options.KeyPrefix = "modularitykit:governance"); -``` - -### Resolve the stores - -```csharp -var requestStore = provider.GetRequiredService(); -var queryStore = provider.GetRequiredService(); -``` - -### Use the normal governance APIs - -Once registered, the rest of the application uses the same request and query contracts as the -in-memory provider. - -## Usage pattern - -1. Connect to Redis with `StackExchange.Redis` -2. Register the provider with `AddRedisGovernanceStore(...)` -3. Resolve `IMutationRequestStore` and `IMutationRequestQueryStore` from DI -4. Use the normal governance request and query APIs - -## Related docs - -- [`src/Redis/README.md`](../../src/Redis/README.md) -- [`Examples/Governance/RedisQueries/README.md`](../../Examples/Governance/RedisQueries/README.md) -- [`Reference.md`](Reference.md) -- [Governance API](API.md) diff --git a/Docs/API/Reference.md b/Docs/API/Reference.md index 177e261..e79182b 100644 --- a/Docs/API/Reference.md +++ b/Docs/API/Reference.md @@ -1,6 +1,7 @@ # API Reference -DocFX generates the public API reference from the compiled assemblies and XML documentation files. +DocFX generates the public API reference from the project code, compiled assemblies, and XML +documentation files. ## Included packages @@ -8,11 +9,11 @@ DocFX generates the public API reference from the compiled assemblies and XML do - `ModularityKit.Mutator.Governance` - `ModularityKit.Mutator.Governance.Redis` -## Build locally +## Browse the generated API -```bash -dotnet tool update -g docfx -docfx docfx.json -``` +Use the package roots below to explore the public types for each package. These pages are generated +from the code, not hand-written docs: -The rendered API pages appear under the `API` section of the generated site. +- [Core package root](../../obj/api/core/ModularityKit.Mutator.Abstractions.html) +- [Governance package root](../../obj/api/governance/ModularityKit.Mutator.Governance.Abstractions.html) +- [Redis provider root](../../obj/api/redis/ModularityKit.Mutator.Governance.Redis.html) diff --git a/Docs/Decision/Adr/ADR_012_Mutation_Execution_Interfaces_and_Context _Separation.md b/Docs/Decision/Adr/ADR_012_Mutation_Execution_Interfaces_and_Context_Separation.md similarity index 100% rename from Docs/Decision/Adr/ADR_012_Mutation_Execution_Interfaces_and_Context _Separation.md rename to Docs/Decision/Adr/ADR_012_Mutation_Execution_Interfaces_and_Context_Separation.md diff --git a/Docs/Decision/Adr/ADR_013_Mutation_Engine_and_Executor_Runtime _Integration.md b/Docs/Decision/Adr/ADR_013_Mutation_Engine_and_Executor_Runtime_Integration.md similarity index 100% rename from Docs/Decision/Adr/ADR_013_Mutation_Engine_and_Executor_Runtime _Integration.md rename to Docs/Decision/Adr/ADR_013_Mutation_Engine_and_Executor_Runtime_Integration.md diff --git a/Docs/Home.md b/Docs/Home.md index ab26e6c..b519bc7 100644 --- a/Docs/Home.md +++ b/Docs/Home.md @@ -1,31 +1,31 @@ +# ModularityKit.Mutator + +ModularityKit.Mutator is a .NET mutation runtime with governance, request lifecycle control, +approval flow, and Redis-backed storage. + ![ModularityKit.Mutator](../assets/brand/mutator-landing-banner.png) -# ModularityKit.Mutator +## Start here -ModularityKit.Mutator is a .NET mutation runtime with governance, request lifecycle, approval flow, -and Redis-backed storage. +- [API reference](API/Reference.md) +- [Architecture](Architecture.md) +- [Core concepts](Core-Concepts.md) +- [Execution model](ExecutionModel.md) +- [ADR index](Decision/listadr.md) ## Packages | Package | What it covers | | --- | --- | -| [`ModularityKit.Mutator`](../src/README.md) | mutation runtime, policies, execution, audit, history | -| [`ModularityKit.Mutator.Governance`](../src/Governance/README.md) | request lifecycle, approvals, resolution, governed execution | +| [`ModularityKit.Mutator`](../src/README.md) | mutation runtime, policies, execution, audit, and history | +| [`ModularityKit.Mutator.Governance`](../src/Governance/README.md) | request lifecycle, approvals, resolution, and governed execution | | [`ModularityKit.Mutator.Governance.Redis`](../src/Redis/README.md) | Redis-backed storage and query provider | -## Explore - -| Area | What to read | -| --- | --- | -| API reference | [`Docs/API/Reference.md`](API/Reference.md) | -| Core concepts | [`Docs/Core-Concepts.md`](Core-Concepts.md) | -| Execution model | [`Docs/ExecutionModel.md`](ExecutionModel.md) | -| Roadmap | [`Docs/Roadmap.md`](Roadmap.md) | - -## What this site contains +## What is covered - package overviews for the runtime and governance extensions - conceptual docs for the mutation model and request flow +- decision records for architecture-level changes - generated API reference from XML docs ## Build locally diff --git a/docfx.json b/docfx.json index 392c87a..6b34799 100644 --- a/docfx.json +++ b/docfx.json @@ -4,19 +4,38 @@ "src": [ { "files": [ - "src/bin/Release/net10.0/ModularityKit.Mutator.dll", - "src/bin/Release/net10.0/ModularityKit.Mutator.Governance.dll", - "src/Redis/bin/Release/net10.0/ModularityKit.Mutator.Governance.Redis.dll" + "src/ModularityKit.Mutator.csproj" ] } ], - "dest": "obj/api" + "dest": "obj/api/core" + }, + { + "src": [ + { + "files": [ + "src/ModularityKit.Mutator.Governance.csproj" + ] + } + ], + "dest": "obj/api/governance" + }, + { + "src": [ + { + "files": [ + "src/Redis/ModularityKit.Mutator.Governance.Redis.csproj" + ] + } + ], + "dest": "obj/api/redis" } ], "build": { "content": [ { "files": [ + "index.md", "README.md", "toc.yml", "Docs/**/*.md", @@ -32,8 +51,12 @@ }, { "files": [ - "obj/api/**.yml", - "obj/api/**.md" + "obj/api/core/**/*.yml", + "obj/api/core/**/*.md", + "obj/api/governance/**/*.yml", + "obj/api/governance/**/*.md", + "obj/api/redis/**/*.yml", + "obj/api/redis/**/*.md" ] } ], @@ -53,7 +76,7 @@ "_appTitle": "ModularityKit.Mutator", "_appName": "ModularityKit.Mutator", "_appLogoPath": "assets/brand/logo.png", - "_appLogoUrl": "Docs/Home.html", + "_appLogoUrl": "index.html", "_appFooter": "ModularityKit.Mutator documentation", "_enableSearch": true }, diff --git a/index.md b/index.md new file mode 100644 index 0000000..f60865f --- /dev/null +++ b/index.md @@ -0,0 +1,76 @@ +
+
+

Mutation runtime and governance

+

ModularityKit.Mutator

+

Deterministic mutation execution, request governance, and generated API reference for the repository.

+ +
+
+ModularityKit.Mutator banner +
+

What is included

+
    +
  • Core mutation engine and runtime contracts
  • +
  • Governance extensions for request lifecycle control
  • +
  • Redis-backed storage and query support
  • +
  • Architecture decisions collected as ADRs
  • +
+
+
+
+ +
+
+
+

Packages

+

Source packages and their generated documentation.

+
+ +
+ +
+
+

Guides

+

Concise project docs for architecture and execution flow.

+
+ +
+ +
+
+

Decision

+

Architecture Decision Records and their index.

+
+ +
+ +
+
+

API

+

Generated reference for packages and provider surfaces.

+
+ +
+
diff --git a/src/Governance/Abstractions/NamespaceDoc.cs b/src/Governance/Abstractions/NamespaceDoc.cs new file mode 100644 index 0000000..0c3528b --- /dev/null +++ b/src/Governance/Abstractions/NamespaceDoc.cs @@ -0,0 +1,8 @@ +namespace ModularityKit.Mutator.Governance.Abstractions; + +/// +/// Documentation anchor for the governance abstractions package. +/// +public sealed class NamespaceDoc +{ +} diff --git a/src/Governance/README.md b/src/Governance/README.md index 5455a74..66ea494 100644 --- a/src/Governance/README.md +++ b/src/Governance/README.md @@ -97,7 +97,7 @@ decision wrapper when the category is already known. implementations, which removes the need to pass the current and resulting version selectors when both come from `state.Version`. -See [`Docs/API/API.md`](../../Docs/API/API.md) for the practical usage surface and example call patterns. +See the generated package root at [`obj/api/governance/ModularityKit.Mutator.Governance.Abstractions.html`](../../obj/api/governance/ModularityKit.Mutator.Governance.Abstractions.html) for the public usage surface and type tree. ## Package structure diff --git a/src/Redis/README.md b/src/Redis/README.md index 7357a7a..956043c 100644 --- a/src/Redis/README.md +++ b/src/Redis/README.md @@ -1,6 +1,6 @@ ![ModularityKit.Mutator.Governance.Redis overview](../../assets/governance/providers/mutator-governance-redis-overview.png) -See [`Docs/API/Redis.md`](../../Docs/API/Redis.md) for the practical API surface and usage examples. +See the generated provider root at [`obj/api/redis/ModularityKit.Mutator.Governance.Redis.html`](../../obj/api/redis/ModularityKit.Mutator.Governance.Redis.html) for the public API surface and type tree. ## Package structure diff --git a/templates/modularitykit/layout/_master.tmpl b/templates/modularitykit/layout/_master.tmpl index 3652e40..b5bb4c7 100644 --- a/templates/modularitykit/layout/_master.tmpl +++ b/templates/modularitykit/layout/_master.tmpl @@ -11,12 +11,6 @@ {{^redirect_url}} -
-
- {{_appTitle}} - Mutation runtime, governance, and generated API reference. -
-
{{^_disableNavbar}} diff --git a/templates/modularitykit/partials/footer.tmpl.partial b/templates/modularitykit/partials/footer.tmpl.partial index 0a99398..68da40b 100644 --- a/templates/modularitykit/partials/footer.tmpl.partial +++ b/templates/modularitykit/partials/footer.tmpl.partial @@ -12,3 +12,55 @@
+ diff --git a/templates/modularitykit/partials/head.tmpl.partial b/templates/modularitykit/partials/head.tmpl.partial index 9bef6c6..9931eb7 100644 --- a/templates/modularitykit/partials/head.tmpl.partial +++ b/templates/modularitykit/partials/head.tmpl.partial @@ -17,6 +17,7 @@ {{^redirect_url}} + {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} @@ -26,6 +27,7 @@ + {{#_noindex}}{{/_noindex}} diff --git a/templates/modularitykit/partials/navbar.tmpl.partial b/templates/modularitykit/partials/navbar.tmpl.partial index 713f0f6..bc4c376 100644 --- a/templates/modularitykit/partials/navbar.tmpl.partial +++ b/templates/modularitykit/partials/navbar.tmpl.partial @@ -13,17 +13,22 @@ diff --git a/templates/modularitykit/styles/main.css b/templates/modularitykit/styles/main.css index f9d4820..9b5c29d 100644 --- a/templates/modularitykit/styles/main.css +++ b/templates/modularitykit/styles/main.css @@ -1,17 +1,20 @@ :root { - --site-bg: #f6f7f9; - --site-surface: #ffffff; - --site-border: #d9dee7; - --site-text: #122033; - --site-muted: #5b6778; - --site-accent: #143a5a; - --site-accent-strong: #0d2940; - --site-accent-soft: #e8eef5; + --site-bg: #0a1020; + --site-surface: #111a2f; + --site-surface-2: #15213b; + --site-border: #253555; + --site-border-strong: #35507a; + --site-text: #edf4ff; + --site-muted: #a1afc4; + --site-accent: #2d73ff; + --site-accent-strong: #42e0d8; + --site-accent-soft: rgba(45, 115, 255, 0.12); + --site-code-bg: #0c1323; } html, body { - background: var(--site-bg); + background: linear-gradient(180deg, #08101f 0%, #0a1020 45%, #0b1224 100%); color: var(--site-text); } @@ -30,7 +33,7 @@ a:focus { top: 0; z-index: 1000; padding: 0.75rem 1rem; - background: var(--site-accent-strong); + background: var(--site-accent); color: #fff; } @@ -39,34 +42,12 @@ a:focus { top: 1rem; } -.site-topbar { - background: linear-gradient(90deg, var(--site-accent-strong), var(--site-accent)); - color: #fff; - border-bottom: 1px solid rgba(255, 255, 255, 0.12); -} - -.site-topbar__inner { - display: flex; - align-items: center; - gap: 1rem; - min-height: 2.75rem; -} - -.site-topbar__brand { - font-weight: 700; - letter-spacing: 0; -} - -.site-topbar__text { - color: rgba(255, 255, 255, 0.82); -} - .site-frame { background: var(--site-bg); } .site-navbar { - background: var(--site-surface); + background: rgba(10, 16, 32, 0.88); border: 0; border-bottom: 1px solid var(--site-border); border-radius: 0; @@ -90,18 +71,180 @@ a:focus { .site-nav > li > a { color: var(--site-muted); font-weight: 600; + border-radius: 6px; + transition: background-color 0.15s ease, color 0.15s ease; } .site-nav > li > a:hover, .site-nav > li > a:focus { - color: var(--site-accent-strong); - background: var(--site-accent-soft); + color: var(--site-text); + background: rgba(45, 115, 255, 0.14); +} + +.site-nav > li.active > a, +.site-nav > li.active > a:hover, +.site-nav > li.active > a:focus, +.site-nav > li > a[aria-current="page"] { + color: #ffffff; + background: rgba(45, 115, 255, 0.18); + border-bottom: 2px solid var(--site-accent-strong); + margin-bottom: -2px; } .site-search { margin-right: 0; } +.site-navbar__actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 0.9rem; +} + +.site-github-link { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.9rem; + height: 2.9rem; + color: #ffffff; + background: rgba(13, 23, 48, 0.96); + border: 1px solid rgba(67, 118, 255, 0.55); + border-radius: 10px; + box-shadow: 0 10px 24px rgba(3, 8, 20, 0.18), 0 0 0 1px rgba(25, 50, 97, 0.35) inset; + transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease, transform 0.15s ease; +} + +.site-github-link svg { + width: 1.4rem; + height: 1.4rem; + display: block; + fill: currentColor; +} + +.site-github-link:hover, +.site-github-link:focus { + color: #ffffff; + border-color: rgba(60, 178, 255, 0.72); + background: rgba(19, 33, 63, 0.98); + transform: translateY(-1px); +} + +#autocollapse .site-search .form-control, +#autocollapse .site-search .form-control:hover, +#autocollapse .site-search .form-control:focus, +#autocollapse .site-search .form-control:active { + background-color: var(--site-code-bg) !important; + background-image: none !important; + border: 1px solid var(--site-border) !important; + color: var(--site-text) !important; + box-shadow: none !important; +} + +#autocollapse .site-search .form-control::placeholder { + color: var(--site-muted); + opacity: 1; +} + +#autocollapse .site-search .form-control:focus { + border-color: var(--site-accent) !important; + box-shadow: 0 0 0 2px rgba(45, 115, 255, 0.2) !important; +} + +#autocollapse .site-search input:-webkit-autofill, +#autocollapse .site-search input:-webkit-autofill:hover, +#autocollapse .site-search input:-webkit-autofill:focus, +#autocollapse .site-search input:-webkit-autofill:active { + -webkit-text-fill-color: var(--site-text) !important; + -webkit-box-shadow: 0 0 0 1000px var(--site-code-bg) inset !important; + box-shadow: 0 0 0 1000px var(--site-code-bg) inset !important; + caret-color: var(--site-text); + background-color: var(--site-code-bg) !important; + background-image: none !important; +} + +#search-results { + background: #0a1020; + border-bottom: 1px solid var(--site-border); +} + +#search-results, +#search-results .search-list, +#search-results .sr-items, +#search-results .pagination { + color: var(--site-text); +} + +#search-results .search-list, +#search-results .sr-items, +#search-results .pagination > li > a, +#search-results .pagination > li > span { + color: var(--site-text); +} + +#search-results .search-list, +#search-results .sr-items { + background: transparent; +} + +#search-results .pagination > .active > a, +#search-results .pagination > .active > span, +#search-results .pagination > li > a:hover, +#search-results .pagination > li > span:hover { + background: var(--site-accent-soft); + border-color: var(--site-border); + color: var(--site-text); +} + +#search-results .search-list { + border-bottom: 1px solid var(--site-border); +} + +#search-results .sr-item { + border-bottom: 1px solid rgba(37, 53, 85, 0.72); +} + +#search-results .sr-item h5 a { + color: var(--site-text); +} + +#search-results .sr-item p, +#search-results .sr-item .sr-item-body { + color: var(--site-muted); +} + +.subnav.navbar.navbar-default { + background: rgba(10, 16, 32, 0.88); + border: 0; + border-bottom: 1px solid var(--site-border); + border-radius: 0; + min-height: 0; + margin: 0; + box-shadow: none; +} + +.subnav .container { + min-height: 0; + padding-top: 0.35rem; + padding-bottom: 0.35rem; +} + +.subnav .breadcrumb { + background: transparent; + margin: 0; + padding: 0; +} + +.subnav .breadcrumb > li, +.subnav .breadcrumb > li > a { + color: var(--site-muted); +} + +.subnav .breadcrumb > li + li:before { + color: var(--site-muted); +} + .site-brand { display: flex; align-items: center; @@ -133,19 +276,683 @@ a:focus { } .body-content { - padding-top: 1.25rem; + padding-top: 1rem; + padding-bottom: 1.5rem; } .content { - background: var(--site-surface); + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); border: 1px solid var(--site-border); - border-radius: 6px; + border-radius: 8px; + padding: 1.5rem; + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.28); +} + +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: var(--site-text); +} + +.content p, +.content li, +.content td, +.content th { + color: var(--site-text); +} + +.content blockquote { + border-left: 4px solid var(--site-accent); + color: var(--site-muted); + background: rgba(45, 115, 255, 0.06); +} + +.content code, +.content pre { + background: var(--site-code-bg); + border-color: var(--site-border); + color: #d8e7ff; +} + +.content table { + border-color: var(--site-border); +} + +.content table th, +.content table td { + border-color: var(--site-border); +} + +.content table th { + background: var(--site-surface-2); +} + +.content hr { + border-top-color: var(--site-border); +} + +.content img { + border-radius: 8px; +} + +.content > :first-child { + margin-top: 0; +} + +body.is-homepage .article.row.grid > .col-md-10 { + width: 100%; +} + +body.is-homepage .article.row.grid > .hidden-sm.col-md-2[role="complementary"] { + display: none; +} + +/* Managed reference / API pages */ +.article.row.grid-right { + align-items: flex-start; +} + +.article.row.grid-right > .col-md-10 { + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)) !important; + border: 1px solid var(--site-border); + border-radius: 8px; + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); padding: 1.5rem; - box-shadow: 0 1px 2px rgba(18, 32, 51, 0.04); + margin-top: 2.5rem; +} + +.article.row.grid-right > .col-md-10, +.article.row.grid-right > .col-md-10 > article.content, +.article.row.grid-right > .col-md-10 > article.content.wrap, +body .article.row.grid-right > .col-md-10 > article.content, +body .article.row.grid-right > .col-md-10 > article.content.wrap, +body article.content, +body article.content.wrap { + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)) !important; + color: var(--site-text) !important; +} + +.article.row.grid-right > .col-md-10 > article.content, +.article.row.grid-right > .col-md-10 > article.content.wrap { + background: transparent !important; + border: 0 !important; + box-shadow: none; + padding: 0; +} + +.article.row.grid-right > .col-md-10 > article.content .markdown, +.article.row.grid-right > .col-md-10 > article.content .markdown.level0.summary, +.article.row.grid-right > .col-md-10 > article.content .markdown.level0.remarks, +.article.row.grid-right > .col-md-10 > article.content .markdown.level0.conceptual, +.article.row.grid-right > .col-md-10 > article.content .inheritance, +.article.row.grid-right > .col-md-10 > article.content .inheritedMembers, +.article.row.grid-right > .col-md-10 > article.content section, +.article.row.grid-right > .col-md-10 > article.content .member-list, +.article.row.grid-right > .col-md-10 > article.content .children, +.article.row.grid-right > .col-md-10 > article.content .reference, +.article.row.grid-right > .col-md-10 > article.content .syntax, +.article.row.grid-right > .col-md-10 > article.content table { + background: transparent; + color: var(--site-text); +} + +.article.row.grid-right > .col-md-10 > article.content .markdown.level0.summary { + margin-top: 0.5rem; + margin-bottom: 0.9rem; +} + +.article.row.grid-right > .col-md-10 > article.content h1, +.article.row.grid-right > .col-md-10 > article.content h2, +.article.row.grid-right > .col-md-10 > article.content h3, +.article.row.grid-right > .col-md-10 > article.content h4, +.article.row.grid-right > .col-md-10 > article.content h5, +.article.row.grid-right > .col-md-10 > article.content h6, +.article.row.grid-right > .col-md-10 > article.content p, +.article.row.grid-right > .col-md-10 > article.content li, +.article.row.grid-right > .col-md-10 > article.content td, +.article.row.grid-right > .col-md-10 > article.content th { + color: var(--site-text); +} + +.article.row.grid-right > .col-md-10 > article.content h1 { + margin-top: 0; + margin-bottom: 0.75rem; + font-size: 2rem; +} + +.article.row.grid-right > .col-md-10 > article.content h2 { + margin-top: 1.75rem; + padding-bottom: 0.35rem; + border-bottom: 1px solid var(--site-border); +} + +.article.row.grid-right > .col-md-10 > article.content .inheritance h5, +.article.row.grid-right > .col-md-10 > article.content .inheritedMembers h5 { + border-bottom-color: var(--site-border); +} + +.article.row.grid-right > .col-md-10 > article.content .memberNameLink, +.article.row.grid-right > .col-md-10 > article.content .xref, +.article.row.grid-right > .col-md-10 > article.content .xref > a { + color: var(--site-accent-strong); +} + +.article.row.grid-right > .col-md-10 > article.content .xref:hover, +.article.row.grid-right > .col-md-10 > article.content .xref:focus, +.article.row.grid-right > .col-md-10 > article.content .xref > a:hover, +.article.row.grid-right > .col-md-10 > article.content .xref > a:focus, +.article.row.grid-right > .col-md-10 > article.content .memberNameLink:hover, +.article.row.grid-right > .col-md-10 > article.content .memberNameLink:focus { + color: #8ff5ef; +} + +.article.row.grid-right > .col-md-10 > article.content .table, +.article.row.grid-right > .col-md-10 > article.content table { + border-color: var(--site-border); +} + +.article.row.grid-right > .col-md-10 > article.content .table > thead > tr > th, +.article.row.grid-right > .col-md-10 > article.content table > thead > tr > th, +.article.row.grid-right > .col-md-10 > article.content .table > tbody > tr > td, +.article.row.grid-right > .col-md-10 > article.content table > tbody > tr > td { + border-color: var(--site-border); +} + +.article.row.grid-right > .col-md-10 > article.content pre { + background: var(--site-code-bg); + border: 1px solid var(--site-border); + color: #d8e7ff; +} + +.article.row.grid-right > .col-md-10 > article.content code { + background: rgba(10, 16, 32, 0.95); + color: #d8e7ff; +} + +.article.row.grid-right > .col-md-10 > article.content .alert { + border-color: var(--site-border); + background: rgba(45, 115, 255, 0.08); + color: var(--site-text); +} + +.article.row.grid-right > .col-md-10 > article.content .alert-info, +.article.row.grid-right > .col-md-10 > article.content .alert-warning, +.article.row.grid-right > .col-md-10 > article.content .alert-danger { + background: rgba(45, 115, 255, 0.08); +} + +body .docs-search, +body .toc-filter, +body .tryspan { + background: rgba(13, 23, 48, 0.96) !important; + color: var(--site-text) !important; +} + +body .tryspan { + border-color: var(--site-border) !important; +} + +body .toc-filter > input { + color: var(--site-text) !important; +} + +body .toc-filter > .filter-icon, +body .toc-filter > .clear-icon { + color: var(--site-muted) !important; +} + +body .sidenav, +body .fixed_header, +body .toc, +body .sidetoc, +body .sidefilter { + background: rgba(10, 16, 32, 0.94) !important; +} + +body .toc .nav > li > ul { + display: block !important; +} + +body .toc .nav > li > a.sidetoc-expand { + position: static !important; +} + +body .toc-toggle { + display: none !important; +} + +body .sidetoggle.collapse, +body .sidetoggle.collapsing, +body .sidetoggle.collapse.in { + display: block !important; + height: auto !important; + visibility: visible !important; +} + +body .sidenav.hide-when-search { + display: block !important; + float: left !important; + width: 280px !important; + margin-right: 1rem; +} + +body .article.row.grid-right { + margin-left: 300px !important; + margin-top: 0 !important; +} + +body .article.row.grid-right > .col-md-10 { + width: calc(100% - 300px) !important; +} + +body .body-content.hide-when-search { + padding-top: 10rem; +} + +body.api-sidebar-collapsed .article.row.grid-right > .hidden-sm.col-md-2 { + display: none !important; +} + +body.api-sidebar-collapsed .article.row.grid-right > .col-md-10 { + width: 100% !important; +} + +body.api-sidebar-collapsed .article.row.grid-right { + margin-left: 300px !important; +} + +body .article.row.grid-right > .hidden-sm.col-md-2 { + width: 280px; + padding-top: 1.5rem; +} + +.article.row.grid-right > .hidden-sm.col-md-2 { + padding-left: 0; +} + +.article.row.grid-right > .hidden-sm.col-md-2 .sideaffix { + top: 0; +} + +.api-layout-tools { + position: sticky; + top: 0.9rem; + z-index: 6; + display: flex; + justify-content: flex-end; + margin: 0 0 1rem; + padding: 0.55rem 0.75rem 0.7rem; + min-height: 3.25rem; + background: linear-gradient(180deg, rgba(17, 26, 47, 0.94), rgba(10, 16, 32, 0.88)); + border: 1px solid var(--site-border); + border-radius: 8px; + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.18); + backdrop-filter: blur(8px); +} + +.api-layout-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.6rem; + min-width: 9.5rem; + padding: 0.62rem 1rem; + color: var(--site-text); + background: rgba(13, 23, 48, 0.96); + border: 1px solid var(--site-border); + border-radius: 6px; + box-shadow: none; + font-size: 0.88rem; + font-weight: 700; + line-height: 1; + cursor: pointer; + white-space: nowrap; +} + +.api-layout-toggle:hover, +.api-layout-toggle:focus { + color: #ffffff; + border-color: var(--site-border-strong); + background: rgba(23, 37, 67, 0.98); + box-shadow: 0 0 0 2px rgba(45, 115, 255, 0.14); +} + +body.api-sidebar-collapsed .api-layout-tools { + margin-bottom: 1.15rem; +} + +/* Left TOC panel */ +.sidenav, +.fixed_header, +.toc, +.sidefilter, +.sidetoc { + background: transparent; +} + +.sidefilter { + position: sticky; + top: 0.75rem; + width: 100%; + padding: 1rem 1rem 0.85rem; + border: 1px solid var(--site-border); + border-bottom: 0; + border-radius: 8px 8px 0 0; + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); +} + +.sidetoc { + position: sticky; + top: 5rem; + width: 100%; + overflow-x: hidden; + overflow-y: auto; + border: 1px solid var(--site-border); + border-top: 0; + border-radius: 0 0 8px 8px; + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); +} + +.toc { + margin: 0; + padding: 0 1rem 1rem; +} + +.toc-filter { + background: rgba(13, 23, 48, 0.96); + color: var(--site-muted); + border: 1px solid var(--site-border); + border-radius: 6px; + padding: 0.45rem 0.65rem 0.45rem 2rem; + margin: 0; +} + +.toc-filter > input { + background: transparent; + color: var(--site-text); + padding: 0; + width: 100%; +} + +.toc-filter > input::placeholder { + color: var(--site-muted); +} + +.toc-filter > input:focus { + outline: 0; +} + +.toc-filter > .filter-icon, +.toc-filter > .clear-icon { + color: var(--site-muted); +} + +.toc-filter > .filter-icon { + top: 50%; + left: 0.65rem; + transform: translateY(-50%); +} + +.toc-filter > .clear-icon { + top: 50%; + right: 0.55rem; + transform: translateY(-50%); +} + +.toc .nav > li > a { + color: var(--site-muted); + margin-left: 0; + display: block; + padding: 0.4rem 0 0.4rem 0.85rem; + border-left: 2px solid transparent; +} + +.toc .nav > li > .expand-stub { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + height: 1.4rem; + margin-right: 0.2rem; + color: var(--site-muted); + cursor: pointer; + vertical-align: middle; +} + +.toc .nav > li > .expand-stub::before { + content: "▾"; + font-size: 0.85rem; + line-height: 1; +} + +.toc .nav > li.api-toc-collapsed > .expand-stub::before { + content: "▸"; +} + +.toc .nav > li.api-toc-collapsed > ul.nav { + display: none; +} + +.toc .nav > li > ul.nav[hidden] { + display: none !important; +} + +.toc .nav > li > ul.nav { + display: block; +} + +.toc .nav > li > a:hover, +.toc .nav > li > a:focus { + color: var(--site-text); + background: rgba(45, 115, 255, 0.06); + border-left-color: rgba(45, 115, 255, 0.35); +} + +.toc .nav > li.active > a { + color: var(--site-accent-strong); + border-left-color: var(--site-accent-strong); +} + +.toc .nav > li.active > a:hover, +.toc .nav > li.active > a:focus { + color: var(--site-accent-strong); +} + +.toc .nav > li > .expand-stub, +.toc .nav > li.active > .expand-stub { + color: var(--site-muted); +} + +.toc .level1 > li { + position: relative; + margin-top: 0.8rem; + font-size: 1rem; + font-weight: 700; +} + +.toc .level2 { + margin: 0.45rem 0 0.1rem 0.9rem; + font-size: 0.95rem; + font-weight: 400; +} + +@media (min-width: 992px) { + .article.row.grid-right > .col-md-10 { + width: 72%; + } + + .article.row.grid-right > .col-md-2 { + width: 28%; + } + + .sideaffix { + padding-left: 0.85rem; + } +} + +.sideaffix { + position: sticky; + top: 0; + padding-left: 0.5rem; +} + +.sideaffix .contribution, +.sideaffix .bs-docs-sidebar { + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); + border: 1px solid var(--site-border); + border-radius: 8px; + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); + width: 100%; +} + +.sideaffix .contribution { + padding: 1.15rem; + margin-bottom: 1rem; +} + +.article.row.grid-right > .hidden-sm.col-md-2 { + padding-top: 1rem; +} + +.sideaffix .contribution .nav { + margin-bottom: 0; + display: flex; + flex-direction: column; + gap: 0.65rem; +} + +.sideaffix .contribution .nav > li > a, +.sideaffix .contribution .nav > li > a:hover, +.sideaffix .contribution .nav > li > a:focus { + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 2.9rem; + padding: 0.82rem 1rem; + color: var(--site-accent-strong); + background: rgba(45, 115, 255, 0.08) !important; + border: 1px solid rgba(66, 224, 216, 0.18); + border-radius: 6px; + text-decoration: none; + box-shadow: none; +} + +.sideaffix .contribution .nav > li > a:hover, +.sideaffix .contribution .nav > li > a:focus { + color: #ffffff; + background: rgba(45, 115, 255, 0.16) !important; + border-color: rgba(66, 224, 216, 0.3); +} + +.sideaffix .contribution-link { + color: var(--site-accent-strong); + font-weight: 700; + font-size: 1rem; +} + +.sideaffix .contribution-link:hover, +.sideaffix .contribution-link:focus { + color: #8ff5ef; +} + +.sideaffix .bs-docs-sidebar { + padding: 1.15rem; +} + +.sideaffix .bs-docs-sidebar h5 { + margin: 0 0 1rem; + color: var(--site-text); + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; +} + +.sideaffix .bs-docs-sidebar .nav > li > a { + color: var(--site-muted); + padding: 0.72rem 0; + border-left: 2px solid transparent; + padding-left: 1rem; + margin-left: -1rem; + font-size: 0.97rem; + line-height: 1.5; +} + +.sideaffix .bs-docs-sidebar .nav > li > a:hover, +.sideaffix .bs-docs-sidebar .nav > li > a:focus { + color: var(--site-text); + border-left-color: var(--site-accent); + background: rgba(45, 115, 255, 0.08); +} + +.sideaffix .bs-docs-sidebar .nav > li.active > a, +.sideaffix .bs-docs-sidebar .nav > li.active > a:hover, +.sideaffix .bs-docs-sidebar .nav > li.active > a:focus { + color: var(--site-text); + border-left-color: var(--site-accent-strong); + background: rgba(45, 115, 255, 0.12); +} + +.sideaffix .bs-docs-sidebar .nav .nav > li > a { + padding-left: 1.45rem; + font-size: 0.92rem; +} + +.sideaffix .contribution + .bs-docs-sidebar { + margin-top: 0; +} + +.sideaffix .contribution + .bs-docs-sidebar, +.sideaffix .bs-docs-sidebar + .contribution { + margin-top: 1rem; +} + +.sideaffix .contribution .nav > li { + margin: 0; +} + +.sideaffix .bs-docs-sidebar .nav > li + li { + margin-top: 0.1rem; +} + +.btn-primary, +.btn.btn-primary { + background: linear-gradient(90deg, var(--site-accent), var(--site-accent-strong)); + border-color: transparent; + color: #08101f; +} + +.btn-default, +.btn.btn-default { + color: var(--site-text); + background: #121c32; + border-color: var(--site-border); +} + +.btn-default:hover, +.btn-default:focus, +.btn.btn-default:hover, +.btn.btn-default:focus { + color: #ffffff; + background: #172543; + border-color: var(--site-border-strong); +} + +.btn-primary:hover, +.btn-primary:focus, +.btn.btn-primary:hover, +.btn.btn-primary:focus { + filter: brightness(1.04); } .site-footer .footer { - background: var(--site-surface); + background: rgba(10, 16, 32, 0.96); border-top: 1px solid var(--site-border); } @@ -153,7 +960,7 @@ a:focus { display: flex; align-items: center; gap: 1rem; - min-height: 4rem; + min-height: 4.25rem; } .site-footer__brand { @@ -164,27 +971,213 @@ a:focus { color: var(--site-muted); } -@media (max-width: 767px) { - .site-topbar__inner, - .site-footer__inner, - .site-navbar .container { - flex-direction: column; - align-items: flex-start; - } +.content .hero, +.content .surface-card { + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); + border: 1px solid var(--site-border); + border-radius: 8px; + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); + transition: border-color 0.15s ease, background-color 0.15s ease; +} - .site-search { - width: 100%; - } +.content .surface-card { + padding: 1.25rem; +} - .site-search .form-control { - width: 100%; - } +.content .surface-card h2, +.content .surface-card h3, +.content .surface-card h4 { + position: relative; + padding-right: 1.65rem; +} - .site-nav { - margin-left: 0; - } +.content .surface-card h2 .anchorjs-link, +.content .surface-card h3 .anchorjs-link, +.content .surface-card h4 .anchorjs-link { + position: absolute !important; + top: 0.2rem; + right: 0; + left: auto !important; + display: inline-flex !important; + align-items: center; + margin: 0 !important; + padding: 0 !important; + line-height: 1; +} - .site-brand { - padding-bottom: 0.25rem; - } +.content .hero { + display: grid; + grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr); + gap: 1rem; + margin-bottom: 1rem; + overflow: hidden; +} + +.content .hero > * { + min-width: 0; +} + +.home-hero__copy { + display: flex; + flex-direction: column; + gap: 1.15rem; + justify-content: center; + padding: 1.8rem; +} + +.home-eyebrow { + margin: 0; + color: var(--site-accent-strong); + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.14em; + text-transform: uppercase; +} + +.home-eyebrow--accent { + color: var(--site-accent); +} + +.home-hero__copy h1 { + margin: 0; + font-size: 2.4rem; + line-height: 1.05; +} + +.home-lead { + margin: 0; + max-width: 52ch; + color: var(--site-muted); + font-size: 1.05rem; + line-height: 1.65; +} + +.home-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 0.1rem; +} + +.home-actions .btn { + min-height: 2.6rem; + padding: 0.65rem 1rem; + border-radius: 6px; +} + +.home-facts { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid var(--site-border); + color: var(--site-muted); + font-size: 0.95rem; +} + +.home-facts span { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.7rem; + border: 1px solid var(--site-border); + border-radius: 6px; + background: rgba(10, 16, 32, 0.9); +} + +.home-hero__visual { + display: flex; + flex-direction: column; + overflow: hidden; + padding: 0; +} + +.home-hero__visual img { + display: block; + width: 100%; + height: auto; + aspect-ratio: 16 / 9; + object-fit: cover; + border-radius: 8px 8px 0 0; +} + +.home-hero__visual-copy { + display: flex; + flex-direction: column; + gap: 0.85rem; + padding: 1.15rem 1.25rem 1.25rem; +} + +.home-stack { + margin: 0; + padding-left: 1.1rem; + display: grid; + gap: 0.6rem; + color: var(--site-text); + line-height: 1.55; +} + +.content .hero:hover, +.content .surface-card:hover { + border-color: var(--site-border-strong); +} + +.section-grid, +.content .section-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1rem; +} + +.section-grid--wide { + margin-top: 1rem; +} + +.home-panel { + display: flex; + flex-direction: column; + gap: 0.95rem; + min-height: 100%; +} + +.home-panel__header { + display: grid; + gap: 0.3rem; +} + +.home-panel__header h2 { + margin: 0; +} + +.home-panel__header p { + margin: 0; + color: var(--site-muted); + line-height: 1.5; +} + +.content .link-list { + display: grid; + gap: 0.45rem; +} + +.content .link-list a { + display: inline-flex; + width: fit-content; + color: var(--site-text); +} + +.content .link-list a:hover, +.content .link-list a:focus { + color: var(--site-accent-strong); +} + +.home-strip { + margin-top: 1rem; + padding: 1rem 1.25rem; +} + +.home-strip code { + display: inline-flex; + padding: 0.2rem 0.4rem; + border-radius: 4px; } diff --git a/templates/modularitykit/styles/main.js b/templates/modularitykit/styles/main.js new file mode 100644 index 0000000..0b5a5a2 --- /dev/null +++ b/templates/modularitykit/styles/main.js @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. +(function () { + var SIDEBAR_STATE_KEY = 'modularitykit.docfx.apiSidebarCollapsed'; + + function forceApiSidebarOpen() { + var sidetoggle = document.getElementById('sidetoggle'); + if (!sidetoggle) { + return; + } + + sidetoggle.classList.add('in'); + sidetoggle.classList.remove('collapse'); + sidetoggle.classList.remove('collapsing'); + sidetoggle.style.display = 'block'; + sidetoggle.style.height = 'auto'; + sidetoggle.style.visibility = 'visible'; + + var tocToggle = document.querySelector('.toc-toggle'); + if (tocToggle) { + tocToggle.setAttribute('aria-expanded', 'true'); + tocToggle.removeAttribute('data-toggle'); + tocToggle.removeAttribute('href'); + tocToggle.style.display = 'none'; + } + + } + + function getApiLayout() { + return document.querySelector('.article.row.grid-right'); + } + + function getApiSidebar() { + return document.querySelector('.article.row.grid-right > .hidden-sm.col-md-2'); + } + + function getApiMain() { + return document.querySelector('.article.row.grid-right > .col-md-10'); + } + + function readSidebarState() { + try { + return window.localStorage.getItem(SIDEBAR_STATE_KEY); + } catch (error) { + return null; + } + } + + function writeSidebarState(isCollapsed) { + try { + window.localStorage.setItem(SIDEBAR_STATE_KEY, isCollapsed ? '1' : '0'); + } catch (error) { + return; + } + } + + function getCurrentDocPath() { + var path = window.location.pathname.replace(/\/+$/, ''); + var file = path.split('/').pop() || ''; + return { + path: path, + file: file + }; + } + + function itemMatchesCurrentPage(item, current) { + var links = item.querySelectorAll('a[href]'); + for (var i = 0; i < links.length; i += 1) { + var href = links[i].getAttribute('href') || ''; + if (!href) { + continue; + } + + if (href === current.file) { + return true; + } + + if (current.path.indexOf(href.replace(/^\.\.\//, '')) !== -1) { + return true; + } + } + + return false; + } + + function setTocItemCollapsed(item, collapsed) { + var stub = null; + var childList = null; + var children = item.children; + + for (var i = 0; i < children.length; i += 1) { + if (!stub && children[i].classList.contains('expand-stub')) { + stub = children[i]; + } else if (!childList && children[i].tagName === 'UL' && children[i].classList.contains('nav')) { + childList = children[i]; + } + } + + if (!stub || !childList) { + return; + } + + item.classList.toggle('api-toc-collapsed', collapsed); + childList.hidden = collapsed; + stub.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + stub.setAttribute('title', collapsed ? 'Expand section' : 'Collapse section'); + } + + function toggleTocItem(item) { + setTocItemCollapsed(item, !item.classList.contains('api-toc-collapsed')); + } + + function ensureApiTocCollapsible() { + if (window.location.pathname.indexOf('/obj/api/') === -1) { + return; + } + + var tocRoot = document.querySelector('.toc .nav.level1'); + if (!tocRoot) { + return; + } + + var current = getCurrentDocPath(); + var items = tocRoot.children; + + Array.prototype.forEach.call(items, function (item) { + var childList = null; + var stub = null; + var children = item.children; + var j; + + for (j = 0; j < children.length; j += 1) { + if (!stub && children[j].classList && children[j].classList.contains('expand-stub')) { + stub = children[j]; + } else if (!childList && children[j].tagName === 'UL' && children[j].classList.contains('nav')) { + childList = children[j]; + } + } + + if (!childList || !stub) { + return; + } + + stub.setAttribute('role', 'button'); + stub.setAttribute('tabindex', '0'); + stub.setAttribute('aria-label', 'Toggle section'); + stub.style.display = 'inline-flex'; + stub.style.pointerEvents = 'auto'; + stub.textContent = ''; + + var shouldCollapse = !itemMatchesCurrentPage(item, current); + setTocItemCollapsed(item, shouldCollapse); + + stub.onclick = function (event) { + event.preventDefault(); + event.stopPropagation(); + toggleTocItem(item); + }; + + stub.onkeydown = function (event) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + toggleTocItem(item); + } + }; + }); + } + + function ensureApiSidebarToggle() { + if (window.location.pathname.indexOf('/obj/api/') === -1) { + return; + } + + var layout = getApiLayout(); + var sidebar = getApiSidebar(); + var main = getApiMain(); + + if (!layout || !sidebar || !main) { + return; + } + + var toolbar = main.querySelector('.api-layout-tools'); + if (!toolbar) { + toolbar = document.createElement('div'); + toolbar.className = 'api-layout-tools'; + + var toggle = document.createElement('button'); + toggle.type = 'button'; + toggle.className = 'api-layout-toggle'; + toggle.setAttribute('aria-expanded', 'true'); + toggle.setAttribute('aria-controls', 'api-sidepanel'); + toggle.textContent = 'Hide side panel'; + + toggle.addEventListener('click', function () { + var collapsed = document.body.classList.toggle('api-sidebar-collapsed'); + writeSidebarState(collapsed); + toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + toggle.textContent = collapsed ? 'Show side panel' : 'Hide side panel'; + }); + + toolbar.appendChild(toggle); + main.insertBefore(toolbar, main.firstChild); + } + + sidebar.id = 'api-sidepanel'; + + var collapsed = readSidebarState() === '1'; + document.body.classList.toggle('api-sidebar-collapsed', collapsed); + + var toggleButton = toolbar.querySelector('.api-layout-toggle'); + if (toggleButton) { + toggleButton.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); + toggleButton.textContent = collapsed ? 'Show side panel' : 'Hide side panel'; + } + } + + document.addEventListener('click', function (event) { + if (event.target.closest('.toc-toggle')) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + }, true); + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', forceApiSidebarOpen); + document.addEventListener('DOMContentLoaded', ensureApiSidebarToggle); + document.addEventListener('DOMContentLoaded', ensureApiTocCollapsible); + } else { + forceApiSidebarOpen(); + ensureApiSidebarToggle(); + ensureApiTocCollapsible(); + } +})(); diff --git a/templates/modularitykit/styles/mobile.css b/templates/modularitykit/styles/mobile.css new file mode 100644 index 0000000..74dfd63 --- /dev/null +++ b/templates/modularitykit/styles/mobile.css @@ -0,0 +1,272 @@ +@media (max-width: 991px) { + .site-footer__inner { + flex-direction: column; + align-items: flex-start; + } + + .site-navbar .container { + display: block; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + } + + .site-navbar__header { + display: grid; + grid-template-columns: auto minmax(0, 1fr); + align-items: center; + column-gap: 0.75rem; + width: 100%; + } + + .site-navbar__header .navbar-toggle { + display: inline-flex; + flex-direction: column; + justify-content: center; + gap: 0.22rem; + margin: 0; + padding: 0.7rem; + border: 1px solid var(--site-border); + border-radius: 10px; + background: rgba(13, 23, 48, 0.96); + float: none; + justify-self: start; + } + + .site-navbar__header .navbar-toggle .icon-bar { + width: 1.1rem; + height: 2px; + border-radius: 999px; + background: var(--site-text); + } + + .site-navbar__header .site-brand { + display: flex; + align-items: center; + min-width: 0; + margin: 0; + padding: 0; + float: none; + } + + .site-navbar__header .site-brand__copy { + min-width: 0; + } + + .site-navbar__header .site-brand__title { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + #navbar.navbar-collapse { + clear: both; + margin-top: 0.85rem; + padding: 0.9rem; + width: 100%; + border: 1px solid var(--site-border); + border-radius: 12px; + background: linear-gradient(180deg, rgba(17, 26, 47, 0.98), rgba(12, 19, 35, 0.98)); + box-shadow: 0 18px 44px rgba(3, 8, 20, 0.22); + } + + .site-nav { + width: 100%; + margin-left: 0; + margin-bottom: 0.85rem; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; + } + + .site-nav > li { + float: none; + width: 100%; + } + + .site-nav > li > a { + display: flex; + align-items: center; + min-height: 2.75rem; + padding: 0.7rem 0.85rem; + border: 1px solid rgba(53, 80, 122, 0.75); + background: rgba(45, 115, 255, 0.06); + } + + .site-nav > li.active > a, + .site-nav > li > a[aria-current="page"] { + border-color: rgba(66, 224, 216, 0.42); + } + + .site-navbar__actions { + width: 100%; + margin-left: 0; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 0.75rem; + } + + .site-search { + width: 100%; + margin: 0; + } + + .site-search.navbar-form { + padding: 0; + border: 0; + box-shadow: none; + } + + .site-search .form-group, + .site-search .form-control { + width: 100%; + } + + .site-github-link { + width: 3rem; + height: 3rem; + } + + body .body-content.hide-when-search { + padding-top: 1.5rem; + } + + body .sidenav.hide-when-search { + float: none !important; + width: auto !important; + margin-right: 0; + margin-bottom: 1rem; + } + + body .article.row.grid-right, + body.api-sidebar-collapsed .article.row.grid-right { + margin-left: 0 !important; + } + + body .article.row.grid-right > .col-md-10, + body.api-sidebar-collapsed .article.row.grid-right > .col-md-10 { + width: 100% !important; + } + + .article.row.grid-right > .col-md-10 { + margin-top: 0; + padding: 1rem; + } + + body .article.row.grid-right > .hidden-sm.col-md-2 { + width: 100%; + padding-top: 1rem; + } + + .api-layout-tools { + position: static; + min-height: 0; + padding: 0 0 0.85rem; + margin-bottom: 0.5rem; + background: transparent; + border: 0; + border-radius: 0; + box-shadow: none; + backdrop-filter: none; + } + + .api-layout-toggle { + width: 100%; + min-width: 0; + } + + .sideaffix { + position: static; + padding-left: 0; + } + + .sidefilter, + .sidetoc { + position: static; + width: auto; + border-radius: 0; + } + + .sidefilter { + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + + .sidetoc { + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + max-height: none; + } + + .content, + .surface-card, + .home-strip { + padding: 1rem; + } + + .content .hero { + grid-template-columns: 1fr; + } + + .home-hero__copy, + .home-hero__visual-copy { + padding: 1rem; + } + + .home-hero__copy h1 { + font-size: 2rem; + } + + .content .section-grid, + .section-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 767px) { + #navbar.navbar-collapse { + padding: 0.8rem; + } + + .site-nav { + grid-template-columns: 1fr; + } + + .site-brand__title { + font-size: 1rem; + } + + .site-brand__subtitle { + font-size: 0.72rem; + } + + .site-navbar__actions { + grid-template-columns: 1fr; + } + + .site-github-link { + width: 100%; + height: 2.85rem; + border-radius: 8px; + } + + .site-github-link svg { + width: 1.3rem; + height: 1.3rem; + } + + .content h1, + .article.row.grid-right > .col-md-10 > article.content h1, + .home-hero__copy h1 { + font-size: 1.8rem; + } + + .home-actions { + flex-direction: column; + } + + .home-actions .btn { + width: 100%; + justify-content: center; + } +} diff --git a/toc.yml b/toc.yml index 350c008..24c8aa3 100644 --- a/toc.yml +++ b/toc.yml @@ -1,5 +1,5 @@ - name: Home - href: Docs/Home.md + href: index.md - name: Packages items: - name: ModularityKit.Mutator @@ -18,11 +18,11 @@ href: Docs/ExecutionModel.md - name: Roadmap href: Docs/Roadmap.md +- name: Decision + items: + - name: ADR Index + href: Docs/Decision/listadr.md - name: API items: - name: Reference href: Docs/API/Reference.md - - name: Core API - href: Docs/API/API.md - - name: Redis API - href: Docs/API/Redis.md