Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/solid/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
dist
.wrangler
.output
.vercel
.netlify
.vinxi
app.config.timestamp_*.js

# Environment
.env
.env*.local

# dependencies
/node_modules

# IDEs and editors
/.idea
.project
.classpath
*.launch
.settings/

# Temp
gitignore

# System Files
.DS_Store
Thumbs.db
82 changes: 82 additions & 0 deletions packages/solid/.releaserc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* eslint-env node */
const {
createCommitAnalyzer,
createReleaseNotesGenerator,
} = require('../../scripts/semantic-release/strict-breaking-parser.cjs');

/*
* Commit filter: solid declares `superdoc` in dependencies (not
* peerDependencies), so existing consumers with lockfiles won't pick up a
* new core version until solid republishes. Expand commit analysis into
* core paths so semantic-release triggers a solid release on core changes.
*
* When solid migrates `superdoc` to peerDependencies, narrow this to
* packages/solid only. See .github/package-impact-map.md.
*/
require('../../scripts/semantic-release/patch-commit-filter.cjs')([
'packages/solid',
'packages/superdoc',
'packages/super-editor',
'packages/layout-engine',
'packages/word-layout',
'packages/preset-geometry',
'shared',
'pnpm-workspace.yaml',
]);

const branch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_BRANCH;

const branches = [
{ name: 'stable', channel: 'latest' },
{ name: 'main', prerelease: 'next', channel: 'next' },
];

const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease);

// Use AI-powered notes for stable releases, conventional generator for prereleases
const notesPlugin = isPrerelease ? createReleaseNotesGenerator() : ['semantic-release-ai-notes', { style: 'concise' }];

const config = {
branches,
tagFormat: 'solid-v${version}',
plugins: [
createCommitAnalyzer({
// Cap at minor — solid declares superdoc in dependencies, so
// upstream breaking changes don't break solid's own public API.
// Prevents accidental major bumps from superdoc feat!/BREAKING CHANGE commits.
releaseRules: [
{ breaking: true, release: 'minor' },
{ type: 'feat', release: 'minor' },
{ type: 'fix', release: 'patch' },
{ type: 'perf', release: 'patch' },
{ type: 'revert', release: 'patch' },
],
}),
notesPlugin,
['semantic-release-pnpm', { npmPublish: false }],
'../../scripts/publish-solid.cjs',
],
};

if (!isPrerelease) {
config.plugins.push([
'@semantic-release/git',
{
assets: ['package.json'],
message: 'chore(solid): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
]);
}

// Linear integration - labels issues with version on release
config.plugins.push(['semantic-release-linear-app', { teamKeys: ['SD'], addComment: true, packageName: 'solid' }]);

config.plugins.push([
'@semantic-release/github',
{
successComment:
':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/solid** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})',
},
]);

module.exports = config;
101 changes: 101 additions & 0 deletions packages/solid/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# @superdoc-dev/solid

Solid wrapper for SuperDoc.

## Files

| File | Purpose |
|------|---------|
| `src/SuperDocEditor.tsx` | Main component |
| `src/types.ts` | TypeScript types (extracted from superdoc) |
| `src/index.ts` | Public exports |

## Type System

Types are extracted from `superdoc` constructor to avoid duplication:

```typescript
type SuperDocConstructorConfig = ConstructorParameters<typeof SuperDoc>[0];

export type DocumentMode = NonNullable<SuperDocConstructorConfig['documentMode']>;
export type UserRole = NonNullable<SuperDocConstructorConfig['role']>;
export type SuperDocUser = NonNullable<SuperDocConstructorConfig['user']>;
export type SuperDocModules = NonNullable<SuperDocConstructorConfig['modules']>;
export type SuperDocConfig = SuperDocConstructorConfig;
export type SuperDocInstance = InstanceType<typeof SuperDoc>;

// Props = SuperDocConfig (minus internal) + Solid-specific
type InternalProps = 'selector'; // managed by component
type OptionalInReact = 'documentMode'; // defaults to 'editing'

export interface SuperDocEditorProps
extends Omit<SuperDocConfig, InternalProps | OptionalInSolid>,
Partial<Pick<SuperDocConfig, OptionalInSolid>>,
SolidProps {}
```

## Solid-Specific Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `id` | `string` | Custom container ID (auto-generated if not provided) |
| `ref` | `Ref<SuperDocRef>` | Ref to the SuperDoc instance |
| `renderLoading` | `() => JSX.Element` | Loading UI |
| `hideToolbar` | `boolean` | Hide toolbar (default: false) |
| `class` | `string` | Wrapper CSS class |
| `style` | `JSX.CSSProperties` | Wrapper inline styles |

## Fixed-height container embedding

Pass `contained` to scroll inside a fixed-height parent. The wrapper must have a definite height.

```tsx
<div style={{ height: '500px' }}>
<SuperDocEditor
document={file()}
documentMode="viewing"
contained
style={{ height: '100%' }}
/>
</div>
```

## SSR Behavior

- Container divs are always rendered (hidden with `display: none` until initialized)
- No `isClient` state or extra rerender — containers exist from first render
- SuperDoc initializes in `createEffect` (client-side only) and mounts into the existing containers
- `renderLoading()` shown alongside hidden containers until initialization completes

## Ref API

```typescript
let editorRef: SuperDocRef | null = null;

// Access SuperDoc instance
const instance = editorRef?.getInstance();

// Call methods
instance?.setDocumentMode('viewing');
instance?.export({ triggerDownload: true });
instance?.getHTML();
```

## Props That Trigger Rebuild

These props cause the SuperDoc instance to be destroyed and recreated:
- `document` - new document to load
- `user` - user identity changed
- `users` - users list changed
- `modules` - module config changed
- `role` - permission level changed
- `hideToolbar` - toolbar visibility changed

Other props like `documentMode` and callbacks are handled without rebuild.

## Commands

```bash
pnpm --filter @superdoc-dev/solid build
pnpm --filter @superdoc-dev/solid test
```
1 change: 1 addition & 0 deletions packages/solid/CLAUDE.md
Loading
Loading