Server: - New POST /api/lint/check proxies to LanguageTool API (public free endpoint by default; override with LANGUAGETOOL_URL env var for self-hosted instance) - Returns trimmed match list: message, offset, length, replacements, kind (spelling|grammar), ruleId - Disables cosmetic rules that don't suit creative writing - 20-second timeout; falls back with 502 on error Frontend: - LintMark: transient TipTap Mark extension (never saved to DB — stripLintMarks() removes them from getJSON() before onChange fires) - buildTextMap(): walks ProseMirror doc tree, builds parallel plain- text string + position array so LanguageTool char offsets map back to exact PM positions even across paragraphs / headings - clearLintMarks() + runLint(): check → clear old marks → apply new marks in one transaction, no flicker - Click on underlined text → fixed-position popover showing the error message + up to 6 one-click replacement buttons + Ignore / ✕ - Applying a replacement triggers an automatic re-check after 600 ms - lintStatus: idle → checking → done → stale (when user edits) - ABC toolbar button shows count badge when errors found, ✓ when clean - Wavy red underline = spelling, wavy blue = grammar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
27 lines
950 B
JavaScript
27 lines
950 B
JavaScript
import express from 'express'
|
|
import path from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
import authRoutes from './routes/auth.js'
|
|
import storiesRoutes from './routes/stories.js'
|
|
import imagesRoutes from './routes/images.js'
|
|
import adminRoutes from './routes/admin.js'
|
|
import promptsRoutes from './routes/prompts.js'
|
|
import notesRoutes from './routes/notes.js'
|
|
import lintRoutes from './routes/lint.js'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const app = express()
|
|
|
|
app.use(express.json({ limit: '1mb' }))
|
|
app.use('/uploads', express.static(path.join(__dirname, 'uploads')))
|
|
|
|
app.use('/api/auth', authRoutes)
|
|
app.use('/api/stories', storiesRoutes)
|
|
app.use('/api/images', imagesRoutes)
|
|
app.use('/api/admin', adminRoutes)
|
|
app.use('/api/prompts', promptsRoutes)
|
|
app.use('/api/stories/:storyId/notes', notesRoutes)
|
|
app.use('/api/lint', lintRoutes)
|
|
|
|
app.listen(3000, () => console.log('Server ready on :3000'))
|