Browse Source

Merge branch 'master' into support/migrate-to-y-websocket

Yuki Takei 2 weeks ago
parent
commit
5695973d31
33 changed files with 1047 additions and 289 deletions
  1. 1 1
      .github/mergify.yml
  2. 324 0
      .kiro/specs/optimize-presentation/design.md
  3. 58 0
      .kiro/specs/optimize-presentation/requirements.md
  4. 84 0
      .kiro/specs/optimize-presentation/research.md
  5. 22 0
      .kiro/specs/optimize-presentation/spec.json
  6. 49 0
      .kiro/specs/optimize-presentation/tasks.md
  7. 11 1
      CHANGELOG.md
  8. 5 5
      apps/app/.claude/skills/vendor-styles-components/SKILL.md
  9. 1 1
      apps/app/.gitignore
  10. 1 1
      apps/app/docker/README.md
  11. 4 7
      apps/app/package.json
  12. 28 28
      apps/app/src/client/components/ContentLinkButtons.tsx
  13. 4 4
      apps/app/src/client/components/PageSideContents/PageSideContents.tsx
  14. 1 1
      apps/app/src/client/components/StaffCredit/StaffCredit.module.scss
  15. 64 14
      apps/app/src/client/components/StaffCredit/StaffCredit.tsx
  16. 38 0
      apps/app/src/client/util/smooth-scroll.ts
  17. 2 5
      apps/app/src/features/search/client/components/SearchPage/SearchResultContent.tsx
  18. 1 1
      apps/app/src/server/service/config-manager/config-definition.ts
  19. 1 4
      apps/app/tsconfig.json
  20. 4 4
      apps/app/turbo.json
  21. 4 4
      apps/app/vite.vendor-styles-components.ts
  22. 1 1
      biome.json
  23. 1 1
      package.json
  24. 9 0
      packages/editor/src/client/services/use-codemirror-editor/use-codemirror-editor.ts
  25. 2 0
      packages/presentation/.gitignore
  26. 1 1
      packages/presentation/package.json
  27. 70 0
      packages/presentation/scripts/extract-marpit-css.ts
  28. 5 7
      packages/presentation/src/client/components/GrowiSlides.tsx
  29. 17 3
      packages/presentation/src/client/components/Slides.tsx
  30. 2 0
      packages/presentation/src/client/consts/index.ts
  31. 4 1
      packages/presentation/src/client/services/growi-marpit.ts
  32. 27 0
      packages/presentation/turbo.json
  33. 201 194
      pnpm-lock.yaml

+ 1 - 1
.github/mergify.yml

@@ -6,7 +6,7 @@ queue_rules:
       - check-success ~= ci-app-launch-dev
       - check-success ~= ci-app-launch-dev
       - -check-failure ~= ci-app-
       - -check-failure ~= ci-app-
       - -check-failure ~= ci-slackbot-
       - -check-failure ~= ci-slackbot-
-      - -check-failure ~= test-prod-node20 /
+      - -check-failure ~= test-prod-node24 /
     merge_conditions:
     merge_conditions:
       - check-success ~= ci-app-lint
       - check-success ~= ci-app-lint
       - check-success ~= ci-app-test
       - check-success ~= ci-app-test

+ 324 - 0
.kiro/specs/optimize-presentation/design.md

@@ -0,0 +1,324 @@
+# Design Document: optimize-presentation
+
+## Overview
+
+**Purpose**: This feature decouples heavy Marp rendering dependencies (`@marp-team/marp-core`, `@marp-team/marpit`) from the common slide rendering path, so they are loaded only when a page explicitly uses `marp: true` frontmatter.
+
+**Users**: All GROWI users benefit from reduced JavaScript payload when viewing slide pages that do not use Marp. Developers benefit from clearer module boundaries.
+
+**Impact**: Changes the `@growi/presentation` package's internal import structure. No external API or behavioral changes.
+
+### Goals
+- Eliminate `@marp-team/marp-core` and `@marp-team/marpit` from the GrowiSlides module graph
+- Load MarpSlides and its Marp dependencies only on demand via dynamic import
+- Maintain identical rendering behavior for all slide types
+
+### Non-Goals
+- Optimizing the `useSlidesByFrontmatter` hook (already lightweight with internal dynamic imports)
+- Changing the app-level lazy-loading architecture (`useLazyLoader`, `next/dynamic` for the modal)
+- Modifying the Marp rendering logic or configuration itself
+- Reducing the size of `@marp-team/marp-core` internals
+
+## Architecture
+
+### Existing Architecture Analysis
+
+Current module dependency graph for `Slides.tsx`:
+
+```mermaid
+graph TD
+  Slides[Slides.tsx]
+  MarpSlides[MarpSlides.tsx]
+  GrowiSlides[GrowiSlides.tsx]
+  GrowiMarpit[growi-marpit.ts]
+  MarpCore[marp-team/marp-core]
+  Marpit[marp-team/marpit]
+
+  Slides --> MarpSlides
+  Slides --> GrowiSlides
+  MarpSlides --> GrowiMarpit
+  GrowiSlides --> GrowiMarpit
+  GrowiMarpit --> MarpCore
+  GrowiMarpit --> Marpit
+```
+
+**Problem**: Both `MarpSlides` and `GrowiSlides` statically import `growi-marpit.ts`, which instantiates Marp at module scope. This forces ~896KB of Marp modules to load even when rendering non-Marp slides.
+
+**Constraints**:
+- `growi-marpit.ts` exports both runtime Marp instances and a simple string constant (`MARP_CONTAINER_CLASS_NAME`)
+- `GrowiSlides` uses Marp only for CSS extraction (`marpit.render('')`), not for content rendering
+- The package uses Vite with `preserveModules: true` and `nodeExternals({ devDeps: true })`
+
+### Architecture Pattern & Boundary Map
+
+Target module dependency graph:
+
+```mermaid
+graph TD
+  Slides[Slides.tsx]
+  MarpSlides[MarpSlides.tsx]
+  GrowiSlides[GrowiSlides.tsx]
+  GrowiMarpit[growi-marpit.ts]
+  MarpCore[marp-team/marp-core]
+  Marpit[marp-team/marpit]
+  Consts[consts/index.ts]
+  BaseCss[consts/marpit-base-css.ts]
+
+  Slides -.->|React.lazy| MarpSlides
+  Slides --> GrowiSlides
+  MarpSlides --> GrowiMarpit
+  GrowiMarpit --> MarpCore
+  GrowiMarpit --> Marpit
+  GrowiMarpit --> Consts
+  GrowiSlides --> Consts
+  GrowiSlides --> BaseCss
+```
+
+**Key changes**:
+- Dashed arrow = dynamic import (React.lazy), solid arrow = static import
+- `GrowiSlides` no longer imports `growi-marpit.ts`; uses pre-extracted CSS from `marpit-base-css.ts`
+- `MARP_CONTAINER_CLASS_NAME` moves to shared `consts/index.ts`
+- Marp modules are only reachable through the dynamic `MarpSlides` path
+
+**Steering compliance**:
+- Follows "subpath imports over barrel imports" principle
+- Aligns with `next/dynamic({ ssr: false })` technique from build-optimization skill
+- Preserves existing lazy-loading boundaries at app level
+
+### Technology Stack
+
+| Layer | Choice / Version | Role in Feature | Notes |
+|-------|------------------|-----------------|-------|
+| Frontend | React 18 (`React.lazy`, `Suspense`) | Dynamic import boundary for MarpSlides | Standard React code-splitting; SSR not a concern (see research.md) |
+| Build | Vite (existing) | Package compilation with `preserveModules` | Dynamic `import()` preserved in output |
+| Build script | Node.js ESM (.mjs) | CSS extraction at build time | No additional tooling dependency |
+
+## System Flows
+
+### Slide Rendering Decision Flow
+
+```mermaid
+flowchart TD
+  A[Slides component receives props] --> B{hasMarpFlag?}
+  B -->|true| C[React.lazy loads MarpSlides chunk]
+  C --> D[MarpSlides renders via marp-core]
+  B -->|false| E[GrowiSlides renders with pre-extracted CSS]
+  E --> F[No Marp modules loaded]
+```
+
+### Build-Time CSS Extraction Flow
+
+```mermaid
+flowchart TD
+  A[pnpm run build] --> B[pre:build:src script runs]
+  B --> C[extract-marpit-css.mjs executes]
+  C --> D[Instantiate Marp with config]
+  D --> E[Call slideMarpit.render and presentationMarpit.render]
+  E --> F[Write marpit-base-css.ts with CSS constants]
+  F --> G[vite build compiles all sources]
+```
+
+## Requirements Traceability
+
+| Requirement | Summary | Components | Interfaces | Flows |
+|-------------|---------|------------|------------|-------|
+| 1.1 | No marp-core references in GrowiSlides build output | GrowiSlides, marpit-base-css | — | — |
+| 1.2 | Non-Marp slides render without loading Marp | Slides, GrowiSlides | — | Slide Rendering Decision |
+| 1.3 | Pre-extracted CSS constants for container styling | marpit-base-css | MarpitBaseCss | CSS Extraction |
+| 1.4 | MARP_CONTAINER_CLASS_NAME in shared consts | consts/index.ts | — | — |
+| 2.1 | Dynamic load MarpSlides for marp:true pages | Slides | — | Slide Rendering Decision |
+| 2.2 | Loading indicator during MarpSlides load | Slides | — | Slide Rendering Decision |
+| 2.3 | No MarpSlides import triggered for non-Marp | Slides | — | Slide Rendering Decision |
+| 3.1 | Build-time CSS extraction script | extract-marpit-css.mjs | ExtractScript | CSS Extraction |
+| 3.2 | Extraction runs before source compilation | package.json pre:build:src | — | CSS Extraction |
+| 3.3 | Generated file committed for dev mode | marpit-base-css.ts | — | — |
+| 4.1–4.5 | Functional equivalence across all render paths | All components | — | Both flows |
+| 5.1–5.4 | Build verification of module separation | Build outputs | — | — |
+
+## Components and Interfaces
+
+| Component | Domain | Intent | Req Coverage | Key Dependencies | Contracts |
+|-----------|--------|--------|--------------|------------------|-----------|
+| Slides | UI / Routing | Route between MarpSlides and GrowiSlides based on hasMarpFlag | 2.1, 2.2, 2.3 | MarpSlides (P1, dynamic), GrowiSlides (P0, static) | — |
+| GrowiSlides | UI / Rendering | Render non-Marp slides with pre-extracted CSS | 1.1, 1.2, 1.3, 1.4 | consts (P0), marpit-base-css (P0) | — |
+| marpit-base-css | Constants | Provide pre-extracted Marp theme CSS | 1.3, 3.3 | — | State |
+| consts/index.ts | Constants | Shared constants including MARP_CONTAINER_CLASS_NAME | 1.4 | — | — |
+| growi-marpit.ts | Service | Marp engine setup (unchanged, now only reached via MarpSlides) | 4.1, 4.3 | marp-core (P0, external), marpit (P0, external) | Service |
+| extract-marpit-css.mjs | Build Script | Generate marpit-base-css.ts at build time | 3.1, 3.2 | marp-core (P0, external), marpit (P0, external) | — |
+
+### UI / Routing Layer
+
+#### Slides
+
+| Field | Detail |
+|-------|--------|
+| Intent | Route rendering to MarpSlides (dynamic) or GrowiSlides (static) based on `hasMarpFlag` prop |
+| Requirements | 2.1, 2.2, 2.3 |
+
+**Responsibilities & Constraints**
+- Conditionally render MarpSlides or GrowiSlides based on `hasMarpFlag`
+- Wrap MarpSlides in `<Suspense>` with a loading fallback
+- Must not statically import MarpSlides
+
+**Dependencies**
+- Outbound: MarpSlides — dynamic import for Marp rendering (P1)
+- Outbound: GrowiSlides — static import for non-Marp rendering (P0)
+
+**Contracts**: State [x]
+
+##### State Management
+
+```typescript
+// Updated Slides component signature (unchanged externally)
+type SlidesProps = {
+  options: PresentationOptions;
+  children?: string;
+  hasMarpFlag?: boolean;
+  presentation?: boolean;
+};
+
+// Internal: MarpSlides loaded via React.lazy
+// const MarpSlides = lazy(() => import('./MarpSlides').then(mod => ({ default: mod.MarpSlides })))
+```
+
+- Persistence: None (stateless component)
+- Concurrency: Single render path per props
+
+**Implementation Notes**
+- Integration: Replace static `import { MarpSlides }` with `React.lazy` dynamic import
+- Validation: `hasMarpFlag` is optional boolean; undefined/false → GrowiSlides path
+- Risks: Suspense fallback visible during first MarpSlides load (mitigated by parent-level loading indicators)
+
+### UI / Rendering Layer
+
+#### GrowiSlides
+
+| Field | Detail |
+|-------|--------|
+| Intent | Render non-Marp slides using pre-extracted CSS instead of runtime Marp |
+| Requirements | 1.1, 1.2, 1.3, 1.4 |
+
+**Responsibilities & Constraints**
+- Render slide content via ReactMarkdown with section extraction
+- Apply Marp container CSS from pre-extracted constants (no runtime Marp dependency)
+- Use `MARP_CONTAINER_CLASS_NAME` from shared constants module
+
+**Dependencies**
+- Inbound: Slides — rendering delegation (P0)
+- Outbound: consts/index.ts — `MARP_CONTAINER_CLASS_NAME` (P0)
+- Outbound: consts/marpit-base-css — `SLIDE_MARPIT_CSS`, `PRESENTATION_MARPIT_CSS` (P0)
+
+**Implementation Notes**
+- Integration: Replace `import { ... } from '../services/growi-marpit'` with imports from `../consts` and `../consts/marpit-base-css`
+- Replace `const { css } = marpit.render('')` with `const css = presentation ? PRESENTATION_MARPIT_CSS : SLIDE_MARPIT_CSS`
+
+### Constants Layer
+
+#### marpit-base-css
+
+| Field | Detail |
+|-------|--------|
+| Intent | Provide pre-extracted Marp theme CSS as string constants |
+| Requirements | 1.3, 3.3 |
+
+**Contracts**: State [x]
+
+##### State Management
+
+```typescript
+// Generated file — do not edit manually
+// Regenerate with: node scripts/extract-marpit-css.mjs
+
+export const SLIDE_MARPIT_CSS: string;
+export const PRESENTATION_MARPIT_CSS: string;
+```
+
+- Persistence: Committed to repository; regenerated at build time
+- Consistency: Deterministic output from Marp config; changes only on `@marp-team/marp-core` version update
+
+#### consts/index.ts
+
+| Field | Detail |
+|-------|--------|
+| Intent | Shared constants for presentation package |
+| Requirements | 1.4 |
+
+**Implementation Notes**
+- Add `export const MARP_CONTAINER_CLASS_NAME = 'marpit'` (moved from `growi-marpit.ts`)
+- Existing `PresentationOptions` type export unchanged
+
+### Service Layer
+
+#### growi-marpit.ts
+
+| Field | Detail |
+|-------|--------|
+| Intent | Marp engine setup and instance creation (unchanged behavior, now only reachable via MarpSlides dynamic path) |
+| Requirements | 4.1, 4.3 |
+
+**Contracts**: Service [x]
+
+##### Service Interface
+
+```typescript
+// Existing exports (unchanged)
+export const MARP_CONTAINER_CLASS_NAME: string; // Re-exported from consts for backward compat
+export const slideMarpit: Marp;
+export const presentationMarpit: Marp;
+```
+
+- Preconditions: `@marp-team/marp-core` and `@marp-team/marpit` available
+- Postconditions: Marp instances configured with GROWI options
+- Invariants: Instances are singletons created at module scope
+
+**Implementation Notes**
+- Import `MARP_CONTAINER_CLASS_NAME` from `../consts` instead of defining locally
+- Re-export for backward compatibility (MarpSlides still imports from here)
+
+### Build Script
+
+#### extract-marpit-css.mjs
+
+| Field | Detail |
+|-------|--------|
+| Intent | Generate `marpit-base-css.ts` by running Marp with the same configuration as `growi-marpit.ts` |
+| Requirements | 3.1, 3.2 |
+
+**Responsibilities & Constraints**
+- Replicate the Marp configuration from `growi-marpit.ts` (container classes, inlineSVG, etc.)
+- Generate `slideMarpit.render('')` and `presentationMarpit.render('')` CSS output
+- Write TypeScript file with exported string constants
+- Must run in Node.js ESM without additional tooling (no `tsx`, no `ts-node`)
+
+**Dependencies**
+- External: `@marp-team/marp-core` — Marp rendering engine (P0)
+- External: `@marp-team/marpit` — Element class for container config (P0)
+
+**Implementation Notes**
+- Integration: Added as `"pre:build:src"` script in `package.json`; runs before `vite build`
+- Validation: Script exits with error if CSS extraction produces empty output
+- Risks: Marp options must stay synchronized with `growi-marpit.ts`
+
+## Testing Strategy
+
+### Unit Tests
+- Verify `GrowiSlides` renders correctly with pre-extracted CSS constants (no Marp imports in test)
+- Verify `Slides` renders `GrowiSlides` when `hasMarpFlag` is false/undefined
+- Verify `Slides` renders `MarpSlides` (via Suspense) when `hasMarpFlag` is true
+
+### Build Verification Tests
+- `GrowiSlides.js` build output contains no references to `@marp-team/marp-core` or `@marp-team/marpit`
+- `Slides.js` build output contains dynamic `import()` for MarpSlides
+- `@growi/presentation` builds without errors
+- `@growi/app` builds without errors
+
+### Integration Tests
+- Marp slide page (`marp: true`) renders correctly in inline view
+- Non-Marp slide page (`slide: true`) renders correctly in inline view
+- Presentation modal works for both Marp and non-Marp content
+
+## Performance & Scalability
+
+**Target**: Eliminate ~896KB of Marp-related JavaScript from the async chunk loaded for non-Marp slide rendering.
+
+**Measurement**: Compare the chunk contents before and after optimization using the existing `ChunkModuleStatsPlugin` or manual inspection of build output. The `initial` module count (primary KPI from build-optimization skill) is not directly affected since slides are already in async chunks, but the async chunk size is reduced.

+ 58 - 0
.kiro/specs/optimize-presentation/requirements.md

@@ -0,0 +1,58 @@
+# Requirements Document
+
+## Introduction
+
+The GROWI presentation feature (`@growi/presentation` package) statically imports `@marp-team/marp-core` (~524KB) and `@marp-team/marpit` (~372KB) whenever any slide component loads, even when Marp rendering is not needed. This is because both `MarpSlides` and `GrowiSlides` components statically import `growi-marpit.ts`, which instantiates Marp objects at module scope.
+
+The goal is to decouple heavy Marp dependencies so they are only loaded when a page explicitly uses `marp: true` in its frontmatter, reducing the async chunk size for the common non-Marp slide rendering path and improving overall bundle efficiency.
+
+## Requirements
+
+### Requirement 1: Decouple GrowiSlides from Marp Runtime Dependencies
+
+**Objective:** As a developer, I want GrowiSlides to render without loading `@marp-team/marp-core` or `@marp-team/marpit`, so that non-Marp slide pages do not incur unnecessary module loading overhead.
+
+#### Acceptance Criteria
+1. The `@growi/presentation` build output for GrowiSlides shall not contain import references to `@marp-team/marp-core` or `@marp-team/marpit`.
+2. When a slide page without `marp: true` is rendered, the Presentation module shall render GrowiSlides without loading `@marp-team/marp-core` or `@marp-team/marpit` modules.
+3. The Presentation module shall provide the Marp base CSS (previously generated by `marpit.render('')`) as pre-extracted static string constants, so that GrowiSlides can apply Marp container styling without a runtime Marp dependency.
+4. The `MARP_CONTAINER_CLASS_NAME` constant shall be defined in a shared constants module, not in `growi-marpit.ts`, to avoid transitive Marp imports.
+
+### Requirement 2: Dynamic Loading of MarpSlides
+
+**Objective:** As a developer, I want MarpSlides to be loaded dynamically (on demand), so that the Marp rendering engine is only fetched when a page actually uses Marp.
+
+#### Acceptance Criteria
+1. When a slide page with `marp: true` is rendered, the Presentation module shall dynamically load MarpSlides and render Marp content correctly.
+2. While MarpSlides is loading, the Presentation module shall display a loading indicator (Suspense fallback).
+3. When a slide page without `marp: true` is rendered, the Presentation module shall not trigger the dynamic import of MarpSlides.
+
+### Requirement 3: Build-Time CSS Extraction
+
+**Objective:** As a developer, I want the Marp base CSS to be extracted at build time via an automated script, so that the pre-extracted CSS stays synchronized with the installed `@marp-team/marp-core` version.
+
+#### Acceptance Criteria
+1. The `@growi/presentation` package shall include a build-time script that generates Marp base CSS constants by invoking `slideMarpit.render('')` and `presentationMarpit.render('')`.
+2. When `pnpm run build` is executed for `@growi/presentation`, the build pipeline shall regenerate the CSS constants before compiling source files.
+3. The generated CSS constants file shall be committed to the repository so that `dev` mode works without running the extraction script first.
+
+### Requirement 4: Functional Equivalence
+
+**Objective:** As a user, I want the presentation feature to behave identically after optimization, so that existing Marp and non-Marp presentations continue to work without regression.
+
+#### Acceptance Criteria
+1. When a page with `marp: true` frontmatter is viewed inline, the Presentation module shall render Marp slides with correct styling.
+2. When a page with `slide: true` frontmatter (without `marp: true`) is viewed inline, the Presentation module shall render GrowiSlides with correct styling.
+3. When the presentation modal is opened for a Marp page, the Presentation module shall render Marp slides in the modal with correct fullscreen behavior.
+4. When the presentation modal is opened for a non-Marp slide page, the Presentation module shall render GrowiSlides in the modal with correct fullscreen behavior.
+5. When a non-slide page is viewed, the Presentation module shall not load any slide rendering components (existing lazy-loading behavior preserved).
+
+### Requirement 5: Build Verification
+
+**Objective:** As a developer, I want to verify that the optimization achieves its goal, so that Marp module separation can be confirmed in CI and during development.
+
+#### Acceptance Criteria
+1. The `@growi/presentation` package shall build successfully with `pnpm run build`.
+2. The `@growi/app` package shall build successfully with `turbo run build --filter @growi/app`.
+3. The built `GrowiSlides.js` output shall contain no references to `@marp-team/marp-core` or `@marp-team/marpit`.
+4. The built `Slides.js` output shall contain a dynamic `import()` expression for `MarpSlides`.

+ 84 - 0
.kiro/specs/optimize-presentation/research.md

@@ -0,0 +1,84 @@
+# Research & Design Decisions
+
+## Summary
+- **Feature**: `optimize-presentation`
+- **Discovery Scope**: Extension
+- **Key Findings**:
+  - `GrowiSlides` imports `growi-marpit.ts` only for CSS extraction (`marpit.render('')`) and a string constant — no Marp rendering is performed
+  - `growi-marpit.ts` instantiates `new Marp(...)` at module scope, pulling in `@marp-team/marp-core` (~524KB) and `@marp-team/marpit` (~372KB) unconditionally
+  - All consumers of `Slides.tsx` are already behind SSR-disabled dynamic boundaries (`next/dynamic`, `useLazyLoader`), making `React.lazy` safe to use within the package
+
+## Research Log
+
+### GrowiSlides dependency on growi-marpit.ts
+- **Context**: Investigating why GrowiSlides requires Marp at all
+- **Sources Consulted**: `packages/presentation/src/client/components/GrowiSlides.tsx`, `packages/presentation/src/client/services/growi-marpit.ts`
+- **Findings**:
+  - `GrowiSlides` imports `MARP_CONTAINER_CLASS_NAME` (string `'marpit'`), `presentationMarpit`, `slideMarpit`
+  - Usage: `const { css } = marpit.render('');` — renders empty string to extract base theme CSS
+  - The CSS output is deterministic (same Marp config → same CSS every time)
+  - `MARP_CONTAINER_CLASS_NAME` is a plain string constant co-located with heavy Marp code
+- **Implications**: GrowiSlides can be fully decoupled by pre-extracting the CSS and moving the constant
+
+### Vite build with preserveModules and React.lazy
+- **Context**: Verifying that React.lazy dynamic import works correctly in the package's Vite build output
+- **Sources Consulted**: `packages/presentation/vite.config.ts`
+- **Findings**:
+  - Build uses `preserveModules: true` with `preserveModulesRoot: 'src'` — each source file → separate output module
+  - `nodeExternals({ devDeps: true })` externalizes `@marp-team/marp-core` and `@marp-team/marpit`
+  - Dynamic `import('./MarpSlides')` in source will produce dynamic `import('./MarpSlides.js')` in output
+  - Next.js (app bundler) handles code-splitting of the externalized Marp packages into async chunks
+- **Implications**: React.lazy in Slides.tsx will produce the correct dynamic import boundary in the build output
+
+### SSR safety of React.lazy
+- **Context**: React.lazy does not support SSR in React 18; need to verify all render paths are client-only
+- **Sources Consulted**: Consumer components in apps/app
+- **Findings**:
+  - `PagePresentationModal`: `useLazyLoader` → client-only
+  - `Presentation` wrapper: `next/dynamic({ ssr: false })`
+  - `SlideRenderer`: `next/dynamic({ ssr: false })` in PageView.tsx
+  - No SSR path exists for `Slides.tsx`
+- **Implications**: React.lazy is safe; no need for `next/dynamic` in the shared package
+
+### CSS extraction approach
+- **Context**: Evaluating how to pre-extract the Marp base CSS
+- **Sources Consulted**: `growi-marpit.ts`, package.json scripts, existing `build:vendor-styles` pattern
+- **Findings**:
+  - `slideMarpit.render('')` and `presentationMarpit.render('')` produce deterministic CSS strings
+  - CSS only changes when `@marp-team/marp-core` is upgraded or Marp options change
+  - The package already has a `build:vendor-styles` script pattern for pre-building CSS assets
+  - `tsx` is not available in the workspace; extraction script must use plain Node.js (.mjs)
+- **Implications**: An .mjs extraction script with dynamic imports from the built package or direct Marp usage is the simplest approach
+
+## Design Decisions
+
+### Decision: Pre-extracted CSS constants vs runtime generation
+- **Context**: GrowiSlides needs Marp theme CSS but should not load Marp runtime
+- **Alternatives Considered**:
+  1. Dynamic import of growi-marpit in GrowiSlides — adds async complexity for a static value
+  2. Pre-extract CSS at build time as string constants — zero runtime cost
+  3. CSS file extracted to dist — requires additional CSS import handling
+- **Selected Approach**: Pre-extract CSS as TypeScript string constants in `consts/marpit-base-css.ts`
+- **Rationale**: The CSS is deterministic; generating it at build time eliminates runtime overhead entirely. TypeScript constants integrate seamlessly with existing import patterns.
+- **Trade-offs**: Requires regeneration on Marp version upgrade (mitigated by `pre:build:src` script)
+- **Follow-up**: Verify CSS output matches runtime generation; commit generated file for dev mode
+
+### Decision: React.lazy vs next/dynamic for MarpSlides
+- **Context**: Need dynamic import boundary for MarpSlides within the shared `@growi/presentation` package
+- **Alternatives Considered**:
+  1. `next/dynamic` — Next.js-specific, couples package to framework
+  2. `React.lazy + Suspense` — standard React, works with any bundler
+- **Selected Approach**: `React.lazy + Suspense`
+- **Rationale**: Although the package already uses `next/head`, `React.lazy` is the standard React pattern for code-splitting and avoids further Next.js coupling. All consumer paths already disable SSR, so React.lazy's SSR limitation is irrelevant.
+- **Trade-offs**: Requires `<Suspense>` wrapper; fallback UI is visible during chunk load
+- **Follow-up**: Verify chunk splitting in Next.js production build
+
+### Decision: Shared constants module for MARP_CONTAINER_CLASS_NAME
+- **Context**: `MARP_CONTAINER_CLASS_NAME` is defined in `growi-marpit.ts` but needed by GrowiSlides without Marp dependency
+- **Selected Approach**: Move to `consts/index.ts` (existing shared constants module)
+- **Rationale**: The constant has no dependency on Marp; co-locating it with Marp code creates an unnecessary transitive import
+
+## Risks & Mitigations
+- **CSS drift on Marp upgrade**: Pre-extracted CSS may become stale → `pre:build:src` script auto-regenerates before every build
+- **Suspense flash on Marp load**: Brief loading indicator when MarpSlides loads → Masked by parent `next/dynamic` loading spinner in most paths
+- **Build script compatibility**: `.mjs` script must work in CI and local dev → Use standard Node.js ESM with no external tooling dependencies

+ 22 - 0
.kiro/specs/optimize-presentation/spec.json

@@ -0,0 +1,22 @@
+{
+  "feature_name": "optimize-presentation",
+  "created_at": "2026-03-05T12:00:00Z",
+  "updated_at": "2026-03-05T13:30:00Z",
+  "language": "en",
+  "phase": "implementation-complete",
+  "approvals": {
+    "requirements": {
+      "generated": true,
+      "approved": true
+    },
+    "design": {
+      "generated": true,
+      "approved": true
+    },
+    "tasks": {
+      "generated": true,
+      "approved": true
+    }
+  },
+  "ready_for_implementation": true
+}

+ 49 - 0
.kiro/specs/optimize-presentation/tasks.md

@@ -0,0 +1,49 @@
+# Implementation Plan
+
+- [x] 1. Set up shared constants and build-time CSS extraction infrastructure
+- [x] 1.1 Move the Marp container class name constant to the shared constants module and update growi-marpit to import from there
+  - Add the `MARP_CONTAINER_CLASS_NAME` string constant to the existing shared constants module in the presentation package
+  - Update growi-marpit to import the constant from the shared module instead of defining it locally
+  - Re-export the constant from growi-marpit for backward compatibility with MarpSlides
+  - _Requirements: 1.4_
+
+- [x] 1.2 Create the build-time CSS extraction script
+  - Write a Node.js ESM script that instantiates Marp with the same configuration as growi-marpit (container classes, inlineSVG, emoji/html/math disabled)
+  - The script renders empty strings through both slide and presentation Marp instances to extract their CSS output
+  - Write the CSS strings as exported TypeScript constants to the constants directory
+  - Include a file header comment indicating the file is auto-generated and how to regenerate it
+  - Validate that extracted CSS is non-empty before writing
+  - _Requirements: 3.1_
+
+- [x] 1.3 Wire the extraction script into the build pipeline and generate the initial CSS file
+  - Add a `pre:build:src` script entry in the presentation package's package.json that runs the extraction script before the main Vite build
+  - Execute the script once to generate the initial pre-extracted CSS constants file
+  - Commit the generated file so that dev mode works without running extraction first
+  - _Requirements: 3.2, 3.3_
+
+- [x] 2. (P) Decouple GrowiSlides from Marp runtime dependencies
+  - Replace the growi-marpit import in GrowiSlides with imports from the shared constants module and the pre-extracted CSS constants
+  - Replace the runtime `marpit.render('')` call with a lookup of the pre-extracted CSS constant based on the presentation mode flag
+  - After this change, GrowiSlides must have no import path leading to `@marp-team/marp-core` or `@marp-team/marpit`
+  - Depends on task 1 (shared constants and CSS file must exist)
+  - _Requirements: 1.1, 1.2, 1.3_
+
+- [x] 3. (P) Add dynamic import for MarpSlides in the Slides routing component
+  - Replace the static import of MarpSlides with a React.lazy dynamic import that resolves the named export
+  - Wrap the MarpSlides rendering branch in a Suspense boundary with a simple loading fallback
+  - Keep GrowiSlides as a static import (the common, lightweight path)
+  - The dynamic import ensures MarpSlides and its transitive Marp dependencies are only loaded when `hasMarpFlag` is true
+  - Depends on task 1 (shared constants must exist); parallel-safe with task 2 (different file)
+  - _Requirements: 2.1, 2.2, 2.3_
+
+- [x] 4. Build verification and functional validation
+- [x] 4.1 Build the presentation package and verify module separation in the output
+  - Run the presentation package build and confirm it succeeds
+  - Inspect the built GrowiSlides output file to confirm it contains no references to `@marp-team/marp-core` or `@marp-team/marpit`
+  - Inspect the built Slides output file to confirm it contains a dynamic `import()` expression for MarpSlides
+  - _Requirements: 5.1, 5.3, 5.4_
+
+- [x] 4.2 Build the main GROWI application and verify successful compilation
+  - Run the full app build to confirm no regressions from the presentation package changes
+  - Verify that both Marp and non-Marp slide rendering paths are intact by checking the build completes without type errors
+  - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 5.2_

+ 11 - 1
CHANGELOG.md

@@ -1,9 +1,19 @@
 # Changelog
 # Changelog
 
 
-## [Unreleased](https://github.com/growilabs/compare/v7.4.6...HEAD)
+## [Unreleased](https://github.com/growilabs/compare/v7.4.7...HEAD)
 
 
 *Please do not manually update this file. We've automated the process.*
 *Please do not manually update this file. We've automated the process.*
 
 
+## [v7.4.7](https://github.com/growilabs/compare/v7.4.6...v7.4.7) - 2026-03-23
+
+### 💎 Features
+
+* feat: Prevent inline mime type sniffing vulnerabilities (#10087) @arvid-e
+
+### 🐛 Bug Fixes
+
+* fix(editor): Disable bracketMatching to prevent IME composition rendering corruption (#10900) @miya
+
 ## [v7.4.6](https://github.com/growilabs/compare/v7.4.5...v7.4.6) - 2026-03-10
 ## [v7.4.6](https://github.com/growilabs/compare/v7.4.5...v7.4.6) - 2026-03-10
 
 
 ### 🐛 Bug Fixes
 ### 🐛 Bug Fixes

+ 5 - 5
apps/app/.claude/skills/vendor-styles-components/SKILL.md

@@ -23,15 +23,15 @@ Centralizing all vendor CSS in `_app` would degrade FCP for pages that don't nee
 ### Components Track (component-specific CSS)
 ### Components Track (component-specific CSS)
 
 
 - **For**: CSS needed only by specific components
 - **For**: CSS needed only by specific components
-- **Mechanism**: Vite precompiles `*.vendor-styles.ts` entry points into `*.vendor-styles.prebuilt.js` using `?inline` CSS import suffix
+- **Mechanism**: Vite precompiles `*.vendor-styles.ts` entry points into `*.vendor-styles.prebuilt.ts` using `?inline` CSS import suffix
 - **Output**: Pure JS modules (no CSS imports) — Turbopack sees them as regular JS
 - **Output**: Pure JS modules (no CSS imports) — Turbopack sees them as regular JS
 
 
 ## How It Works
 ## How It Works
 
 
 1. **Entry point** (`ComponentName.vendor-styles.ts`): imports CSS via Vite `?inline` suffix, which inlines CSS as a string
 1. **Entry point** (`ComponentName.vendor-styles.ts`): imports CSS via Vite `?inline` suffix, which inlines CSS as a string
 2. **Runtime injection**: the entry point creates a `<style>` tag and appends CSS to `document.head`
 2. **Runtime injection**: the entry point creates a `<style>` tag and appends CSS to `document.head`
-3. **Vite prebuild** (`pre:styles-components` Turborepo task): compiles entry points into `*.vendor-styles.prebuilt.js`
-4. **Component import**: imports the `.prebuilt.js` file instead of raw CSS
+3. **Vite prebuild** (`pre:styles-components` Turborepo task): compiles entry points into `*.vendor-styles.prebuilt.ts`
+4. **Component import**: imports the `.prebuilt.ts` file instead of raw CSS
 
 
 ### Entry Point Template
 ### Entry Point Template
 
 
@@ -96,7 +96,7 @@ When vendor CSS references external assets (e.g., KaTeX `@font-face` with `url(f
 - The `moveAssetsToPublic` plugin (in `vite.vendor-styles-components.ts`) relocates them to `public/static/fonts/`
 - The `moveAssetsToPublic` plugin (in `vite.vendor-styles-components.ts`) relocates them to `public/static/fonts/`
 - URL references in prebuilt JS are rewritten from `/assets/` to `/static/fonts/`
 - URL references in prebuilt JS are rewritten from `/assets/` to `/static/fonts/`
 - Fonts are served by the existing `express.static(crowi.publicDir)` middleware
 - Fonts are served by the existing `express.static(crowi.publicDir)` middleware
-- Both `public/static/fonts/` and `src/**/*.vendor-styles.prebuilt.js` are git-ignored
+- Both `public/static/fonts/` and `src/**/*.vendor-styles.prebuilt.ts` are git-ignored
 
 
 ## Build Pipeline Integration
 ## Build Pipeline Integration
 
 
@@ -106,7 +106,7 @@ turbo.json tasks:
   dev:pre:styles-components  →  dev (dependency)
   dev:pre:styles-components  →  dev (dependency)
 
 
 Inputs:  vite.vendor-styles-components.ts, src/**/*.vendor-styles.ts, package.json
 Inputs:  vite.vendor-styles-components.ts, src/**/*.vendor-styles.ts, package.json
-Outputs: src/**/*.vendor-styles.prebuilt.js, public/static/fonts/**
+Outputs: src/**/*.vendor-styles.prebuilt.ts, public/static/fonts/**
 ```
 ```
 
 
 ## Important Caveats
 ## Important Caveats

+ 1 - 1
apps/app/.gitignore

@@ -16,7 +16,7 @@ next.config.js
 /public/static/styles
 /public/static/styles
 /public/uploads
 /public/uploads
 /src/styles/prebuilt
 /src/styles/prebuilt
-/src/**/*.vendor-styles.prebuilt.js
+/src/**/*.vendor-styles.prebuilt.*
 /tmp/
 /tmp/
 
 
 # cache
 # cache

+ 1 - 1
apps/app/docker/README.md

@@ -10,7 +10,7 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 ------------------------------------------------
 
 
-* [`7.4.6`, `7.4`, `7`, `latest` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.4.6/apps/app/docker/Dockerfile)
+* [`7.4.7`, `7.4`, `7`, `latest` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.4.7/apps/app/docker/Dockerfile)
 * [`7.3.0`, `7.3` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.3.0/apps/app/docker/Dockerfile)
 * [`7.3.0`, `7.3` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.3.0/apps/app/docker/Dockerfile)
 * [`7.2.0`, `7.2` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.2.0/apps/app/docker/Dockerfile)
 * [`7.2.0`, `7.2` (Dockerfile)](https://github.com/growilabs/growi/blob/v7.2.0/apps/app/docker/Dockerfile)
 
 

+ 4 - 7
apps/app/package.json

@@ -58,6 +58,7 @@
   "// comments for dependencies": {
   "// comments for dependencies": {
     "@aws-skd/*": "fix version above 3.186.0 that is required by mongodb@4.16.0",
     "@aws-skd/*": "fix version above 3.186.0 that is required by mongodb@4.16.0",
     "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM.",
     "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM.",
+    "bootstrap": "v5.3.3 has a bug. refs: https://github.com/twbs/bootstrap/issues/39798",
     "escape-string-regexp": "5.0.0 or above exports only ESM",
     "escape-string-regexp": "5.0.0 or above exports only ESM",
     "next-themes": "0.3.0 causes a type error: https://github.com/pacocoursey/next-themes/issues/122",
     "next-themes": "0.3.0 causes a type error: https://github.com/pacocoursey/next-themes/issues/122",
     "string-width": "5.0.0 or above exports only ESM."
     "string-width": "5.0.0 or above exports only ESM."
@@ -197,10 +198,11 @@
     "mongoose-gridfs": "^1.3.0",
     "mongoose-gridfs": "^1.3.0",
     "mongoose-paginate-v2": "^1.3.9",
     "mongoose-paginate-v2": "^1.3.9",
     "mongoose-unique-validator": "^2.0.3",
     "mongoose-unique-validator": "^2.0.3",
+    "motion": "^12.35.0",
     "multer": "~1.4.0",
     "multer": "~1.4.0",
     "multer-autoreap": "^1.0.3",
     "multer-autoreap": "^1.0.3",
     "mustache": "^4.2.0",
     "mustache": "^4.2.0",
-    "next": "^16.0.0",
+    "next": "^16.2.1",
     "next-dynamic-loading-props": "^0.1.1",
     "next-dynamic-loading-props": "^0.1.1",
     "next-i18next": "^15.3.1",
     "next-i18next": "^15.3.1",
     "next-themes": "^0.2.1",
     "next-themes": "^0.2.1",
@@ -240,7 +242,6 @@
     "react-input-autosize": "^3.0.0",
     "react-input-autosize": "^3.0.0",
     "react-markdown": "^9.0.1",
     "react-markdown": "^9.0.1",
     "react-multiline-clamp": "^2.0.0",
     "react-multiline-clamp": "^2.0.0",
-    "react-scroll": "^1.8.7",
     "react-stickynode": "^4.1.1",
     "react-stickynode": "^4.1.1",
     "react-syntax-highlighter": "^16.1.0",
     "react-syntax-highlighter": "^16.1.0",
     "react-toastify": "^9.1.3",
     "react-toastify": "^9.1.3",
@@ -295,10 +296,8 @@
     "zod": "^3.24.2"
     "zod": "^3.24.2"
   },
   },
   "// comments for defDependencies": {
   "// comments for defDependencies": {
-    "bootstrap": "v5.3.3 has a bug. refs: https://github.com/twbs/bootstrap/issues/39798",
     "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
     "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
-    "handsontable": "v7.0.0 or above is no loger MIT lisence.",
-    "mongodb": "mongoose which is used requires mongo@4.16.0."
+    "handsontable": "v7.0.0 or above is no loger MIT lisence."
   },
   },
   "devDependencies": {
   "devDependencies": {
     "@apidevtools/swagger-parser": "^10.1.1",
     "@apidevtools/swagger-parser": "^10.1.1",
@@ -321,7 +320,6 @@
     "@types/react": "^18.2.14",
     "@types/react": "^18.2.14",
     "@types/react-dom": "^18.2.6",
     "@types/react-dom": "^18.2.6",
     "@types/react-input-autosize": "^2.2.4",
     "@types/react-input-autosize": "^2.2.4",
-    "@types/react-scroll": "^1.8.4",
     "@types/react-stickynode": "^4.0.3",
     "@types/react-stickynode": "^4.0.3",
     "@types/supertest": "^6.0.3",
     "@types/supertest": "^6.0.3",
     "@types/testing-library__dom": "^7.5.0",
     "@types/testing-library__dom": "^7.5.0",
@@ -355,7 +353,6 @@
     "supertest": "^7.1.4",
     "supertest": "^7.1.4",
     "swagger2openapi": "^7.0.8",
     "swagger2openapi": "^7.0.8",
     "tinykeys": "^3.0.0",
     "tinykeys": "^3.0.0",
-    "typescript": "~5.0.4",
     "unist-util-is": "^6.0.0",
     "unist-util-is": "^6.0.0",
     "unist-util-visit-parents": "^6.0.0"
     "unist-util-visit-parents": "^6.0.0"
   }
   }

+ 28 - 28
apps/app/src/client/components/ContentLinkButtons.tsx

@@ -1,7 +1,8 @@
 import React, { type JSX } from 'react';
 import React, { type JSX } from 'react';
 import { type IUserHasId, USER_STATUS } from '@growi/core';
 import { type IUserHasId, USER_STATUS } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { Link as ScrollLink } from 'react-scroll';
+
+import { scrollToElement } from '~/client/util/smooth-scroll';
 
 
 import {
 import {
   BOOKMARKS_LIST_ID,
   BOOKMARKS_LIST_ID,
@@ -12,15 +13,14 @@ import {
 const BookMarkLinkButton = React.memo(() => {
 const BookMarkLinkButton = React.memo(() => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   return (
   return (
-    <ScrollLink to={BOOKMARKS_LIST_ID} offset={-120}>
-      <button
-        type="button"
-        className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
-      >
-        <span className="material-symbols-outlined p-0 me-2">bookmark</span>
-        <span>{t('user_home_page.bookmarks')}</span>
-      </button>
-    </ScrollLink>
+    <button
+      type="button"
+      className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
+      onClick={() => scrollToElement(BOOKMARKS_LIST_ID, { offset: -120 })}
+    >
+      <span className="material-symbols-outlined p-0 me-2">bookmark</span>
+      <span>{t('user_home_page.bookmarks')}</span>
+    </button>
   );
   );
 });
 });
 
 
@@ -29,15 +29,16 @@ BookMarkLinkButton.displayName = 'BookMarkLinkButton';
 const RecentlyCreatedLinkButton = React.memo(() => {
 const RecentlyCreatedLinkButton = React.memo(() => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   return (
   return (
-    <ScrollLink to={RECENTLY_CREATED_LIST_ID} offset={-120}>
-      <button
-        type="button"
-        className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
-      >
-        <span className="growi-custom-icons mx-2 ">recently_created</span>
-        <span>{t('user_home_page.recently_created')}</span>
-      </button>
-    </ScrollLink>
+    <button
+      type="button"
+      className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
+      onClick={() =>
+        scrollToElement(RECENTLY_CREATED_LIST_ID, { offset: -120 })
+      }
+    >
+      <span className="growi-custom-icons mx-2 ">recently_created</span>
+      <span>{t('user_home_page.recently_created')}</span>
+    </button>
   );
   );
 });
 });
 
 
@@ -46,15 +47,14 @@ RecentlyCreatedLinkButton.displayName = 'RecentlyCreatedLinkButton';
 const RecentActivityLinkButton = React.memo(() => {
 const RecentActivityLinkButton = React.memo(() => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   return (
   return (
-    <ScrollLink to={RECENT_ACTIVITY_LIST_ID} offset={-120}>
-      <button
-        type="button"
-        className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
-      >
-        <span className="material-symbols-outlined mx-1">update</span>
-        <span>{t('user_home_page.recent_activity')}</span>
-      </button>
-    </ScrollLink>
+    <button
+      type="button"
+      className="btn btn-sm btn-outline-neutral-secondary rounded-pill d-flex align-items-center w-100 px-3"
+      onClick={() => scrollToElement(RECENT_ACTIVITY_LIST_ID, { offset: -120 })}
+    >
+      <span className="material-symbols-outlined mx-1">update</span>
+      <span>{t('user_home_page.recent_activity')}</span>
+    </button>
   );
   );
 });
 });
 
 

+ 4 - 4
apps/app/src/client/components/PageSideContents/PageSideContents.tsx

@@ -1,12 +1,12 @@
-import React, { type JSX, Suspense, useCallback, useRef } from 'react';
+import { type JSX, Suspense, useCallback, useRef } from 'react';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import type { IPagePopulatedToShowRevision } from '@growi/core';
 import type { IPagePopulatedToShowRevision } from '@growi/core';
 import { isIPageInfoForOperation } from '@growi/core/dist/interfaces';
 import { isIPageInfoForOperation } from '@growi/core/dist/interfaces';
 import { pagePathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils } from '@growi/core/dist/utils';
 import { useAtomValue } from 'jotai';
 import { useAtomValue } from 'jotai';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { scroller } from 'react-scroll';
 
 
+import { scrollToElement } from '~/client/util/smooth-scroll';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { showPageSideAuthorsAtom } from '~/states/server-configurations';
 import { showPageSideAuthorsAtom } from '~/states/server-configurations';
 import { useDescendantsPageListModalActions } from '~/states/ui/modal/descendants-page-list';
 import { useDescendantsPageListModalActions } from '~/states/ui/modal/descendants-page-list';
@@ -158,9 +158,9 @@ export const PageSideContents = (props: PageSideContentsProps): JSX.Element => {
                   : undefined
                   : undefined
               }
               }
               onClick={() =>
               onClick={() =>
-                scroller.scrollTo('comments-container', {
-                  smooth: false,
+                scrollToElement('comments-container', {
                   offset: -120,
                   offset: -120,
+                  duration: 0.3,
                 })
                 })
               }
               }
             />
             />

+ 1 - 1
apps/app/src/client/components/StaffCredit/StaffCredit.module.scss

@@ -63,6 +63,6 @@
   }
   }
 
 
   :global(.staff-credit-content) {
   :global(.staff-credit-content) {
-    padding-bottom: 40vh;
+    padding-bottom: 35vh !important;
   }
   }
 }
 }

+ 64 - 14
apps/app/src/client/components/StaffCredit/StaffCredit.tsx

@@ -1,6 +1,13 @@
-import React, { type JSX, useCallback, useState } from 'react';
+import React, {
+  type JSX,
+  useCallback,
+  useEffect,
+  useRef,
+  useState,
+} from 'react';
 import localFont from 'next/font/local';
 import localFont from 'next/font/local';
-import { animateScroll } from 'react-scroll';
+import type { AnimationPlaybackControls } from 'motion';
+import { animate } from 'motion';
 import { Modal, ModalBody } from 'reactstrap';
 import { Modal, ModalBody } from 'reactstrap';
 
 
 import { useSWRxStaffs } from '~/stores/staff';
 import { useSWRxStaffs } from '~/stores/staff';
@@ -10,6 +17,9 @@ import styles from './StaffCredit.module.scss';
 
 
 const _logger = loggerFactory('growi:components:StaffCredit');
 const _logger = loggerFactory('growi:components:StaffCredit');
 
 
+const SCROLL_DELAY = 200; // ms
+const SCROLL_SPEED = 300; // pixels per second
+
 // define fonts
 // define fonts
 const pressStart2P = localFont({
 const pressStart2P = localFont({
   src: '../../../../resource/fonts/PressStart2P-latin.woff2',
   src: '../../../../resource/fonts/PressStart2P-latin.woff2',
@@ -27,6 +37,41 @@ const StaffCredit = (props: Props): JSX.Element => {
   const { data: contributors } = useSWRxStaffs();
   const { data: contributors } = useSWRxStaffs();
 
 
   const [isScrolling, setScrolling] = useState(false);
   const [isScrolling, setScrolling] = useState(false);
+  const animationRef = useRef<AnimationPlaybackControls | null>(null);
+
+  const stopAutoScroll = useCallback(() => {
+    animationRef.current?.stop();
+    animationRef.current = null;
+    setScrolling(false);
+  }, []);
+
+  // Stop auto-scroll on wheel or scrollbar interaction
+  useEffect(() => {
+    if (!isScrolling) return;
+
+    const modalBody = document.getElementById('modalBody');
+    if (modalBody == null) return;
+
+    const handleWheel = () => {
+      stopAutoScroll();
+    };
+
+    const handlePointerDown = (event: PointerEvent) => {
+      const scrollbarStart =
+        modalBody.getBoundingClientRect().left + modalBody.clientWidth;
+      if (event.clientX >= scrollbarStart) {
+        stopAutoScroll();
+      }
+    };
+
+    modalBody.addEventListener('wheel', handleWheel, { passive: true });
+    modalBody.addEventListener('pointerdown', handlePointerDown);
+
+    return () => {
+      modalBody.removeEventListener('wheel', handleWheel);
+      modalBody.removeEventListener('pointerdown', handlePointerDown);
+    };
+  }, [isScrolling, stopAutoScroll]);
 
 
   const closeHandler = useCallback(() => {
   const closeHandler = useCallback(() => {
     if (onClosed != null) {
     if (onClosed != null) {
@@ -36,11 +81,11 @@ const StaffCredit = (props: Props): JSX.Element => {
 
 
   const contentsClickedHandler = useCallback(() => {
   const contentsClickedHandler = useCallback(() => {
     if (isScrolling) {
     if (isScrolling) {
-      setScrolling(false);
+      stopAutoScroll();
     } else {
     } else {
       closeHandler();
       closeHandler();
     }
     }
-  }, [closeHandler, isScrolling]);
+  }, [closeHandler, isScrolling, stopAutoScroll]);
 
 
   const renderMembers = useCallback((memberGroup, keyPrefix) => {
   const renderMembers = useCallback((memberGroup, keyPrefix) => {
     // construct members elements
     // construct members elements
@@ -113,19 +158,24 @@ const StaffCredit = (props: Props): JSX.Element => {
   }, [contentsClickedHandler, contributors, renderMembers]);
   }, [contentsClickedHandler, contributors, renderMembers]);
 
 
   const openedHandler = useCallback(() => {
   const openedHandler = useCallback(() => {
-    // init
-    animateScroll.scrollTo(0, { containerId: 'modalBody', duration: 0 });
+    const container = document.getElementById('modalBody');
+    if (container == null) return;
 
 
+    container.scrollTop = 0;
     setScrolling(true);
     setScrolling(true);
 
 
-    // start scrolling
-    animateScroll.scrollToBottom({
-      containerId: 'modalBody',
-      smooth: 'linear',
-      delay: 200,
-      duration: (scrollDistanceInPx: number) => {
-        const scrollSpeed = 200;
-        return (scrollDistanceInPx / scrollSpeed) * 1000;
+    const maxScroll = container.scrollHeight - container.clientHeight;
+
+    animationRef.current = animate(0, maxScroll, {
+      duration: maxScroll / SCROLL_SPEED,
+      ease: 'linear',
+      delay: SCROLL_DELAY / 1000,
+      onUpdate: (v) => {
+        container.scrollTop = v;
+      },
+      onComplete: () => {
+        animationRef.current = null;
+        setScrolling(false);
       },
       },
     });
     });
   }, []);
   }, []);

+ 38 - 0
apps/app/src/client/util/smooth-scroll.ts

@@ -0,0 +1,38 @@
+import { animate } from 'motion';
+
+type ScrollToElementOptions = {
+  offset?: number;
+  duration?: number;
+};
+
+/**
+ * Smooth scroll to an element by ID
+ */
+export const scrollToElement = (
+  id: string,
+  { offset = 0, duration = 0.5 }: ScrollToElementOptions = {},
+): void => {
+  const el = document.getElementById(id);
+  if (el == null) return;
+  const target = el.getBoundingClientRect().top + window.scrollY + offset;
+  animate(window.scrollY, target, {
+    duration,
+    onUpdate: (v) => window.scrollTo(0, v),
+  });
+};
+
+/**
+ * Smooth scroll within a container by a relative distance
+ */
+export const scrollWithinContainer = (
+  container: HTMLElement,
+  distance: number,
+  duration = 0.2,
+): void => {
+  animate(container.scrollTop, container.scrollTop + distance, {
+    duration,
+    onUpdate: (v) => {
+      container.scrollTop = v;
+    },
+  });
+};

+ 2 - 5
apps/app/src/features/search/client/components/SearchPage/SearchResultContent.tsx

@@ -4,7 +4,6 @@ import dynamic from 'next/dynamic';
 import type { IPageToDeleteWithMeta, IPageToRenameWithMeta } from '@growi/core';
 import type { IPageToDeleteWithMeta, IPageToRenameWithMeta } from '@growi/core';
 import { getIdStringForRef } from '@growi/core';
 import { getIdStringForRef } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { animateScroll } from 'react-scroll';
 import { DropdownItem } from 'reactstrap';
 import { DropdownItem } from 'reactstrap';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 
 
@@ -14,6 +13,7 @@ import type {
 } from '~/client/components/Common/Dropdown/PageItemControl';
 } from '~/client/components/Common/Dropdown/PageItemControl';
 import type { RevisionLoaderProps } from '~/client/components/Page/RevisionLoader';
 import type { RevisionLoaderProps } from '~/client/components/Page/RevisionLoader';
 import { exportAsMarkdown } from '~/client/services/page-operation';
 import { exportAsMarkdown } from '~/client/services/page-operation';
+import { scrollWithinContainer } from '~/client/util/smooth-scroll';
 import { toastSuccess } from '~/client/util/toastr';
 import { toastSuccess } from '~/client/util/toastr';
 import { PagePathNav } from '~/components/Common/PagePathNav';
 import { PagePathNav } from '~/components/Common/PagePathNav';
 import type { IPageWithSearchMeta } from '~/interfaces/search';
 import type { IPageWithSearchMeta } from '~/interfaces/search';
@@ -112,10 +112,7 @@ const scrollToFirstHighlightedKeyword = (scrollElement: HTMLElement): void => {
     toElem.getBoundingClientRect().top -
     toElem.getBoundingClientRect().top -
     scrollElement.getBoundingClientRect().top -
     scrollElement.getBoundingClientRect().top -
     SCROLL_OFFSET_TOP;
     SCROLL_OFFSET_TOP;
-  animateScroll.scrollMore(distance, {
-    containerId: scrollElement.id,
-    duration: 200,
-  });
+  scrollWithinContainer(scrollElement, distance);
 };
 };
 const scrollToFirstHighlightedKeywordDebounced = debounce(
 const scrollToFirstHighlightedKeywordDebounced = debounce(
   500,
   500,

+ 1 - 1
apps/app/src/server/service/config-manager/config-definition.ts

@@ -1062,7 +1062,7 @@ export const CONFIG_DEFINITIONS = {
     defaultValue: false,
     defaultValue: false,
   }),
   }),
   'customize:isEnabledMarp': defineConfig<boolean>({
   'customize:isEnabledMarp': defineConfig<boolean>({
-    defaultValue: false,
+    defaultValue: true,
   }),
   }),
   'customize:isSidebarCollapsedMode': defineConfig<boolean>({
   'customize:isSidebarCollapsedMode': defineConfig<boolean>({
     defaultValue: false,
     defaultValue: false,

+ 1 - 4
apps/app/tsconfig.json

@@ -27,10 +27,7 @@
     ]
     ]
   },
   },
   "include": ["next-env.d.ts", "config", "src"],
   "include": ["next-env.d.ts", "config", "src"],
-  "exclude": [
-    "src/**/*.vendor-styles.ts",
-    "src/**/*.vendor-styles.prebuilt.js"
-  ],
+  "exclude": ["src/**/*.vendor-styles.*"],
   "ts-node": {
   "ts-node": {
     "transpileOnly": true,
     "transpileOnly": true,
     "swc": true,
     "swc": true,

+ 4 - 4
apps/app/turbo.json

@@ -15,7 +15,7 @@
     },
     },
     "pre:styles-components": {
     "pre:styles-components": {
       "dependsOn": ["^build"],
       "dependsOn": ["^build"],
-      "outputs": ["src/**/*.vendor-styles.prebuilt.js"],
+      "outputs": ["src/**/*.vendor-styles.prebuilt.*"],
       "inputs": [
       "inputs": [
         "vite.vendor-styles-components.ts",
         "vite.vendor-styles-components.ts",
         "src/**/*.vendor-styles.ts",
         "src/**/*.vendor-styles.ts",
@@ -56,11 +56,11 @@
     },
     },
     "dev:pre:styles-components": {
     "dev:pre:styles-components": {
       "dependsOn": ["^dev"],
       "dependsOn": ["^dev"],
-      "outputs": ["src/**/*.vendor-styles.prebuilt.js"],
+      "outputs": ["src/**/*.vendor-styles.prebuilt.*"],
       "inputs": [
       "inputs": [
         "vite.vendor-styles-components.ts",
         "vite.vendor-styles-components.ts",
-        "src/**/*.vendor-styles.ts",
-        "!src/**/*.vendor-styles.prebuilt.js",
+        "src/**/*.vendor-styles.*",
+        "!src/**/*.vendor-styles.prebuilt.*",
         "package.json"
         "package.json"
       ],
       ],
       "outputLogs": "new-only"
       "outputLogs": "new-only"

+ 4 - 4
apps/app/vite.vendor-styles-components.ts

@@ -18,7 +18,7 @@ const entries = fs
   );
   );
 
 
 // Move emitted font assets from src/assets/ to public/static/fonts/
 // Move emitted font assets from src/assets/ to public/static/fonts/
-// and rewrite URL references in prebuilt JS files
+// and rewrite URL references in prebuilt TS files
 function moveAssetsToPublic(): Plugin {
 function moveAssetsToPublic(): Plugin {
   return {
   return {
     name: 'move-assets-to-public',
     name: 'move-assets-to-public',
@@ -34,8 +34,8 @@ function moveAssetsToPublic(): Plugin {
       }
       }
       fs.rmdirSync(srcDir);
       fs.rmdirSync(srcDir);
 
 
-      // Rewrite /assets/ -> /static/fonts/ and prepend // @ts-nocheck in prebuilt JS files
-      const prebuiltFiles = fs.globSync('src/**/*.vendor-styles.prebuilt.js', {
+      // Rewrite /assets/ -> /static/fonts/ and prepend // @ts-nocheck in prebuilt TS files
+      const prebuiltFiles = fs.globSync('src/**/*.vendor-styles.prebuilt.ts', {
         cwd: __dirname,
         cwd: __dirname,
       });
       });
       for (const file of prebuiltFiles) {
       for (const file of prebuiltFiles) {
@@ -63,7 +63,7 @@ export default defineConfig({
       input: entries,
       input: entries,
       output: {
       output: {
         format: 'es',
         format: 'es',
-        entryFileNames: '[name].js',
+        entryFileNames: '[name].ts',
       },
       },
     },
     },
   },
   },

+ 1 - 1
biome.json

@@ -15,12 +15,12 @@
       "!**/.devcontainer",
       "!**/.devcontainer",
       "!**/.stylelintrc.json",
       "!**/.stylelintrc.json",
       "!**/package.json",
       "!**/package.json",
+      "!**/*.vendor-styles.prebuilt.*",
       "!.turbo",
       "!.turbo",
       "!.vscode",
       "!.vscode",
       "!.claude",
       "!.claude",
       "!tsconfig.base.json",
       "!tsconfig.base.json",
       "!apps/app/src/styles/prebuilt",
       "!apps/app/src/styles/prebuilt",
-      "!apps/app/src/**/*.vendor-styles.prebuilt.js",
       "!apps/app/next-env.d.ts",
       "!apps/app/next-env.d.ts",
       "!apps/app/tmp",
       "!apps/app/tmp",
       "!apps/pdf-converter/specs",
       "!apps/pdf-converter/specs",

+ 1 - 1
package.json

@@ -83,7 +83,7 @@
     "tsconfig-paths": "^4.2.0",
     "tsconfig-paths": "^4.2.0",
     "tspc": "^1.1.2",
     "tspc": "^1.1.2",
     "turbo": "^2.1.3",
     "turbo": "^2.1.3",
-    "typescript": "~5.0.0",
+    "typescript": "^5.9.3",
     "typescript-transform-paths": "^3.4.7",
     "typescript-transform-paths": "^3.4.7",
     "vite": "^5.4.21",
     "vite": "^5.4.21",
     "vite-plugin-dts": "^3.9.1",
     "vite-plugin-dts": "^3.9.1",

+ 9 - 0
packages/editor/src/client/services/use-codemirror-editor/use-codemirror-editor.ts

@@ -1,4 +1,5 @@
 import { useMemo } from 'react';
 import { useMemo } from 'react';
+import { bracketMatching } from '@codemirror/language';
 import type { EditorState } from '@codemirror/state';
 import type { EditorState } from '@codemirror/state';
 import type { EditorView } from '@codemirror/view';
 import type { EditorView } from '@codemirror/view';
 import { type UseCodeMirror, useCodeMirror } from '@uiw/react-codemirror';
 import { type UseCodeMirror, useCodeMirror } from '@uiw/react-codemirror';
@@ -60,7 +61,15 @@ export const useCodeMirrorEditor = (
             dropCursor: false,
             dropCursor: false,
             highlightActiveLine: false,
             highlightActiveLine: false,
             highlightActiveLineGutter: false,
             highlightActiveLineGutter: false,
+            // Disable default bracketMatching and re-add with afterCursor: false
+            // to prevent a rendering bug where text visually disappears after IME
+            // composition inside brackets on non-Safari browsers (e.g. Chrome).
+            // When afterCursor is true (default), bracketMatching decorates brackets
+            // ahead of the cursor immediately after composition ends, which corrupts
+            // CodeMirror's DOM reconciliation.
+            bracketMatching: false,
           },
           },
+          extensions: [bracketMatching({ afterCursor: false })],
           // ------- End -------
           // ------- End -------
         },
         },
         props ?? {},
         props ?? {},

+ 2 - 0
packages/presentation/.gitignore

@@ -1 +1,3 @@
 /dist
 /dist
+# Generated at build time by scripts/extract-marpit-css.ts
+*.vendor-styles.prebuilt.*

+ 1 - 1
packages/presentation/package.json

@@ -29,8 +29,8 @@
     }
     }
   },
   },
   "scripts": {
   "scripts": {
+    "generate:marpit-base-css": "node scripts/extract-marpit-css.ts",
     "build": "vite build",
     "build": "vite build",
-    "build:vendor-styles": "vite build --config vite.vendor-styles.ts",
     "clean": "shx rm -rf dist",
     "clean": "shx rm -rf dist",
     "dev": "vite build --mode dev",
     "dev": "vite build --mode dev",
     "watch": "pnpm run dev -w --emptyOutDir=false",
     "watch": "pnpm run dev -w --emptyOutDir=false",

+ 70 - 0
packages/presentation/scripts/extract-marpit-css.ts

@@ -0,0 +1,70 @@
+/**
+ * Build-time script to extract Marp base CSS constants.
+ *
+ * Replicates the Marp configuration from growi-marpit.ts and generates
+ * pre-extracted CSS so that GrowiSlides can apply Marp container styling
+ * without a runtime dependency on @marp-team/marp-core or @marp-team/marpit.
+ *
+ * Regenerate with: node scripts/extract-marpit-css.ts
+ */
+
+import { writeFileSync } from 'node:fs';
+import { dirname, resolve } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import type { MarpOptions } from '@marp-team/marp-core';
+import { Marp } from '@marp-team/marp-core';
+import { Element } from '@marp-team/marpit';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+const MARP_CONTAINER_CLASS_NAME = 'marpit';
+
+const marpitOption: MarpOptions = {
+  container: [
+    new Element('div', { class: `slides ${MARP_CONTAINER_CLASS_NAME}` }),
+  ],
+  inlineSVG: true,
+  emoji: undefined,
+  html: false,
+  math: false,
+};
+
+// Slide mode: with shadow/rounded slide containers
+const slideMarpitOption: MarpOptions = { ...marpitOption };
+slideMarpitOption.slideContainer = [
+  new Element('section', { class: 'shadow rounded m-2' }),
+];
+const slideMarpit = new Marp(slideMarpitOption);
+
+// Presentation mode: minimal slide containers
+const presentationMarpitOption: MarpOptions = { ...marpitOption };
+presentationMarpitOption.slideContainer = [
+  new Element('section', { class: 'm-2' }),
+];
+const presentationMarpit = new Marp(presentationMarpitOption);
+
+const { css: slideCss } = slideMarpit.render('');
+const { css: presentationCss } = presentationMarpit.render('');
+
+if (!slideCss || !presentationCss) {
+  // biome-ignore lint/suspicious/noConsole: Allows console output for script
+  console.error('ERROR: CSS extraction produced empty output');
+  process.exit(1);
+}
+
+const output = `// Generated file — do not edit manually
+// Regenerate with: node scripts/extract-marpit-css.ts
+
+export const SLIDE_MARPIT_CSS = ${JSON.stringify(slideCss)};
+
+export const PRESENTATION_MARPIT_CSS = ${JSON.stringify(presentationCss)};
+`;
+
+const outPath = resolve(
+  __dirname,
+  '../src/client/consts/marpit-base-css.vendor-styles.prebuilt.ts',
+);
+writeFileSync(outPath, output, 'utf-8');
+
+// biome-ignore lint/suspicious/noConsole: Allows console output for script
+console.log(`Extracted Marp base CSS to ${outPath}`);

+ 5 - 7
packages/presentation/src/client/components/GrowiSlides.tsx

@@ -2,12 +2,11 @@ import type { JSX } from 'react';
 import Head from 'next/head';
 import Head from 'next/head';
 import ReactMarkdown from 'react-markdown';
 import ReactMarkdown from 'react-markdown';
 
 
-import type { PresentationOptions } from '../consts';
+import { MARP_CONTAINER_CLASS_NAME, type PresentationOptions } from '../consts';
 import {
 import {
-  MARP_CONTAINER_CLASS_NAME,
-  presentationMarpit,
-  slideMarpit,
-} from '../services/growi-marpit';
+  PRESENTATION_MARPIT_CSS,
+  SLIDE_MARPIT_CSS,
+} from '../consts/marpit-base-css.vendor-styles.prebuilt';
 import * as extractSections from '../services/renderer/extract-sections';
 import * as extractSections from '../services/renderer/extract-sections';
 import {
 import {
   PresentationRichSlideSection,
   PresentationRichSlideSection,
@@ -43,8 +42,7 @@ export const GrowiSlides = (props: Props): JSX.Element => {
     ? PresentationRichSlideSection
     ? PresentationRichSlideSection
     : RichSlideSection;
     : RichSlideSection;
 
 
-  const marpit = presentation ? presentationMarpit : slideMarpit;
-  const { css } = marpit.render('');
+  const css = presentation ? PRESENTATION_MARPIT_CSS : SLIDE_MARPIT_CSS;
   return (
   return (
     <>
     <>
       <Head>
       <Head>

+ 17 - 3
packages/presentation/src/client/components/Slides.tsx

@@ -1,11 +1,14 @@
-import type { JSX } from 'react';
+import { type JSX, lazy, Suspense } from 'react';
 
 
 import type { PresentationOptions } from '../consts';
 import type { PresentationOptions } from '../consts';
 import { GrowiSlides } from './GrowiSlides';
 import { GrowiSlides } from './GrowiSlides';
-import { MarpSlides } from './MarpSlides';
 
 
 import styles from './Slides.module.scss';
 import styles from './Slides.module.scss';
 
 
+const MarpSlides = lazy(() =>
+  import('./MarpSlides').then((mod) => ({ default: mod.MarpSlides })),
+);
+
 export type SlidesProps = {
 export type SlidesProps = {
   options: PresentationOptions;
   options: PresentationOptions;
   children?: string;
   children?: string;
@@ -19,7 +22,18 @@ export const Slides = (props: SlidesProps): JSX.Element => {
   return (
   return (
     <div className={`${styles['slides-styles']}`}>
     <div className={`${styles['slides-styles']}`}>
       {hasMarpFlag ? (
       {hasMarpFlag ? (
-        <MarpSlides presentation={presentation}>{children}</MarpSlides>
+        <Suspense
+          fallback={
+            <div className="d-flex flex-column justify-content-center align-items-center py-5">
+              <output className="spinner-border text-secondary">
+                <span className="visually-hidden">Loading...</span>
+              </output>
+              <span className="mt-3 small text-secondary">Loading Marp...</span>
+            </div>
+          }
+        >
+          <MarpSlides presentation={presentation}>{children}</MarpSlides>
+        </Suspense>
       ) : (
       ) : (
         <GrowiSlides options={options} presentation={presentation}>
         <GrowiSlides options={options} presentation={presentation}>
           {children}
           {children}

+ 2 - 0
packages/presentation/src/client/consts/index.ts

@@ -1,6 +1,8 @@
 import type { Options as ReactMarkdownOptions } from 'react-markdown';
 import type { Options as ReactMarkdownOptions } from 'react-markdown';
 import type { Options as RevealOptions } from 'reveal.js';
 import type { Options as RevealOptions } from 'reveal.js';
 
 
+export const MARP_CONTAINER_CLASS_NAME = 'marpit';
+
 export type PresentationOptions = {
 export type PresentationOptions = {
   rendererOptions: ReactMarkdownOptions;
   rendererOptions: ReactMarkdownOptions;
   revealOptions?: RevealOptions;
   revealOptions?: RevealOptions;

+ 4 - 1
packages/presentation/src/client/services/growi-marpit.ts

@@ -2,7 +2,9 @@ import type { MarpOptions } from '@marp-team/marp-core';
 import { Marp } from '@marp-team/marp-core';
 import { Marp } from '@marp-team/marp-core';
 import { Element } from '@marp-team/marpit';
 import { Element } from '@marp-team/marpit';
 
 
-export const MARP_CONTAINER_CLASS_NAME = 'marpit';
+import { MARP_CONTAINER_CLASS_NAME } from '../consts';
+
+export { MARP_CONTAINER_CLASS_NAME };
 
 
 // Add data-line to Marp slide.
 // Add data-line to Marp slide.
 // https://github.com/marp-team/marp-vscode/blob/d9af184ed12b65bb28c0f328e250955d548ac1d1/src/plugins/line-number.ts
 // https://github.com/marp-team/marp-vscode/blob/d9af184ed12b65bb28c0f328e250955d548ac1d1/src/plugins/line-number.ts
@@ -12,6 +14,7 @@ const lineNumber = (md) => {
     md.renderer.rules;
     md.renderer.rules;
 
 
   // Enable line sync by per slides
   // Enable line sync by per slides
+  // biome-ignore lint/nursery/useMaxParams: Allows 5 parameters for marpit renderer rules
   md.renderer.rules.marpit_slide_containers_open = (tks, i, opts, env, slf) => {
   md.renderer.rules.marpit_slide_containers_open = (tks, i, opts, env, slf) => {
     const slide = tks.slice(i + 1).find((t) => t.type === 'marpit_slide_open');
     const slide = tks.slice(i + 1).find((t) => t.type === 'marpit_slide_open');
 
 

+ 27 - 0
packages/presentation/turbo.json

@@ -0,0 +1,27 @@
+{
+  "$schema": "https://turbo.build/schema.json",
+  "extends": ["//"],
+  "tasks": {
+    "generate:marpit-base-css": {
+      "inputs": [
+        "scripts/extract-marpit-css.ts",
+        "package.json"
+      ],
+      "outputs": ["src/client/consts/marpit-base-css.vendor-styles.prebuilt.ts"],
+      "outputLogs": "new-only"
+    },
+    "build": {
+      "dependsOn": ["generate:marpit-base-css"],
+      "outputs": ["dist/**"],
+      "outputLogs": "new-only"
+    },
+    "dev": {
+      "dependsOn": ["generate:marpit-base-css"],
+      "outputs": ["dist/**"],
+      "outputLogs": "new-only"
+    },
+    "lint": {
+      "dependsOn": ["generate:marpit-base-css"]
+    }
+  }
+}

+ 201 - 194
pnpm-lock.yaml

@@ -35,7 +35,7 @@ importers:
         version: 1.58.2
         version: 1.58.2
       '@swc-node/register':
       '@swc-node/register':
         specifier: ^1.10.9
         specifier: ^1.10.9
-        version: 1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.0.4)
+        version: 1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.9.3)
       '@swc/core':
       '@swc/core':
         specifier: ^1.5.25
         specifier: ^1.5.25
         version: 1.10.7(@swc/helpers@0.5.18)
         version: 1.10.7(@swc/helpers@0.5.18)
@@ -119,19 +119,19 @@ importers:
         version: 0.3.4
         version: 0.3.4
       stylelint:
       stylelint:
         specifier: ^16.5.0
         specifier: ^16.5.0
-        version: 16.5.0(typescript@5.0.4)
+        version: 16.5.0(typescript@5.9.3)
       stylelint-config-recess-order:
       stylelint-config-recess-order:
         specifier: ^5.0.1
         specifier: ^5.0.1
-        version: 5.0.1(stylelint@16.5.0(typescript@5.0.4))
+        version: 5.0.1(stylelint@16.5.0(typescript@5.9.3))
       stylelint-config-recommended-scss:
       stylelint-config-recommended-scss:
         specifier: ^14.0.0
         specifier: ^14.0.0
-        version: 14.0.0(postcss@8.5.6)(stylelint@16.5.0(typescript@5.0.4))
+        version: 14.0.0(postcss@8.5.6)(stylelint@16.5.0(typescript@5.9.3))
       ts-deepmerge:
       ts-deepmerge:
         specifier: ^6.2.0
         specifier: ^6.2.0
         version: 6.2.0
         version: 6.2.0
       ts-node:
       ts-node:
         specifier: ^10.9.2
         specifier: ^10.9.2
-        version: 10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.18))(@types/node@20.19.17)(typescript@5.0.4)
+        version: 10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.18))(@types/node@20.19.17)(typescript@5.9.3)
       ts-patch:
       ts-patch:
         specifier: ^3.2.0
         specifier: ^3.2.0
         version: 3.2.0
         version: 3.2.0
@@ -145,26 +145,26 @@ importers:
         specifier: ^2.1.3
         specifier: ^2.1.3
         version: 2.1.3
         version: 2.1.3
       typescript:
       typescript:
-        specifier: ~5.0.0
-        version: 5.0.4
+        specifier: ^5.9.3
+        version: 5.9.3
       typescript-transform-paths:
       typescript-transform-paths:
         specifier: ^3.4.7
         specifier: ^3.4.7
-        version: 3.4.7(typescript@5.0.4)
+        version: 3.4.7(typescript@5.9.3)
       vite:
       vite:
         specifier: ^5.4.21
         specifier: ^5.4.21
         version: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
         version: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
       vite-plugin-dts:
       vite-plugin-dts:
         specifier: ^3.9.1
         specifier: ^3.9.1
-        version: 3.9.1(@types/node@20.19.17)(rollup@4.39.0)(typescript@5.0.4)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0))
+        version: 3.9.1(@types/node@20.19.17)(rollup@4.39.0)(typescript@5.9.3)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0))
       vite-tsconfig-paths:
       vite-tsconfig-paths:
         specifier: ^5.0.1
         specifier: ^5.0.1
-        version: 5.0.1(typescript@5.0.4)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0))
+        version: 5.0.1(typescript@5.9.3)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0))
       vitest:
       vitest:
         specifier: ^2.1.1
         specifier: ^2.1.1
         version: 2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0)
         version: 2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0)
       vitest-mock-extended:
       vitest-mock-extended:
         specifier: ^2.0.2
         specifier: ^2.0.2
-        version: 2.0.2(typescript@5.0.4)(vitest@2.1.1)
+        version: 2.0.2(typescript@5.9.3)(vitest@2.1.1)
 
 
   apps/app:
   apps/app:
     dependencies:
     dependencies:
@@ -359,7 +359,7 @@ importers:
         version: 3.9.1
         version: 3.9.1
       babel-plugin-superjson-next:
       babel-plugin-superjson-next:
         specifier: ^0.4.2
         specifier: ^0.4.2
-        version: 0.4.5(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(superjson@2.2.2)
+        version: 0.4.5(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(superjson@2.2.2)
       body-parser:
       body-parser:
         specifier: ^1.20.3
         specifier: ^1.20.3
         version: 1.20.3
         version: 1.20.3
@@ -570,6 +570,9 @@ importers:
       mongoose-unique-validator:
       mongoose-unique-validator:
         specifier: ^2.0.3
         specifier: ^2.0.3
         version: 2.0.3(mongoose@6.13.8(@aws-sdk/client-sso-oidc@3.600.0))
         version: 2.0.3(mongoose@6.13.8(@aws-sdk/client-sso-oidc@3.600.0))
+      motion:
+        specifier: ^12.35.0
+        version: 12.38.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       multer:
       multer:
         specifier: ~1.4.0
         specifier: ~1.4.0
         version: 1.4.4
         version: 1.4.4
@@ -580,17 +583,17 @@ importers:
         specifier: ^4.2.0
         specifier: ^4.2.0
         version: 4.2.0
         version: 4.2.0
       next:
       next:
-        specifier: ^16.0.0
-        version: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+        specifier: ^16.2.1
+        version: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       next-dynamic-loading-props:
       next-dynamic-loading-props:
         specifier: ^0.1.1
         specifier: ^0.1.1
         version: 0.1.1(react@18.2.0)
         version: 0.1.1(react@18.2.0)
       next-i18next:
       next-i18next:
         specifier: ^15.3.1
         specifier: ^15.3.1
-        version: 15.3.1(i18next@23.16.5)(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
+        version: 15.3.1(i18next@23.16.5)(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
       next-themes:
       next-themes:
         specifier: ^0.2.1
         specifier: ^0.2.1
-        version: 0.2.1(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+        version: 0.2.1(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       nocache:
       nocache:
         specifier: ^4.0.0
         specifier: ^4.0.0
         version: 4.0.0
         version: 4.0.0
@@ -699,9 +702,6 @@ importers:
       react-multiline-clamp:
       react-multiline-clamp:
         specifier: ^2.0.0
         specifier: ^2.0.0
         version: 2.0.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
         version: 2.0.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
-      react-scroll:
-        specifier: ^1.8.7
-        version: 1.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       react-stickynode:
       react-stickynode:
         specifier: ^4.1.1
         specifier: ^4.1.1
         version: 4.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
         version: 4.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -919,9 +919,6 @@ importers:
       '@types/react-input-autosize':
       '@types/react-input-autosize':
         specifier: ^2.2.4
         specifier: ^2.2.4
         version: 2.2.4
         version: 2.2.4
-      '@types/react-scroll':
-        specifier: ^1.8.4
-        version: 1.8.10
       '@types/react-stickynode':
       '@types/react-stickynode':
         specifier: ^4.0.3
         specifier: ^4.0.3
         version: 4.0.3
         version: 4.0.3
@@ -999,7 +996,7 @@ importers:
         version: 1.10.0
         version: 1.10.0
       openapi-typescript:
       openapi-typescript:
         specifier: ^7.8.0
         specifier: ^7.8.0
-        version: 7.8.0(typescript@5.0.4)
+        version: 7.8.0(typescript@5.9.3)
       rehype-rewrite:
       rehype-rewrite:
         specifier: ^4.0.2
         specifier: ^4.0.2
         version: 4.0.2
         version: 4.0.2
@@ -1021,9 +1018,6 @@ importers:
       tinykeys:
       tinykeys:
         specifier: ^3.0.0
         specifier: ^3.0.0
         version: 3.0.0
         version: 3.0.0
-      typescript:
-        specifier: ~5.0.4
-        version: 5.0.4
       unist-util-is:
       unist-util-is:
         specifier: ^6.0.0
         specifier: ^6.0.0
         version: 6.0.0
         version: 6.0.0
@@ -1089,17 +1083,17 @@ importers:
         version: 4.21.0
         version: 4.21.0
       puppeteer:
       puppeteer:
         specifier: ^23.1.1
         specifier: ^23.1.1
-        version: 23.6.1(typescript@5.4.2)
+        version: 23.6.1(typescript@5.9.3)
       puppeteer-cluster:
       puppeteer-cluster:
         specifier: ^0.24.0
         specifier: ^0.24.0
-        version: 0.24.0(puppeteer@23.6.1(typescript@5.4.2))
+        version: 0.24.0(puppeteer@23.6.1(typescript@5.9.3))
       tslib:
       tslib:
         specifier: ^2.8.0
         specifier: ^2.8.0
         version: 2.8.1
         version: 2.8.1
     devDependencies:
     devDependencies:
       '@swc-node/register':
       '@swc-node/register':
         specifier: ^1.10.9
         specifier: ^1.10.9
-        version: 1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.4.2)
+        version: 1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.9.3)
       '@swc/core':
       '@swc/core':
         specifier: ^1.9.2
         specifier: ^1.9.2
         version: 1.10.7(@swc/helpers@0.5.18)
         version: 1.10.7(@swc/helpers@0.5.18)
@@ -1455,7 +1449,7 @@ importers:
     devDependencies:
     devDependencies:
       orval:
       orval:
         specifier: '=7.2.0'
         specifier: '=7.2.0'
-        version: 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)(typescript@5.4.2)
+        version: 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)(typescript@5.9.3)
 
 
   packages/pluginkit:
   packages/pluginkit:
     dependencies:
     dependencies:
@@ -1474,7 +1468,7 @@ importers:
     dependencies:
     dependencies:
       next:
       next:
         specifier: ^16
         specifier: ^16
-        version: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+        version: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       react:
       react:
         specifier: ^18.2.0
         specifier: ^18.2.0
         version: 18.2.0
         version: 18.2.0
@@ -1752,7 +1746,7 @@ importers:
         version: 7.2.4
         version: 7.2.4
       type-coverage:
       type-coverage:
         specifier: ^2.0.0
         specifier: ^2.0.0
-        version: 2.29.0(typescript@5.4.2)
+        version: 2.29.0(typescript@5.9.3)
       unist-util-remove-position:
       unist-util-remove-position:
         specifier: ^5.0.0
         specifier: ^5.0.0
         version: 5.0.0
         version: 5.0.0
@@ -1785,7 +1779,7 @@ importers:
         version: 6.13.8(@aws-sdk/client-sso-oidc@3.600.0)
         version: 6.13.8(@aws-sdk/client-sso-oidc@3.600.0)
       next:
       next:
         specifier: ^16
         specifier: ^16
-        version: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+        version: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       react:
       react:
         specifier: ^18.2.0
         specifier: ^18.2.0
         version: 18.2.0
         version: 18.2.0
@@ -1889,7 +1883,7 @@ importers:
     dependencies:
     dependencies:
       next:
       next:
         specifier: ^16
         specifier: ^16
-        version: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+        version: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       react:
       react:
         specifier: ^18.2.0
         specifier: ^18.2.0
         version: 18.2.0
         version: 18.2.0
@@ -3568,57 +3562,57 @@ packages:
   '@napi-rs/wasm-runtime@0.2.4':
   '@napi-rs/wasm-runtime@0.2.4':
     resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
     resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
 
 
-  '@next/env@16.1.6':
-    resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==}
+  '@next/env@16.2.1':
+    resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==}
 
 
-  '@next/swc-darwin-arm64@16.1.6':
-    resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==}
+  '@next/swc-darwin-arm64@16.2.1':
+    resolution: {integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [arm64]
     cpu: [arm64]
     os: [darwin]
     os: [darwin]
 
 
-  '@next/swc-darwin-x64@16.1.6':
-    resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==}
+  '@next/swc-darwin-x64@16.2.1':
+    resolution: {integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [x64]
     cpu: [x64]
     os: [darwin]
     os: [darwin]
 
 
-  '@next/swc-linux-arm64-gnu@16.1.6':
-    resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==}
+  '@next/swc-linux-arm64-gnu@16.2.1':
+    resolution: {integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [arm64]
     cpu: [arm64]
     os: [linux]
     os: [linux]
     libc: [glibc]
     libc: [glibc]
 
 
-  '@next/swc-linux-arm64-musl@16.1.6':
-    resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==}
+  '@next/swc-linux-arm64-musl@16.2.1':
+    resolution: {integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [arm64]
     cpu: [arm64]
     os: [linux]
     os: [linux]
     libc: [musl]
     libc: [musl]
 
 
-  '@next/swc-linux-x64-gnu@16.1.6':
-    resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==}
+  '@next/swc-linux-x64-gnu@16.2.1':
+    resolution: {integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [x64]
     cpu: [x64]
     os: [linux]
     os: [linux]
     libc: [glibc]
     libc: [glibc]
 
 
-  '@next/swc-linux-x64-musl@16.1.6':
-    resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==}
+  '@next/swc-linux-x64-musl@16.2.1':
+    resolution: {integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [x64]
     cpu: [x64]
     os: [linux]
     os: [linux]
     libc: [musl]
     libc: [musl]
 
 
-  '@next/swc-win32-arm64-msvc@16.1.6':
-    resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==}
+  '@next/swc-win32-arm64-msvc@16.2.1':
+    resolution: {integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [arm64]
     cpu: [arm64]
     os: [win32]
     os: [win32]
 
 
-  '@next/swc-win32-x64-msvc@16.1.6':
-    resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==}
+  '@next/swc-win32-x64-msvc@16.2.1':
+    resolution: {integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
     cpu: [x64]
     cpu: [x64]
     os: [win32]
     os: [win32]
@@ -5688,9 +5682,6 @@ packages:
   '@types/react-input-autosize@2.2.4':
   '@types/react-input-autosize@2.2.4':
     resolution: {integrity: sha512-7O028jRZHZo3mj63h3HSvB0WpvPXNWN86sajHTi0+CtjA4Ym+DFzO9RzrSbfFURe5ZWsq6P72xk7MInI6aGWJA==}
     resolution: {integrity: sha512-7O028jRZHZo3mj63h3HSvB0WpvPXNWN86sajHTi0+CtjA4Ym+DFzO9RzrSbfFURe5ZWsq6P72xk7MInI6aGWJA==}
 
 
-  '@types/react-scroll@1.8.10':
-    resolution: {integrity: sha512-RD4Z7grbdNGOKwKnUBKar6zNxqaW3n8m9QSrfvljW+gmkj1GArb8AFBomVr6xMOgHPD3v1uV3BrIf01py57daQ==}
-
   '@types/react-stickynode@4.0.3':
   '@types/react-stickynode@4.0.3':
     resolution: {integrity: sha512-K7YkwdhXQE4YVxIVweix4nkpdG4onm/dcnKK+qCj0vgUrNiKng+09zOfjF5AlOcC1HQkg5yxVLwp/0AzT84R0w==}
     resolution: {integrity: sha512-K7YkwdhXQE4YVxIVweix4nkpdG4onm/dcnKK+qCj0vgUrNiKng+09zOfjF5AlOcC1HQkg5yxVLwp/0AzT84R0w==}
 
 
@@ -6399,8 +6390,9 @@ packages:
     resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
     resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
     engines: {node: '>=6.0.0'}
     engines: {node: '>=6.0.0'}
 
 
-  baseline-browser-mapping@2.9.10:
-    resolution: {integrity: sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==}
+  baseline-browser-mapping@2.10.10:
+    resolution: {integrity: sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==}
+    engines: {node: '>=6.0.0'}
     hasBin: true
     hasBin: true
 
 
   basic-auth@2.0.1:
   basic-auth@2.0.1:
@@ -8662,6 +8654,20 @@ packages:
     resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
     resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
     engines: {node: '>= 0.6'}
     engines: {node: '>= 0.6'}
 
 
+  framer-motion@12.38.0:
+    resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==}
+    peerDependencies:
+      '@emotion/is-prop-valid': '*'
+      react: ^18.0.0 || ^19.0.0
+      react-dom: ^18.0.0 || ^19.0.0
+    peerDependenciesMeta:
+      '@emotion/is-prop-valid':
+        optional: true
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
   fresh@0.5.2:
   fresh@0.5.2:
     resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
     resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
     engines: {node: '>= 0.6'}
     engines: {node: '>= 0.6'}
@@ -9832,7 +9838,6 @@ packages:
     resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==}
     resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==}
     engines: {node: '>=8.17.0'}
     engines: {node: '>=8.17.0'}
     hasBin: true
     hasBin: true
-    bundledDependencies: []
 
 
   jsonfile@3.0.1:
   jsonfile@3.0.1:
     resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==}
     resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==}
@@ -10882,6 +10887,26 @@ packages:
     resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==}
     resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==}
     engines: {node: '>= 0.8.0'}
     engines: {node: '>= 0.8.0'}
 
 
+  motion-dom@12.38.0:
+    resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==}
+
+  motion-utils@12.36.0:
+    resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==}
+
+  motion@12.38.0:
+    resolution: {integrity: sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==}
+    peerDependencies:
+      '@emotion/is-prop-valid': '*'
+      react: ^18.0.0 || ^19.0.0
+      react-dom: ^18.0.0 || ^19.0.0
+    peerDependenciesMeta:
+      '@emotion/is-prop-valid':
+        optional: true
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
   move-file@2.1.0:
   move-file@2.1.0:
     resolution: {integrity: sha512-i9qLW6gqboJ5Ht8bauZi7KlTnQ3QFpBCvMvFfEcHADKgHGeJ9BZMO7SFCTwHPV9Qa0du9DYY1Yx3oqlGt30nXA==}
     resolution: {integrity: sha512-i9qLW6gqboJ5Ht8bauZi7KlTnQ3QFpBCvMvFfEcHADKgHGeJ9BZMO7SFCTwHPV9Qa0du9DYY1Yx3oqlGt30nXA==}
     engines: {node: '>=10.17'}
     engines: {node: '>=10.17'}
@@ -11006,8 +11031,8 @@ packages:
       react: '*'
       react: '*'
       react-dom: '*'
       react-dom: '*'
 
 
-  next@16.1.6:
-    resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==}
+  next@16.2.1:
+    resolution: {integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==}
     engines: {node: '>=20.9.0'}
     engines: {node: '>=20.9.0'}
     hasBin: true
     hasBin: true
     peerDependencies:
     peerDependencies:
@@ -12123,12 +12148,6 @@ packages:
       react: ^16.14.0 || ^17.0.0 || ^18.0.0
       react: ^16.14.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
 
 
-  react-scroll@1.9.0:
-    resolution: {integrity: sha512-mamNcaX9Ng+JeSbBu97nWwRhYvL2oba+xR2GxvyXsbDeGP+gkYIKZ+aDMMj/n20TbV9SCWm/H7nyuNTSiXA6yA==}
-    peerDependencies:
-      react: ^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0
-
   react-stickynode@4.1.1:
   react-stickynode@4.1.1:
     resolution: {integrity: sha512-+Xp3xantrxbFjqNiSbpvsZwCqZYiPq0njKTA+QsIZdmEHih1H/lOV9/LpS37d+v92iSydJJTZMeRaENWeqGeIA==}
     resolution: {integrity: sha512-+Xp3xantrxbFjqNiSbpvsZwCqZYiPq0njKTA+QsIZdmEHih1H/lOV9/LpS37d+v92iSydJJTZMeRaENWeqGeIA==}
     engines: {node: '>=16', npm: '>=8.4'}
     engines: {node: '>=16', npm: '>=8.4'}
@@ -13788,16 +13807,16 @@ packages:
     peerDependencies:
     peerDependencies:
       typescript: '>=3.6.5'
       typescript: '>=3.6.5'
 
 
-  typescript@5.0.4:
-    resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
-    engines: {node: '>=12.20'}
-    hasBin: true
-
   typescript@5.4.2:
   typescript@5.4.2:
     resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
     resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==}
     engines: {node: '>=14.17'}
     engines: {node: '>=14.17'}
     hasBin: true
     hasBin: true
 
 
+  typescript@5.9.3:
+    resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
   typical@7.3.0:
   typical@7.3.0:
     resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==}
     resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==}
     engines: {node: '>=12.17'}
     engines: {node: '>=12.17'}
@@ -17320,30 +17339,30 @@ snapshots:
       '@tybys/wasm-util': 0.9.0
       '@tybys/wasm-util': 0.9.0
     optional: true
     optional: true
 
 
-  '@next/env@16.1.6': {}
+  '@next/env@16.2.1': {}
 
 
-  '@next/swc-darwin-arm64@16.1.6':
+  '@next/swc-darwin-arm64@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-darwin-x64@16.1.6':
+  '@next/swc-darwin-x64@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-linux-arm64-gnu@16.1.6':
+  '@next/swc-linux-arm64-gnu@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-linux-arm64-musl@16.1.6':
+  '@next/swc-linux-arm64-musl@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-linux-x64-gnu@16.1.6':
+  '@next/swc-linux-x64-gnu@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-linux-x64-musl@16.1.6':
+  '@next/swc-linux-x64-musl@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-win32-arm64-msvc@16.1.6':
+  '@next/swc-win32-arm64-msvc@16.2.1':
     optional: true
     optional: true
 
 
-  '@next/swc-win32-x64-msvc@16.1.6':
+  '@next/swc-win32-x64-msvc@16.2.1':
     optional: true
     optional: true
 
 
   '@noble/hashes@1.8.0': {}
   '@noble/hashes@1.8.0': {}
@@ -19318,22 +19337,7 @@ snapshots:
       '@swc/core': 1.10.7(@swc/helpers@0.5.18)
       '@swc/core': 1.10.7(@swc/helpers@0.5.18)
       '@swc/types': 0.1.17
       '@swc/types': 0.1.17
 
 
-  '@swc-node/register@1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.0.4)':
-    dependencies:
-      '@swc-node/core': 1.13.3(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)
-      '@swc-node/sourcemap-support': 0.5.1
-      '@swc/core': 1.10.7(@swc/helpers@0.5.18)
-      colorette: 2.0.20
-      debug: 4.4.3(supports-color@10.0.0)
-      oxc-resolver: 1.12.0
-      pirates: 4.0.6
-      tslib: 2.8.1
-      typescript: 5.0.4
-    transitivePeerDependencies:
-      - '@swc/types'
-      - supports-color
-
-  '@swc-node/register@1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.4.2)':
+  '@swc-node/register@1.10.9(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)(typescript@5.9.3)':
     dependencies:
     dependencies:
       '@swc-node/core': 1.13.3(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)
       '@swc-node/core': 1.13.3(@swc/core@1.10.7(@swc/helpers@0.5.18))(@swc/types@0.1.17)
       '@swc-node/sourcemap-support': 0.5.1
       '@swc-node/sourcemap-support': 0.5.1
@@ -19343,7 +19347,7 @@ snapshots:
       oxc-resolver: 1.12.0
       oxc-resolver: 1.12.0
       pirates: 4.0.6
       pirates: 4.0.6
       tslib: 2.8.1
       tslib: 2.8.1
-      typescript: 5.4.2
+      typescript: 5.9.3
     transitivePeerDependencies:
     transitivePeerDependencies:
       - '@swc/types'
       - '@swc/types'
       - supports-color
       - supports-color
@@ -20328,10 +20332,6 @@ snapshots:
     dependencies:
     dependencies:
       '@types/react': 18.3.3
       '@types/react': 18.3.3
 
 
-  '@types/react-scroll@1.8.10':
-    dependencies:
-      '@types/react': 18.3.3
-
   '@types/react-stickynode@4.0.3':
   '@types/react-stickynode@4.0.3':
     dependencies:
     dependencies:
       '@types/react': 18.3.3
       '@types/react': 18.3.3
@@ -20621,7 +20621,7 @@ snapshots:
       '@vue/compiler-core': 3.5.6
       '@vue/compiler-core': 3.5.6
       '@vue/shared': 3.5.6
       '@vue/shared': 3.5.6
 
 
-  '@vue/language-core@1.8.27(typescript@5.0.4)':
+  '@vue/language-core@1.8.27(typescript@5.9.3)':
     dependencies:
     dependencies:
       '@volar/language-core': 1.11.1
       '@volar/language-core': 1.11.1
       '@volar/source-map': 1.11.1
       '@volar/source-map': 1.11.1
@@ -20633,7 +20633,7 @@ snapshots:
       path-browserify: 1.0.1
       path-browserify: 1.0.1
       vue-template-compiler: 2.7.16
       vue-template-compiler: 2.7.16
     optionalDependencies:
     optionalDependencies:
-      typescript: 5.0.4
+      typescript: 5.9.3
 
 
   '@vue/shared@3.5.6': {}
   '@vue/shared@3.5.6': {}
 
 
@@ -21125,12 +21125,12 @@ snapshots:
       schema-utils: 2.7.1
       schema-utils: 2.7.1
       webpack: 5.92.1(@swc/core@1.10.7(@swc/helpers@0.5.18))
       webpack: 5.92.1(@swc/core@1.10.7(@swc/helpers@0.5.18))
 
 
-  babel-plugin-superjson-next@0.4.5(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(superjson@2.2.2):
+  babel-plugin-superjson-next@0.4.5(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(superjson@2.2.2):
     dependencies:
     dependencies:
       '@babel/helper-module-imports': 7.24.6
       '@babel/helper-module-imports': 7.24.6
       '@babel/types': 7.25.6
       '@babel/types': 7.25.6
       hoist-non-react-statics: 3.3.2
       hoist-non-react-statics: 3.3.2
-      next: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+      next: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       superjson: 2.2.2
       superjson: 2.2.2
 
 
   backoff@2.5.0:
   backoff@2.5.0:
@@ -21174,7 +21174,7 @@ snapshots:
 
 
   base64url@3.0.1: {}
   base64url@3.0.1: {}
 
 
-  baseline-browser-mapping@2.9.10: {}
+  baseline-browser-mapping@2.10.10: {}
 
 
   basic-auth@2.0.1:
   basic-auth@2.0.1:
     dependencies:
     dependencies:
@@ -21341,7 +21341,7 @@ snapshots:
 
 
   browserslist@4.28.1:
   browserslist@4.28.1:
     dependencies:
     dependencies:
-      baseline-browser-mapping: 2.9.10
+      baseline-browser-mapping: 2.10.10
       caniuse-lite: 1.0.30001761
       caniuse-lite: 1.0.30001761
       electron-to-chromium: 1.5.267
       electron-to-chromium: 1.5.267
       node-releases: 2.0.27
       node-releases: 2.0.27
@@ -22046,23 +22046,14 @@ snapshots:
     dependencies:
     dependencies:
       layout-base: 2.0.1
       layout-base: 2.0.1
 
 
-  cosmiconfig@9.0.0(typescript@5.0.4):
-    dependencies:
-      env-paths: 2.2.1
-      import-fresh: 3.3.1
-      js-yaml: 4.1.1
-      parse-json: 5.2.0
-    optionalDependencies:
-      typescript: 5.0.4
-
-  cosmiconfig@9.0.0(typescript@5.4.2):
+  cosmiconfig@9.0.0(typescript@5.9.3):
     dependencies:
     dependencies:
       env-paths: 2.2.1
       env-paths: 2.2.1
       import-fresh: 3.3.1
       import-fresh: 3.3.1
       js-yaml: 4.1.1
       js-yaml: 4.1.1
       parse-json: 5.2.0
       parse-json: 5.2.0
     optionalDependencies:
     optionalDependencies:
-      typescript: 5.4.2
+      typescript: 5.9.3
 
 
   cp-file@9.1.0:
   cp-file@9.1.0:
     dependencies:
     dependencies:
@@ -23460,6 +23451,15 @@ snapshots:
 
 
   forwarded@0.2.0: {}
   forwarded@0.2.0: {}
 
 
+  framer-motion@12.38.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
+    dependencies:
+      motion-dom: 12.38.0
+      motion-utils: 12.36.0
+      tslib: 2.8.1
+    optionalDependencies:
+      react: 18.2.0
+      react-dom: 18.2.0(react@18.2.0)
+
   fresh@0.5.2: {}
   fresh@0.5.2: {}
 
 
   fs-constants@1.0.0: {}
   fs-constants@1.0.0: {}
@@ -26157,6 +26157,20 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
+  motion-dom@12.38.0:
+    dependencies:
+      motion-utils: 12.36.0
+
+  motion-utils@12.36.0: {}
+
+  motion@12.38.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
+    dependencies:
+      framer-motion: 12.38.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+      tslib: 2.8.1
+    optionalDependencies:
+      react: 18.2.0
+      react-dom: 18.2.0(react@18.2.0)
+
   move-file@2.1.0:
   move-file@2.1.0:
     dependencies:
     dependencies:
       path-exists: 4.0.0
       path-exists: 4.0.0
@@ -26264,7 +26278,7 @@ snapshots:
     dependencies:
     dependencies:
       react: 18.2.0
       react: 18.2.0
 
 
-  next-i18next@15.3.1(i18next@23.16.5)(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
+  next-i18next@15.3.1(i18next@23.16.5)(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-i18next@15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
     dependencies:
     dependencies:
       '@babel/runtime': 7.28.6
       '@babel/runtime': 7.28.6
       '@types/hoist-non-react-statics': 3.3.5
       '@types/hoist-non-react-statics': 3.3.5
@@ -26272,35 +26286,35 @@ snapshots:
       hoist-non-react-statics: 3.3.2
       hoist-non-react-statics: 3.3.2
       i18next: 23.16.5
       i18next: 23.16.5
       i18next-fs-backend: 2.3.2
       i18next-fs-backend: 2.3.2
-      next: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+      next: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       react: 18.2.0
       react: 18.2.0
       react-i18next: 15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
       react-i18next: 15.1.1(i18next@23.16.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
 
 
-  next-themes@0.2.1(next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
+  next-themes@0.2.1(next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6))(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
     dependencies:
     dependencies:
-      next: 16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
+      next: 16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6)
       react: 18.2.0
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       react-dom: 18.2.0(react@18.2.0)
 
 
-  next@16.1.6(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6):
+  next@16.2.1(@babel/core@7.24.6)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(sass@1.77.6):
     dependencies:
     dependencies:
-      '@next/env': 16.1.6
+      '@next/env': 16.2.1
       '@swc/helpers': 0.5.15
       '@swc/helpers': 0.5.15
-      baseline-browser-mapping: 2.9.10
+      baseline-browser-mapping: 2.10.10
       caniuse-lite: 1.0.30001761
       caniuse-lite: 1.0.30001761
       postcss: 8.4.31
       postcss: 8.4.31
       react: 18.2.0
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       react-dom: 18.2.0(react@18.2.0)
       styled-jsx: 5.1.6(@babel/core@7.24.6)(react@18.2.0)
       styled-jsx: 5.1.6(@babel/core@7.24.6)(react@18.2.0)
     optionalDependencies:
     optionalDependencies:
-      '@next/swc-darwin-arm64': 16.1.6
-      '@next/swc-darwin-x64': 16.1.6
-      '@next/swc-linux-arm64-gnu': 16.1.6
-      '@next/swc-linux-arm64-musl': 16.1.6
-      '@next/swc-linux-x64-gnu': 16.1.6
-      '@next/swc-linux-x64-musl': 16.1.6
-      '@next/swc-win32-arm64-msvc': 16.1.6
-      '@next/swc-win32-x64-msvc': 16.1.6
+      '@next/swc-darwin-arm64': 16.2.1
+      '@next/swc-darwin-x64': 16.2.1
+      '@next/swc-linux-arm64-gnu': 16.2.1
+      '@next/swc-linux-arm64-musl': 16.2.1
+      '@next/swc-linux-x64-gnu': 16.2.1
+      '@next/swc-linux-x64-musl': 16.2.1
+      '@next/swc-win32-arm64-msvc': 16.2.1
+      '@next/swc-win32-x64-msvc': 16.2.1
       '@opentelemetry/api': 1.9.0
       '@opentelemetry/api': 1.9.0
       '@playwright/test': 1.58.2
       '@playwright/test': 1.58.2
       sass: 1.77.6
       sass: 1.77.6
@@ -26609,14 +26623,14 @@ snapshots:
 
 
   openapi-types@12.1.3: {}
   openapi-types@12.1.3: {}
 
 
-  openapi-typescript@7.8.0(typescript@5.0.4):
+  openapi-typescript@7.8.0(typescript@5.9.3):
     dependencies:
     dependencies:
       '@redocly/openapi-core': 1.34.5(supports-color@10.0.0)
       '@redocly/openapi-core': 1.34.5(supports-color@10.0.0)
       ansi-colors: 4.1.3
       ansi-colors: 4.1.3
       change-case: 5.4.4
       change-case: 5.4.4
       parse-json: 8.3.0
       parse-json: 8.3.0
       supports-color: 10.0.0
       supports-color: 10.0.0
-      typescript: 5.0.4
+      typescript: 5.9.3
       yargs-parser: 21.1.1
       yargs-parser: 21.1.1
 
 
   openapi3-ts@4.2.2:
   openapi3-ts@4.2.2:
@@ -26655,7 +26669,7 @@ snapshots:
       strip-ansi: 6.0.1
       strip-ansi: 6.0.1
       wcwidth: 1.0.1
       wcwidth: 1.0.1
 
 
-  orval@7.2.0(encoding@0.1.13)(openapi-types@12.1.3)(typescript@5.4.2):
+  orval@7.2.0(encoding@0.1.13)(openapi-types@12.1.3)(typescript@5.9.3):
     dependencies:
     dependencies:
       '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3)
       '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3)
       '@orval/angular': 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)
       '@orval/angular': 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)
@@ -26678,7 +26692,7 @@ snapshots:
       lodash.uniq: 4.5.0
       lodash.uniq: 4.5.0
       openapi3-ts: 4.2.2
       openapi3-ts: 4.2.2
       string-argv: 0.3.2
       string-argv: 0.3.2
-      tsconfck: 2.1.2(typescript@5.4.2)
+      tsconfck: 2.1.2(typescript@5.9.3)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - encoding
       - encoding
       - openapi-types
       - openapi-types
@@ -27219,10 +27233,10 @@ snapshots:
     dependencies:
     dependencies:
       escape-goat: 4.0.0
       escape-goat: 4.0.0
 
 
-  puppeteer-cluster@0.24.0(puppeteer@23.6.1(typescript@5.4.2)):
+  puppeteer-cluster@0.24.0(puppeteer@23.6.1(typescript@5.9.3)):
     dependencies:
     dependencies:
       debug: 4.4.3(supports-color@10.0.0)
       debug: 4.4.3(supports-color@10.0.0)
-      puppeteer: 23.6.1(typescript@5.4.2)
+      puppeteer: 23.6.1(typescript@5.9.3)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -27239,11 +27253,11 @@ snapshots:
       - supports-color
       - supports-color
       - utf-8-validate
       - utf-8-validate
 
 
-  puppeteer@23.6.1(typescript@5.4.2):
+  puppeteer@23.6.1(typescript@5.9.3):
     dependencies:
     dependencies:
       '@puppeteer/browsers': 2.4.0
       '@puppeteer/browsers': 2.4.0
       chromium-bidi: 0.8.0(devtools-protocol@0.0.1354347)
       chromium-bidi: 0.8.0(devtools-protocol@0.0.1354347)
-      cosmiconfig: 9.0.0(typescript@5.4.2)
+      cosmiconfig: 9.0.0(typescript@5.9.3)
       devtools-protocol: 0.0.1354347
       devtools-protocol: 0.0.1354347
       puppeteer-core: 23.6.1
       puppeteer-core: 23.6.1
       typed-query-selector: 2.12.0
       typed-query-selector: 2.12.0
@@ -27535,13 +27549,6 @@ snapshots:
       react: 18.2.0
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       react-dom: 18.2.0(react@18.2.0)
 
 
-  react-scroll@1.9.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
-    dependencies:
-      lodash.throttle: 4.1.1
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-
   react-stickynode@4.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
   react-stickynode@4.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
     dependencies:
     dependencies:
       classnames: 2.5.1
       classnames: 2.5.1
@@ -28912,40 +28919,40 @@ snapshots:
     optionalDependencies:
     optionalDependencies:
       '@babel/core': 7.24.6
       '@babel/core': 7.24.6
 
 
-  stylelint-config-recess-order@5.0.1(stylelint@16.5.0(typescript@5.0.4)):
+  stylelint-config-recess-order@5.0.1(stylelint@16.5.0(typescript@5.9.3)):
     dependencies:
     dependencies:
-      stylelint: 16.5.0(typescript@5.0.4)
-      stylelint-order: 6.0.4(stylelint@16.5.0(typescript@5.0.4))
+      stylelint: 16.5.0(typescript@5.9.3)
+      stylelint-order: 6.0.4(stylelint@16.5.0(typescript@5.9.3))
 
 
-  stylelint-config-recommended-scss@14.0.0(postcss@8.5.6)(stylelint@16.5.0(typescript@5.0.4)):
+  stylelint-config-recommended-scss@14.0.0(postcss@8.5.6)(stylelint@16.5.0(typescript@5.9.3)):
     dependencies:
     dependencies:
       postcss-scss: 4.0.9(postcss@8.5.6)
       postcss-scss: 4.0.9(postcss@8.5.6)
-      stylelint: 16.5.0(typescript@5.0.4)
-      stylelint-config-recommended: 14.0.0(stylelint@16.5.0(typescript@5.0.4))
-      stylelint-scss: 6.3.0(stylelint@16.5.0(typescript@5.0.4))
+      stylelint: 16.5.0(typescript@5.9.3)
+      stylelint-config-recommended: 14.0.0(stylelint@16.5.0(typescript@5.9.3))
+      stylelint-scss: 6.3.0(stylelint@16.5.0(typescript@5.9.3))
     optionalDependencies:
     optionalDependencies:
       postcss: 8.5.6
       postcss: 8.5.6
 
 
-  stylelint-config-recommended@14.0.0(stylelint@16.5.0(typescript@5.0.4)):
+  stylelint-config-recommended@14.0.0(stylelint@16.5.0(typescript@5.9.3)):
     dependencies:
     dependencies:
-      stylelint: 16.5.0(typescript@5.0.4)
+      stylelint: 16.5.0(typescript@5.9.3)
 
 
-  stylelint-order@6.0.4(stylelint@16.5.0(typescript@5.0.4)):
+  stylelint-order@6.0.4(stylelint@16.5.0(typescript@5.9.3)):
     dependencies:
     dependencies:
       postcss: 8.5.6
       postcss: 8.5.6
       postcss-sorting: 8.0.2(postcss@8.5.6)
       postcss-sorting: 8.0.2(postcss@8.5.6)
-      stylelint: 16.5.0(typescript@5.0.4)
+      stylelint: 16.5.0(typescript@5.9.3)
 
 
-  stylelint-scss@6.3.0(stylelint@16.5.0(typescript@5.0.4)):
+  stylelint-scss@6.3.0(stylelint@16.5.0(typescript@5.9.3)):
     dependencies:
     dependencies:
       known-css-properties: 0.30.0
       known-css-properties: 0.30.0
       postcss-media-query-parser: 0.2.3
       postcss-media-query-parser: 0.2.3
       postcss-resolve-nested-selector: 0.1.1
       postcss-resolve-nested-selector: 0.1.1
       postcss-selector-parser: 6.1.0
       postcss-selector-parser: 6.1.0
       postcss-value-parser: 4.2.0
       postcss-value-parser: 4.2.0
-      stylelint: 16.5.0(typescript@5.0.4)
+      stylelint: 16.5.0(typescript@5.9.3)
 
 
-  stylelint@16.5.0(typescript@5.0.4):
+  stylelint@16.5.0(typescript@5.9.3):
     dependencies:
     dependencies:
       '@csstools/css-parser-algorithms': 2.6.3(@csstools/css-tokenizer@2.3.1)
       '@csstools/css-parser-algorithms': 2.6.3(@csstools/css-tokenizer@2.3.1)
       '@csstools/css-tokenizer': 2.3.1
       '@csstools/css-tokenizer': 2.3.1
@@ -28954,7 +28961,7 @@ snapshots:
       '@dual-bundle/import-meta-resolve': 4.1.0
       '@dual-bundle/import-meta-resolve': 4.1.0
       balanced-match: 2.0.0
       balanced-match: 2.0.0
       colord: 2.9.3
       colord: 2.9.3
-      cosmiconfig: 9.0.0(typescript@5.0.4)
+      cosmiconfig: 9.0.0(typescript@5.9.3)
       css-functions-list: 3.2.2
       css-functions-list: 3.2.2
       css-tree: 2.3.1
       css-tree: 2.3.1
       debug: 4.4.3(supports-color@10.0.0)
       debug: 4.4.3(supports-color@10.0.0)
@@ -29378,13 +29385,13 @@ snapshots:
 
 
   ts-deepmerge@6.2.0: {}
   ts-deepmerge@6.2.0: {}
 
 
-  ts-essentials@10.0.2(typescript@5.0.4):
+  ts-essentials@10.0.2(typescript@5.9.3):
     optionalDependencies:
     optionalDependencies:
-      typescript: 5.0.4
+      typescript: 5.9.3
 
 
   ts-interface-checker@0.1.13: {}
   ts-interface-checker@0.1.13: {}
 
 
-  ts-node@10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.18))(@types/node@20.19.17)(typescript@5.0.4):
+  ts-node@10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.18))(@types/node@20.19.17)(typescript@5.9.3):
     dependencies:
     dependencies:
       '@cspotcode/source-map-support': 0.8.1
       '@cspotcode/source-map-support': 0.8.1
       '@tsconfig/node10': 1.0.9
       '@tsconfig/node10': 1.0.9
@@ -29398,7 +29405,7 @@ snapshots:
       create-require: 1.1.1
       create-require: 1.1.1
       diff: 4.0.1
       diff: 4.0.1
       make-error: 1.3.6
       make-error: 1.3.6
-      typescript: 5.0.4
+      typescript: 5.9.3
       v8-compile-cache-lib: 3.0.1
       v8-compile-cache-lib: 3.0.1
       yn: 3.1.1
       yn: 3.1.1
     optionalDependencies:
     optionalDependencies:
@@ -29413,13 +29420,13 @@ snapshots:
       semver: 7.7.4
       semver: 7.7.4
       strip-ansi: 6.0.1
       strip-ansi: 6.0.1
 
 
-  tsconfck@2.1.2(typescript@5.4.2):
+  tsconfck@2.1.2(typescript@5.9.3):
     optionalDependencies:
     optionalDependencies:
-      typescript: 5.4.2
+      typescript: 5.9.3
 
 
-  tsconfck@3.0.3(typescript@5.0.4):
+  tsconfck@3.0.3(typescript@5.9.3):
     optionalDependencies:
     optionalDependencies:
-      typescript: 5.0.4
+      typescript: 5.9.3
 
 
   tsconfig-paths@4.2.0:
   tsconfig-paths@4.2.0:
     dependencies:
     dependencies:
@@ -29443,10 +29450,10 @@ snapshots:
 
 
   tsscmp@1.0.6: {}
   tsscmp@1.0.6: {}
 
 
-  tsutils@3.21.0(typescript@5.4.2):
+  tsutils@3.21.0(typescript@5.9.3):
     dependencies:
     dependencies:
       tslib: 1.14.1
       tslib: 1.14.1
-      typescript: 5.4.2
+      typescript: 5.9.3
 
 
   ttf2eot@3.1.0:
   ttf2eot@3.1.0:
     dependencies:
     dependencies:
@@ -29516,20 +29523,20 @@ snapshots:
     dependencies:
     dependencies:
       prelude-ls: 1.2.1
       prelude-ls: 1.2.1
 
 
-  type-coverage-core@2.28.1(typescript@5.4.2):
+  type-coverage-core@2.28.1(typescript@5.9.3):
     dependencies:
     dependencies:
       fast-glob: 3.3.2
       fast-glob: 3.3.2
       minimatch: 9.0.5
       minimatch: 9.0.5
       normalize-path: 3.0.0
       normalize-path: 3.0.0
       tslib: 2.8.1
       tslib: 2.8.1
-      tsutils: 3.21.0(typescript@5.4.2)
-      typescript: 5.4.2
+      tsutils: 3.21.0(typescript@5.9.3)
+      typescript: 5.9.3
 
 
-  type-coverage@2.29.0(typescript@5.4.2):
+  type-coverage@2.29.0(typescript@5.9.3):
     dependencies:
     dependencies:
       chalk: 4.1.2
       chalk: 4.1.2
       minimist: 1.2.8
       minimist: 1.2.8
-      type-coverage-core: 2.28.1(typescript@5.4.2)
+      type-coverage-core: 2.28.1(typescript@5.9.3)
     transitivePeerDependencies:
     transitivePeerDependencies:
       - typescript
       - typescript
 
 
@@ -29619,15 +29626,15 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
-  typescript-transform-paths@3.4.7(typescript@5.0.4):
+  typescript-transform-paths@3.4.7(typescript@5.9.3):
     dependencies:
     dependencies:
       minimatch: 3.1.2
       minimatch: 3.1.2
-      typescript: 5.0.4
-
-  typescript@5.0.4: {}
+      typescript: 5.9.3
 
 
   typescript@5.4.2: {}
   typescript@5.4.2: {}
 
 
+  typescript@5.9.3: {}
+
   typical@7.3.0: {}
   typical@7.3.0: {}
 
 
   typpy@2.3.11:
   typpy@2.3.11:
@@ -30025,16 +30032,16 @@ snapshots:
       - supports-color
       - supports-color
       - terser
       - terser
 
 
-  vite-plugin-dts@3.9.1(@types/node@20.19.17)(rollup@4.39.0)(typescript@5.0.4)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)):
+  vite-plugin-dts@3.9.1(@types/node@20.19.17)(rollup@4.39.0)(typescript@5.9.3)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)):
     dependencies:
     dependencies:
       '@microsoft/api-extractor': 7.43.0(@types/node@20.19.17)
       '@microsoft/api-extractor': 7.43.0(@types/node@20.19.17)
       '@rollup/pluginutils': 5.2.0(rollup@4.39.0)
       '@rollup/pluginutils': 5.2.0(rollup@4.39.0)
-      '@vue/language-core': 1.8.27(typescript@5.0.4)
+      '@vue/language-core': 1.8.27(typescript@5.9.3)
       debug: 4.4.3(supports-color@10.0.0)
       debug: 4.4.3(supports-color@10.0.0)
       kolorist: 1.8.0
       kolorist: 1.8.0
       magic-string: 0.30.11
       magic-string: 0.30.11
-      typescript: 5.0.4
-      vue-tsc: 1.8.27(typescript@5.0.4)
+      typescript: 5.9.3
+      vue-tsc: 1.8.27(typescript@5.9.3)
     optionalDependencies:
     optionalDependencies:
       vite: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
       vite: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
     transitivePeerDependencies:
     transitivePeerDependencies:
@@ -30042,11 +30049,11 @@ snapshots:
       - rollup
       - rollup
       - supports-color
       - supports-color
 
 
-  vite-tsconfig-paths@5.0.1(typescript@5.0.4)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)):
+  vite-tsconfig-paths@5.0.1(typescript@5.9.3)(vite@5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)):
     dependencies:
     dependencies:
       debug: 4.4.3(supports-color@10.0.0)
       debug: 4.4.3(supports-color@10.0.0)
       globrex: 0.1.2
       globrex: 0.1.2
-      tsconfck: 3.0.3(typescript@5.0.4)
+      tsconfck: 3.0.3(typescript@5.9.3)
     optionalDependencies:
     optionalDependencies:
       vite: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
       vite: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
     transitivePeerDependencies:
     transitivePeerDependencies:
@@ -30064,10 +30071,10 @@ snapshots:
       sass: 1.77.6
       sass: 1.77.6
       terser: 5.46.0
       terser: 5.46.0
 
 
-  vitest-mock-extended@2.0.2(typescript@5.0.4)(vitest@2.1.1):
+  vitest-mock-extended@2.0.2(typescript@5.9.3)(vitest@2.1.1):
     dependencies:
     dependencies:
-      ts-essentials: 10.0.2(typescript@5.0.4)
-      typescript: 5.0.4
+      ts-essentials: 10.0.2(typescript@5.9.3)
+      typescript: 5.9.3
       vitest: 2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0)
       vitest: 2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0)
 
 
   vitest@2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0):
   vitest@2.1.1(@types/node@20.19.17)(@vitest/ui@2.1.1)(happy-dom@15.7.4)(jsdom@26.1.0)(sass@1.77.6)(terser@5.46.0):
@@ -30131,12 +30138,12 @@ snapshots:
       de-indent: 1.0.2
       de-indent: 1.0.2
       he: 1.2.0
       he: 1.2.0
 
 
-  vue-tsc@1.8.27(typescript@5.0.4):
+  vue-tsc@1.8.27(typescript@5.9.3):
     dependencies:
     dependencies:
       '@volar/typescript': 1.11.1
       '@volar/typescript': 1.11.1
-      '@vue/language-core': 1.8.27(typescript@5.0.4)
+      '@vue/language-core': 1.8.27(typescript@5.9.3)
       semver: 7.7.4
       semver: 7.7.4
-      typescript: 5.0.4
+      typescript: 5.9.3
 
 
   w3c-keyname@2.2.8: {}
   w3c-keyname@2.2.8: {}