Migrate to Euro-Office and refresh the editor home experience.

Swap Document Server to Euro-Office, add blank-document creation, and restyle the app with an Office-like light/dark theme and compact editor toolbar.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ysj7705andCursor committed 2026-07-16 17:47:06 +09:00
1 parent 5ac6281304
commit 0800fc8571
27 files changed
+735 -394

No files matched your search

+32
View File
@@ -13,6 +13,10 @@ import {
buildEditorConfig,
convertToOriginalFormat,
} from '../services/onlyOffice.js';
import {
createBlankDocument,
isBlankType,
} from '../services/blankDocument.js';
import { extensionOf } from '../utils/formats.js';
import { decodeUploadFilename } from '../utils/filename.js';
import { extractToken, verifyOnlyOfficeToken } from '../utils/jwt.js';
@@ -24,6 +28,34 @@ const upload = multer({
export const documentsRouter = Router();
documentsRouter.post('/new', requireSession, async (req, res) => {
const type = typeof req.body?.type === 'string' ? req.body.type.trim() : '';
if (!isBlankType(type)) {
res.status(400).json({
error: 'unsupported_type',
message: '지원 형식: docx, xlsx, pptx, pdf, hwpx',
});
return;
}
try {
const doc = await createBlankDocument(
res.locals.sessionId as string,
type,
);
res.json({
docId: doc.docId,
filename: doc.filename,
fileType: doc.fileType,
documentType: doc.documentType,
token: doc.token,
});
} catch (err) {
console.error(err);
res.status(500).json({ error: 'create_failed' });
}
});
documentsRouter.post(
'/',
requireSession,
@@ -0,0 +1,42 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { addDocument } from './sessionStore.js';
import type { SessionDoc } from '../types/session.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export const BLANK_TYPES = ['docx', 'xlsx', 'pptx', 'pdf', 'hwpx'] as const;
export type BlankType = (typeof BLANK_TYPES)[number];
export function isBlankType(value: string): value is BlankType {
return (BLANK_TYPES as readonly string[]).includes(value);
}
/** Resolve templates dir for both tsx (src) and compiled dist. */
async function templatesDir(): Promise<string> {
const candidates = [
path.resolve(__dirname, '../../templates'),
path.resolve(__dirname, '../templates'),
];
for (const dir of candidates) {
try {
await fs.access(dir);
return dir;
} catch {
/* try next */
}
}
return candidates[0];
}
export async function createBlankDocument(
sessionId: string,
type: BlankType,
): Promise<SessionDoc> {
const dir = await templatesDir();
const templatePath = path.join(dir, `blank.${type}`);
const buffer = await fs.readFile(templatePath);
const filename = `새문서.${type}`;
return addDocument(sessionId, filename, type, buffer);
}
+2
View File
@@ -78,6 +78,8 @@ export function mimeFor(ext: string): string {
rtf: 'application/rtf',
txt: 'text/plain',
csv: 'text/csv',
hwp: 'application/x-hwp',
hwpx: 'application/hwp+zip',
};
return map[ext] ?? 'application/octet-stream';
}
Binary file not shown.
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
%PDF-1.4
1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj
2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj
3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << >> >>endobj
4 0 obj<< /Length 0 >>stream
endstream
endobj
xref
0 5
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000229 00000 n
trailer<< /Size 5 /Root 1 0 R >>
startxref
279
%%EOF
Binary file not shown.
Binary file not shown.