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
1 change: 1 addition & 0 deletions rhwp-studio/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
<div class="md-item" data-cmd="insert:image"><span class="md-icon icon-image"></span><span class="md-label">그림</span></div>
<div class="md-item" data-cmd="insert:textbox"><span class="md-icon icon-textbox"></span><span class="md-label">글상자</span></div>
<div class="md-item" data-cmd="insert:equation"><span class="md-icon"></span><span class="md-label">수식</span><span class="md-shortcut">Ctrl+N,M</span></div>
<div class="md-item" data-cmd="insert:yangsik-parts"><span class="md-icon"></span><span class="md-label">양식 부품</span></div>
<div class="md-sep"></div>
<div class="md-item disabled" data-cmd="insert:field"><span class="md-icon"></span><span class="md-label">필드 입력</span><span class="md-shortcut">Ctrl+K+E</span></div>
<div class="md-sep"></div>
Expand Down
48 changes: 48 additions & 0 deletions rhwp-studio/src/command/commands/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { SymbolsDialog } from '@/ui/symbols-dialog';
import { BookmarkDialog } from '@/ui/bookmark-dialog';
import { showShapePicker } from '@/ui/shape-picker';
import type { ShapeType } from '@/ui/shape-picker';
import {
YangsikPartsDialog,
fetchYangsikFragmentManifest,
fetchYangsikFragmentXml,
type FragmentManifestEntry,
} from '@/ui/yangsik-parts-dialog';

/** 스텁 커맨드 생성 헬퍼 */
function stub(id: string, label: string, icon?: string, shortcut?: string): CommandDef {
Expand Down Expand Up @@ -391,6 +397,48 @@ export const insertCommands: CommandDef[] = [
toggleFlip(services, 'vertFlip');
},
},
{
id: 'insert:yangsik-parts',
label: '양식 부품',
canExecute: (ctx) => ctx.hasDocument && ctx.isEditable,
async execute(services) {
try {
const fragments = await fetchYangsikFragmentManifest();
if (fragments.length === 0) {
window.alert('양식 부품 카탈로그가 비어있습니다.');
return;
}
const inserter = async (entry: FragmentManifestEntry): Promise<boolean> => {
const ih = services.getInputHandler();
if (!ih) return false;
const pos = ih.getPosition();
try {
const fragmentXml = await fetchYangsikFragmentXml(entry.fragment_file);
const defs = entry.source_definitions ?? {};
services.wasm.pasteHwpxFragmentInDocument(
pos.sectionIndex,
pos.paragraphIndex,
fragmentXml,
defs.char_prs ?? '',
defs.para_prs ?? '',
defs.styles ?? '',
defs.border_fills ?? '',
);
ih.triggerAfterEdit();
return true;
} catch (err) {
console.error('[insert:yangsik-parts] paste failed', err);
return false;
}
};
new YangsikPartsDialog(fragments, inserter).show();
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error('[insert:yangsik-parts]', msg);
window.alert(`양식 부품 목록을 불러오지 못했습니다:\n${msg}`);
}
},
},
];

/** 선택 개체의 속성을 조회/변경 헬퍼 (shape/picture 분기) */
Expand Down
30 changes: 30 additions & 0 deletions rhwp-studio/src/core/wasm-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,36 @@ export class WasmBridge {
return this.doc.insertText(sec, para, charOffset, text);
}

/**
* HWPX fragment 를 Document IR 에 byte-preserving paste 한다.
* rhwp `pasteHwpxFragmentInDocument` (wasm bridge) 호출 — section 의 raw XML 보존본 위에서
* 직접 동작하므로 zip/unzip 라운드트립 없이 즉시 반영.
*
* 반환 JSON 스키마:
* `{"inserted_para_count":N,"id_remap_char_pr":{...},"id_remap_para_pr":{...},
* "id_remap_style":{...},"id_remap_border_fill":{...}}`
*/
pasteHwpxFragmentInDocument(
sec: number,
afterParaIdx: number,
fragmentXml: string,
sourceCharPrs: string,
sourceParaPrs: string,
sourceStyles: string,
sourceBorderFills: string,
): string {
if (!this.doc) throw new Error('문서가 로드되지 않았습니다');
return this.doc.pasteHwpxFragmentInDocument(
sec,
afterParaIdx,
fragmentXml,
sourceCharPrs,
sourceParaPrs,
sourceStyles,
sourceBorderFills,
);
}

deleteText(sec: number, para: number, charOffset: number, count: number): string {
if (!this.doc) throw new Error('문서가 로드되지 않았습니다');
return this.doc.deleteText(sec, para, charOffset, count);
Expand Down
Loading