@kortix/sandbox 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/customize.sh +143 -0
- package/config/kortix-env-setup.sh +25 -0
- package/kortix-master/package.json +22 -0
- package/kortix-master/src/config.ts +22 -0
- package/kortix-master/src/index.ts +44 -0
- package/kortix-master/src/routes/env.ts +65 -0
- package/kortix-master/src/routes/proxy.ts +108 -0
- package/kortix-master/src/routes/update.ts +185 -0
- package/kortix-master/src/services/proxy.ts +43 -0
- package/kortix-master/src/services/secret-store.ts +156 -0
- package/kortix-master/tsconfig.json +14 -0
- package/opencode/agents/kortix-browser.md +142 -0
- package/opencode/agents/kortix-build.md +62 -0
- package/opencode/agents/kortix-explore.md +66 -0
- package/opencode/agents/kortix-image-gen.md +33 -0
- package/opencode/agents/kortix-main.md +450 -0
- package/opencode/agents/kortix-plan.md +100 -0
- package/opencode/agents/kortix-research.md +84 -0
- package/opencode/agents/kortix-sheets.md +61 -0
- package/opencode/agents/kortix-slides.md +64 -0
- package/opencode/agents/kortix-web-dev.md +572 -0
- package/opencode/commands/email.md +36 -0
- package/opencode/commands/init.md +43 -0
- package/opencode/commands/journal.md +44 -0
- package/opencode/commands/memory-init.md +81 -0
- package/opencode/commands/memory-search.md +50 -0
- package/opencode/commands/memory-status.md +56 -0
- package/opencode/commands/research.md +36 -0
- package/opencode/commands/search.md +38 -0
- package/opencode/commands/slides.md +32 -0
- package/opencode/commands/spreadsheet.md +30 -0
- package/opencode/memory.json +37 -0
- package/opencode/ocx.jsonc +10 -0
- package/opencode/opencode.jsonc +103 -0
- package/opencode/package.json +25 -0
- package/opencode/patches/apply.sh +19 -0
- package/opencode/patches/opencode-pty-spawn.txt +49 -0
- package/opencode/plugin/background-agents.ts.disabled +483 -0
- package/opencode/plugin/kdco-primitives/get-project-id.ts +172 -0
- package/opencode/plugin/kdco-primitives/index.ts +26 -0
- package/opencode/plugin/kdco-primitives/log-warn.ts +51 -0
- package/opencode/plugin/kdco-primitives/mutex.ts +122 -0
- package/opencode/plugin/kdco-primitives/shell.ts +138 -0
- package/opencode/plugin/kdco-primitives/temp.ts +36 -0
- package/opencode/plugin/kdco-primitives/terminal-detect.ts +34 -0
- package/opencode/plugin/kdco-primitives/types.ts +13 -0
- package/opencode/plugin/kdco-primitives/with-timeout.ts +84 -0
- package/opencode/plugin/memory.ts +306 -0
- package/opencode/plugin/worktree/state.ts +412 -0
- package/opencode/plugin/worktree/terminal.ts +1002 -0
- package/opencode/plugin/worktree.ts +861 -0
- package/opencode/skills/KORTIX-browser/SKILL.md +478 -0
- package/opencode/skills/KORTIX-cron-triggers/SKILL.md +173 -0
- package/opencode/skills/KORTIX-deep-research/SKILL.md +278 -0
- package/opencode/skills/KORTIX-docx/SKILL.md +398 -0
- package/opencode/skills/KORTIX-docx/scripts/__init__.py +1 -0
- package/opencode/skills/KORTIX-docx/scripts/accept_changes.py +104 -0
- package/opencode/skills/KORTIX-docx/scripts/comment.py +244 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/__init__.py +0 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/merge_runs.py +199 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/opencode/skills/KORTIX-docx/scripts/office/pack.py +159 -0
- package/opencode/skills/KORTIX-docx/scripts/office/soffice.py +183 -0
- package/opencode/skills/KORTIX-docx/scripts/office/unpack.py +132 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validate.py +111 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/__init__.py +15 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/base.py +847 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/docx.py +446 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/pptx.py +275 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/redlining.py +247 -0
- package/opencode/skills/KORTIX-docx/scripts/render_docx.py +179 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/comments.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtended.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtensible.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsIds.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/people.xml +3 -0
- package/opencode/skills/KORTIX-domain-research/SKILL.md +96 -0
- package/opencode/skills/KORTIX-domain-research/scripts/domain-lookup.py +810 -0
- package/opencode/skills/KORTIX-elevenlabs/SKILL.md +230 -0
- package/opencode/skills/KORTIX-elevenlabs/scripts/tts.py +389 -0
- package/opencode/skills/KORTIX-email/SKILL.md +145 -0
- package/opencode/skills/KORTIX-legal-writer/SKILL.md +409 -0
- package/opencode/skills/KORTIX-legal-writer/references/bluebook.md +152 -0
- package/opencode/skills/KORTIX-legal-writer/references/document-types.md +416 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/courtlistener.py +291 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/ecfr_lookup.py +299 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/verify-legal.py +507 -0
- package/opencode/skills/KORTIX-logo-creator/SKILL.md +293 -0
- package/opencode/skills/KORTIX-logo-creator/references/prompt-patterns.md +134 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/compose_logo.py +406 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/create_logo_sheet.py +258 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/remove_bg.py +96 -0
- package/opencode/skills/KORTIX-memory/SKILL.md +261 -0
- package/opencode/skills/KORTIX-memory/scripts/export-sessions.py +409 -0
- package/opencode/skills/KORTIX-paper-creator/SKILL.md +549 -0
- package/opencode/skills/KORTIX-paper-creator/assets/template.tex +101 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/compile.sh +177 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/openalex_to_bibtex.py +220 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/verify.sh +354 -0
- package/opencode/skills/KORTIX-paper-search/SKILL.md +418 -0
- package/opencode/skills/KORTIX-pdf/SKILL.md +232 -0
- package/opencode/skills/KORTIX-pdf/forms.md +36 -0
- package/opencode/skills/KORTIX-pdf/reference.md +105 -0
- package/opencode/skills/KORTIX-pdf/scripts/check_bounding_boxes.py +65 -0
- package/opencode/skills/KORTIX-pdf/scripts/check_fillable_fields.py +11 -0
- package/opencode/skills/KORTIX-pdf/scripts/convert_pdf_to_images.py +33 -0
- package/opencode/skills/KORTIX-pdf/scripts/create_validation_image.py +37 -0
- package/opencode/skills/KORTIX-pdf/scripts/extract_form_field_info.py +122 -0
- package/opencode/skills/KORTIX-pdf/scripts/extract_form_structure.py +115 -0
- package/opencode/skills/KORTIX-pdf/scripts/fill_fillable_fields.py +98 -0
- package/opencode/skills/KORTIX-pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/opencode/skills/KORTIX-plan/SKILL.md +228 -0
- package/opencode/skills/KORTIX-presentation-viewer/SKILL.md +87 -0
- package/opencode/skills/KORTIX-presentation-viewer/serve.ts +136 -0
- package/opencode/skills/KORTIX-presentation-viewer/viewer.html +559 -0
- package/opencode/skills/KORTIX-presentations/SKILL.md +344 -0
- package/opencode/skills/KORTIX-remotion/SKILL.md +56 -0
- package/opencode/skills/KORTIX-remotion/rules/3d.md +86 -0
- package/opencode/skills/KORTIX-remotion/rules/animations.md +29 -0
- package/opencode/skills/KORTIX-remotion/rules/assets.md +78 -0
- package/opencode/skills/KORTIX-remotion/rules/audio-visualization.md +198 -0
- package/opencode/skills/KORTIX-remotion/rules/audio.md +169 -0
- package/opencode/skills/KORTIX-remotion/rules/calculate-metadata.md +104 -0
- package/opencode/skills/KORTIX-remotion/rules/can-decode.md +75 -0
- package/opencode/skills/KORTIX-remotion/rules/charts.md +120 -0
- package/opencode/skills/KORTIX-remotion/rules/compositions.md +141 -0
- package/opencode/skills/KORTIX-remotion/rules/display-captions.md +184 -0
- package/opencode/skills/KORTIX-remotion/rules/extract-frames.md +229 -0
- package/opencode/skills/KORTIX-remotion/rules/ffmpeg.md +38 -0
- package/opencode/skills/KORTIX-remotion/rules/fonts.md +152 -0
- package/opencode/skills/KORTIX-remotion/rules/get-audio-duration.md +58 -0
- package/opencode/skills/KORTIX-remotion/rules/get-video-dimensions.md +68 -0
- package/opencode/skills/KORTIX-remotion/rules/get-video-duration.md +58 -0
- package/opencode/skills/KORTIX-remotion/rules/gifs.md +141 -0
- package/opencode/skills/KORTIX-remotion/rules/images.md +130 -0
- package/opencode/skills/KORTIX-remotion/rules/import-srt-captions.md +69 -0
- package/opencode/skills/KORTIX-remotion/rules/light-leaks.md +73 -0
- package/opencode/skills/KORTIX-remotion/rules/lottie.md +68 -0
- package/opencode/skills/KORTIX-remotion/rules/maps.md +401 -0
- package/opencode/skills/KORTIX-remotion/rules/measuring-dom-nodes.md +35 -0
- package/opencode/skills/KORTIX-remotion/rules/measuring-text.md +143 -0
- package/opencode/skills/KORTIX-remotion/rules/parameters.md +98 -0
- package/opencode/skills/KORTIX-remotion/rules/sequencing.md +118 -0
- package/opencode/skills/KORTIX-remotion/rules/subtitles.md +36 -0
- package/opencode/skills/KORTIX-remotion/rules/tailwind.md +11 -0
- package/opencode/skills/KORTIX-remotion/rules/text-animations.md +20 -0
- package/opencode/skills/KORTIX-remotion/rules/timing.md +179 -0
- package/opencode/skills/KORTIX-remotion/rules/transcribe-captions.md +70 -0
- package/opencode/skills/KORTIX-remotion/rules/transitions.md +197 -0
- package/opencode/skills/KORTIX-remotion/rules/transparent-videos.md +106 -0
- package/opencode/skills/KORTIX-remotion/rules/trimming.md +53 -0
- package/opencode/skills/KORTIX-remotion/rules/videos.md +171 -0
- package/opencode/skills/KORTIX-secrets/SKILL.md +280 -0
- package/opencode/skills/KORTIX-semantic-search/SKILL.md +213 -0
- package/opencode/skills/KORTIX-session-search/SKILL.md +807 -0
- package/opencode/skills/KORTIX-session-search/Untitled +1 -0
- package/opencode/skills/KORTIX-skill-creator/SKILL.md +163 -0
- package/opencode/skills/KORTIX-web-research/SKILL.md +69 -0
- package/opencode/skills/KORTIX-xlsx/LICENSE.txt +30 -0
- package/opencode/skills/KORTIX-xlsx/SKILL.md +549 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/__init__.py +0 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/merge_runs.py +199 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/pack.py +159 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/soffice.py +183 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/unpack.py +132 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validate.py +111 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/__init__.py +15 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/base.py +847 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/docx.py +446 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/pptx.py +275 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/redlining.py +247 -0
- package/opencode/skills/KORTIX-xlsx/scripts/recalc.py +184 -0
- package/opencode/tools/image-gen.ts +342 -0
- package/opencode/tools/image-search.ts +190 -0
- package/opencode/tools/memory-get.ts +168 -0
- package/opencode/tools/memory-search.ts +247 -0
- package/opencode/tools/presentation-gen.ts +723 -0
- package/opencode/tools/scrape-webpage.ts +115 -0
- package/opencode/tools/scripts/.python-version +1 -0
- package/opencode/tools/scripts/convert_pdf.py +184 -0
- package/opencode/tools/scripts/convert_pptx.py +562 -0
- package/opencode/tools/scripts/pyproject.toml +11 -0
- package/opencode/tools/scripts/uv.lock +287 -0
- package/opencode/tools/scripts/validate_slide.py +74 -0
- package/opencode/tools/show-user.ts +217 -0
- package/opencode/tools/tests/e2e-presentation-fix.ts +277 -0
- package/opencode/tools/tests/image-gen.test.ts +215 -0
- package/opencode/tools/tests/image-search.test.ts +125 -0
- package/opencode/tools/tests/memory-system-benchmark.ts +1076 -0
- package/opencode/tools/tests/presentation-gen.test.ts +389 -0
- package/opencode/tools/tests/scrape-webpage.test.ts +74 -0
- package/opencode/tools/tests/show-user.test.ts +241 -0
- package/opencode/tools/tests/video-gen.test.ts +110 -0
- package/opencode/tools/tests/web-search.test.ts +106 -0
- package/opencode/tools/video-gen.ts +200 -0
- package/opencode/tools/web-search.ts +153 -0
- package/opencode/tsconfig.json +29 -0
- package/package.json +36 -0
- package/patch-agent-browser.js +100 -0
- package/postinstall.sh +88 -0
- package/services/KORTIX-presentation-viewer/run +37 -0
- package/services/agent-browser-viewer/run +48 -0
- package/services/kortix-master/run +16 -0
- package/services/lss-sync/run +22 -0
- package/services/opencode-serve/run +25 -0
- package/services/opencode-web/run +21 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from pypdf import PdfReader, PdfWriter
|
|
5
|
+
from pypdf.annotations import FreeText
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def transform_from_image_coords(bbox, image_width, image_height, pdf_width, pdf_height):
|
|
11
|
+
x_scale = pdf_width / image_width
|
|
12
|
+
y_scale = pdf_height / image_height
|
|
13
|
+
|
|
14
|
+
left = bbox[0] * x_scale
|
|
15
|
+
right = bbox[2] * x_scale
|
|
16
|
+
|
|
17
|
+
top = pdf_height - (bbox[1] * y_scale)
|
|
18
|
+
bottom = pdf_height - (bbox[3] * y_scale)
|
|
19
|
+
|
|
20
|
+
return left, bottom, right, top
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def transform_from_pdf_coords(bbox, pdf_height):
|
|
24
|
+
left = bbox[0]
|
|
25
|
+
right = bbox[2]
|
|
26
|
+
|
|
27
|
+
pypdf_top = pdf_height - bbox[1]
|
|
28
|
+
pypdf_bottom = pdf_height - bbox[3]
|
|
29
|
+
|
|
30
|
+
return left, pypdf_bottom, right, pypdf_top
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fill_pdf_form(input_pdf_path, fields_json_path, output_pdf_path):
|
|
34
|
+
|
|
35
|
+
with open(fields_json_path, "r") as f:
|
|
36
|
+
fields_data = json.load(f)
|
|
37
|
+
|
|
38
|
+
reader = PdfReader(input_pdf_path)
|
|
39
|
+
writer = PdfWriter()
|
|
40
|
+
|
|
41
|
+
writer.append(reader)
|
|
42
|
+
|
|
43
|
+
pdf_dimensions = {}
|
|
44
|
+
for i, page in enumerate(reader.pages):
|
|
45
|
+
mediabox = page.mediabox
|
|
46
|
+
pdf_dimensions[i + 1] = [mediabox.width, mediabox.height]
|
|
47
|
+
|
|
48
|
+
annotations = []
|
|
49
|
+
for field in fields_data["form_fields"]:
|
|
50
|
+
page_num = field["page_number"]
|
|
51
|
+
|
|
52
|
+
page_info = next(p for p in fields_data["pages"] if p["page_number"] == page_num)
|
|
53
|
+
pdf_width, pdf_height = pdf_dimensions[page_num]
|
|
54
|
+
|
|
55
|
+
if "pdf_width" in page_info:
|
|
56
|
+
transformed_entry_box = transform_from_pdf_coords(
|
|
57
|
+
field["entry_bounding_box"],
|
|
58
|
+
float(pdf_height)
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
image_width = page_info["image_width"]
|
|
62
|
+
image_height = page_info["image_height"]
|
|
63
|
+
transformed_entry_box = transform_from_image_coords(
|
|
64
|
+
field["entry_bounding_box"],
|
|
65
|
+
image_width, image_height,
|
|
66
|
+
float(pdf_width), float(pdf_height)
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if "entry_text" not in field or "text" not in field["entry_text"]:
|
|
70
|
+
continue
|
|
71
|
+
entry_text = field["entry_text"]
|
|
72
|
+
text = entry_text["text"]
|
|
73
|
+
if not text:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
font_name = entry_text.get("font", "Arial")
|
|
77
|
+
font_size = str(entry_text.get("font_size", 14)) + "pt"
|
|
78
|
+
font_color = entry_text.get("font_color", "000000")
|
|
79
|
+
|
|
80
|
+
annotation = FreeText(
|
|
81
|
+
text=text,
|
|
82
|
+
rect=transformed_entry_box,
|
|
83
|
+
font=font_name,
|
|
84
|
+
font_size=font_size,
|
|
85
|
+
font_color=font_color,
|
|
86
|
+
border_color=None,
|
|
87
|
+
background_color=None,
|
|
88
|
+
)
|
|
89
|
+
annotations.append(annotation)
|
|
90
|
+
writer.add_annotation(page_number=page_num - 1, annotation=annotation)
|
|
91
|
+
|
|
92
|
+
with open(output_pdf_path, "wb") as output:
|
|
93
|
+
writer.write(output)
|
|
94
|
+
|
|
95
|
+
print(f"Successfully filled PDF form and saved to {output_pdf_path}")
|
|
96
|
+
print(f"Added {len(annotations)} text annotations")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
if len(sys.argv) != 4:
|
|
101
|
+
print("Usage: fill_pdf_form_with_annotations.py [input pdf] [fields.json] [output pdf]")
|
|
102
|
+
sys.exit(1)
|
|
103
|
+
input_pdf = sys.argv[1]
|
|
104
|
+
fields_json = sys.argv[2]
|
|
105
|
+
output_pdf = sys.argv[3]
|
|
106
|
+
|
|
107
|
+
fill_pdf_form(input_pdf, fields_json, output_pdf)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kortix-plan
|
|
3
|
+
description: "Structured planning mode for complex tasks. Use when a task involves architectural decisions, multi-file changes, unfamiliar codebases, multi-phase implementations, or when the user explicitly asks to plan before executing. Provides a 5-phase workflow (Understand, Investigate, Design, Write Plan, Execute) with persistent plan files and structured todo management. Triggers on: 'plan this', 'let me think', 'how should we approach', any task requiring 3+ steps across multiple domains, or when you feel uncertain about the right approach."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Plan Mode
|
|
7
|
+
|
|
8
|
+
Structured planning methodology for complex tasks. Produces a persistent plan file and a phased todo list before execution begins.
|
|
9
|
+
|
|
10
|
+
## When to Plan
|
|
11
|
+
|
|
12
|
+
**Enter plan mode when:**
|
|
13
|
+
- The task spans 3+ steps across multiple files, systems, or domains
|
|
14
|
+
- Architectural or design decisions are required (new systems, major refactors, integration work)
|
|
15
|
+
- The codebase or domain is unfamiliar — you need investigation before action
|
|
16
|
+
- The task has dependencies between steps (order matters, one wrong move cascades)
|
|
17
|
+
- The user explicitly asks to plan first ("think about this", "plan this out", "how should we approach")
|
|
18
|
+
- You feel uncertain about the right approach — planning resolves uncertainty before committing
|
|
19
|
+
|
|
20
|
+
**Skip planning when:**
|
|
21
|
+
- The task is a single, clear operation (fix a typo, add a field, run a command)
|
|
22
|
+
- You've done this exact kind of task before and the approach is obvious
|
|
23
|
+
- The task has fewer than 3 steps with no ambiguity
|
|
24
|
+
- The user says "just do it" or "quick fix"
|
|
25
|
+
|
|
26
|
+
## The 5 Phases
|
|
27
|
+
|
|
28
|
+
### Phase 1: Understand
|
|
29
|
+
|
|
30
|
+
Parse intent and establish scope before touching anything.
|
|
31
|
+
|
|
32
|
+
1. **Restate the goal** in one sentence. If you can't, the request is ambiguous — ask clarifying questions.
|
|
33
|
+
2. **Check memory** — search `workspace/.kortix/MEMORY.md` and `workspace/.kortix/memory/` for prior context, decisions, preferences, or past work that relates.
|
|
34
|
+
3. **Check for existing plans** — `glob("workspace/.kortix/plans/*.md")` to see if a related plan already exists from a prior session.
|
|
35
|
+
4. **Identify constraints** — deadlines, tech stack requirements, backwards compatibility, user preferences from memory.
|
|
36
|
+
5. **Define success criteria** — what does "done" look like? Be specific. Write these down.
|
|
37
|
+
6. **Create initial todo list** — high-level phases only at this stage:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
TodoWrite([
|
|
41
|
+
{ id: "plan-1", content: "Phase 1: Understand requirements and context", status: "in_progress", priority: "high" },
|
|
42
|
+
{ id: "plan-2", content: "Phase 2: Investigate codebase and gather information", status: "pending", priority: "high" },
|
|
43
|
+
{ id: "plan-3", content: "Phase 3: Design approach and make decisions", status: "pending", priority: "high" },
|
|
44
|
+
{ id: "plan-4", content: "Phase 4: Write plan file", status: "pending", priority: "high" },
|
|
45
|
+
{ id: "plan-5", content: "Phase 5: Execute plan", status: "pending", priority: "high" }
|
|
46
|
+
])
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Phase 2: Investigate
|
|
50
|
+
|
|
51
|
+
Gather all information needed to make design decisions. This phase is **read-only** — do not edit project files.
|
|
52
|
+
|
|
53
|
+
1. **Launch parallel investigations** — use the Task tool to dispatch explore agents or `@kortix-research` for independent questions:
|
|
54
|
+
- Codebase exploration: "Find all files related to X, understand the current architecture"
|
|
55
|
+
- Dependency analysis: "What would be affected by changing Y?"
|
|
56
|
+
- Research: "What's the best practice for Z? What libraries exist?"
|
|
57
|
+
2. **Read critical files** directly — don't delegate when you need deep understanding of specific files.
|
|
58
|
+
3. **Search the web** if the task involves unfamiliar tech, APIs, or patterns.
|
|
59
|
+
4. **Semantic search** for related past work: `lss "relevant query" -p /workspace/.kortix/ --json -k 5`
|
|
60
|
+
5. **Document findings** — note key discoveries as you go. These feed into the design phase.
|
|
61
|
+
6. **Update todos** — mark Phase 2 complete, note any blockers or open questions discovered.
|
|
62
|
+
|
|
63
|
+
**Key rule:** Do not start writing code or editing files during investigation. Resist the urge. Premature implementation is the enemy of good planning.
|
|
64
|
+
|
|
65
|
+
### Phase 3: Design
|
|
66
|
+
|
|
67
|
+
Synthesize findings into decisions. This is where you think.
|
|
68
|
+
|
|
69
|
+
1. **List approaches** — for each major decision, identify 2-3 options with tradeoffs.
|
|
70
|
+
2. **Decide** — pick the best approach for each. Document why. Don't present a menu to the user unless the tradeoffs are genuinely user-facing (cost, UX, timeline).
|
|
71
|
+
3. **Map dependencies** — which tasks depend on others? What's the critical path? What can be parallelized?
|
|
72
|
+
4. **Break down into tasks** — decompose the implementation into specific, atomic tasks. Each task should be:
|
|
73
|
+
- Independently verifiable (you can check if it worked)
|
|
74
|
+
- Small enough to complete in one focused pass
|
|
75
|
+
- Clear about what files it touches
|
|
76
|
+
5. **Identify risks** — what could go wrong? What's the rollback plan?
|
|
77
|
+
6. **Expand the todo list** — replace the Phase 5 placeholder with the actual task breakdown:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
TodoWrite([
|
|
81
|
+
{ id: "plan-1", content: "Phase 1: Understand requirements", status: "completed", priority: "high" },
|
|
82
|
+
{ id: "plan-2", content: "Phase 2: Investigate codebase", status: "completed", priority: "high" },
|
|
83
|
+
{ id: "plan-3", content: "Phase 3: Design approach", status: "completed", priority: "high" },
|
|
84
|
+
{ id: "plan-4", content: "Phase 4: Write plan file", status: "in_progress", priority: "high" },
|
|
85
|
+
{ id: "exec-1", content: "Create database schema for user metrics", status: "pending", priority: "high" },
|
|
86
|
+
{ id: "exec-2", content: "Implement metrics collection service (depends: exec-1)", status: "pending", priority: "high" },
|
|
87
|
+
{ id: "exec-3", content: "Add API endpoints for metrics retrieval (depends: exec-2)", status: "pending", priority: "medium" },
|
|
88
|
+
{ id: "exec-4", content: "Build export functionality (CSV, JSON, PDF) (depends: exec-3)", status: "pending", priority: "medium" },
|
|
89
|
+
{ id: "exec-5", content: "Write tests and verify end-to-end", status: "pending", priority: "high" }
|
|
90
|
+
])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Note: use `(depends: <id>)` in the content field to express dependencies. Execute tasks in dependency order.
|
|
94
|
+
|
|
95
|
+
### Phase 4: Write Plan
|
|
96
|
+
|
|
97
|
+
Persist the plan as a markdown file for cross-session reference.
|
|
98
|
+
|
|
99
|
+
1. **Create the plan file** at `workspace/.kortix/plans/{YYYY-MM-DD}-{slug}.md`
|
|
100
|
+
- `{slug}` = 2-4 word kebab-case summary (e.g., `user-metrics-export`, `auth-refactor`, `api-v2-migration`)
|
|
101
|
+
2. **Use the standard format** (see Plan File Format below).
|
|
102
|
+
3. **Present the plan** to the user with a concise summary. Wait for confirmation before executing unless the user already said "just do it."
|
|
103
|
+
|
|
104
|
+
### Phase 5: Execute
|
|
105
|
+
|
|
106
|
+
Work through the plan systematically.
|
|
107
|
+
|
|
108
|
+
1. **Follow the todo list** — work through tasks in dependency order.
|
|
109
|
+
2. **One task at a time** — mark `in_progress`, complete it, mark `completed`, then move to the next.
|
|
110
|
+
3. **Verify each task** — run tests, check output, read files back. Don't mark complete until verified.
|
|
111
|
+
4. **Update the plan file** if the approach changes during execution — plans are living documents, not contracts.
|
|
112
|
+
5. **Update todos in real-time** — the todo list is the single source of truth for progress.
|
|
113
|
+
6. **Handle blockers** — if a task can't be completed as planned:
|
|
114
|
+
- Try alternative approaches (Failure Protocol from kortix-main applies)
|
|
115
|
+
- Update the plan file with what changed and why
|
|
116
|
+
- Adjust dependent tasks as needed
|
|
117
|
+
7. **Final verification** — after all tasks complete, verify the overall success criteria from Phase 1.
|
|
118
|
+
8. **Update memory** — persist any learnings, decisions, or patterns worth remembering.
|
|
119
|
+
|
|
120
|
+
## Plan File Format
|
|
121
|
+
|
|
122
|
+
```markdown
|
|
123
|
+
# Plan: {Title}
|
|
124
|
+
|
|
125
|
+
**Created:** {YYYY-MM-DD}
|
|
126
|
+
**Status:** draft | in-progress | completed | abandoned
|
|
127
|
+
**Goal:** {One-sentence goal}
|
|
128
|
+
|
|
129
|
+
## Context
|
|
130
|
+
|
|
131
|
+
{What prompted this plan. Prior work, user request, relevant memory.}
|
|
132
|
+
|
|
133
|
+
## Success Criteria
|
|
134
|
+
|
|
135
|
+
- [ ] {Criterion 1}
|
|
136
|
+
- [ ] {Criterion 2}
|
|
137
|
+
- [ ] {Criterion 3}
|
|
138
|
+
|
|
139
|
+
## Approach
|
|
140
|
+
|
|
141
|
+
{High-level approach. Key architectural decisions and why.}
|
|
142
|
+
|
|
143
|
+
### Alternatives Considered
|
|
144
|
+
|
|
145
|
+
- **{Alternative A}:** {Why rejected}
|
|
146
|
+
- **{Alternative B}:** {Why rejected}
|
|
147
|
+
|
|
148
|
+
## Task Breakdown
|
|
149
|
+
|
|
150
|
+
### Phase: {Phase Name}
|
|
151
|
+
- [ ] {Task 1} — {brief description}
|
|
152
|
+
- [ ] {Task 2} — {brief description, depends on Task 1}
|
|
153
|
+
|
|
154
|
+
### Phase: {Phase Name}
|
|
155
|
+
- [ ] {Task 3} — {brief description}
|
|
156
|
+
|
|
157
|
+
## Risks
|
|
158
|
+
|
|
159
|
+
- **{Risk 1}:** {Mitigation}
|
|
160
|
+
- **{Risk 2}:** {Mitigation}
|
|
161
|
+
|
|
162
|
+
## Notes
|
|
163
|
+
|
|
164
|
+
{Anything discovered during execution. Updated as work progresses.}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Todo List Patterns
|
|
168
|
+
|
|
169
|
+
### Naming Convention
|
|
170
|
+
|
|
171
|
+
Use prefixed IDs to group related tasks:
|
|
172
|
+
- `plan-{n}` — planning phase tasks
|
|
173
|
+
- `exec-{n}` — execution tasks
|
|
174
|
+
- `verify-{n}` — verification tasks
|
|
175
|
+
- `fix-{n}` — fix/adjustment tasks added during execution
|
|
176
|
+
|
|
177
|
+
### Dependency Tracking
|
|
178
|
+
|
|
179
|
+
Express dependencies in the task content: `"Implement X (depends: exec-1, exec-2)"`
|
|
180
|
+
|
|
181
|
+
Execute tasks in dependency order. Never start a task whose dependencies aren't completed.
|
|
182
|
+
|
|
183
|
+
### Progress Updates
|
|
184
|
+
|
|
185
|
+
- Mark `in_progress` when you START a task (not before)
|
|
186
|
+
- Mark `completed` IMMEDIATELY when done (don't batch completions)
|
|
187
|
+
- Only ONE task should be `in_progress` at a time
|
|
188
|
+
- Add new tasks as discovered — plans evolve during execution
|
|
189
|
+
- Cancel tasks that become unnecessary with `cancelled` status
|
|
190
|
+
|
|
191
|
+
### Handling Plan Changes
|
|
192
|
+
|
|
193
|
+
If execution reveals the plan needs adjustment:
|
|
194
|
+
1. Update the relevant task in the todo list
|
|
195
|
+
2. Add new tasks if scope expanded
|
|
196
|
+
3. Cancel tasks that are no longer needed
|
|
197
|
+
4. Update the plan file's Notes section with what changed and why
|
|
198
|
+
5. Continue execution — don't restart planning unless the fundamental approach is wrong
|
|
199
|
+
|
|
200
|
+
## Managing Existing Plans
|
|
201
|
+
|
|
202
|
+
### List Plans
|
|
203
|
+
```
|
|
204
|
+
glob("workspace/.kortix/plans/*.md")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Resume a Plan
|
|
208
|
+
Read the plan file, check which tasks are incomplete, rebuild the todo list from the remaining tasks, and continue execution from where it left off.
|
|
209
|
+
|
|
210
|
+
### Archive a Plan
|
|
211
|
+
Update the plan file status to `completed` or `abandoned`. No need to delete — plans serve as historical reference for future similar tasks.
|
|
212
|
+
|
|
213
|
+
## Integration with Subagents
|
|
214
|
+
|
|
215
|
+
During Phase 2 (Investigate), dispatch parallel investigations:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
Task(@kortix-research, "Research best practices for X. Return: key findings, recommended libraries, common pitfalls.")
|
|
219
|
+
Task(explore, "Find all files in the codebase related to Y. Return: file paths, key functions, architecture overview.")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
During Phase 5 (Execute), delegate specialist subtasks:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
Task(@kortix-web-dev, "Build the frontend component for X. Here is the plan: {paste relevant section}. Here is the context: {paste findings from Phase 2}.")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Always include full context when delegating — subagents start with zero context.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# KORTIX Presentation Viewer
|
|
2
|
+
|
|
3
|
+
A polished slide viewer and preview server for HTML presentations (1920x1080).
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The viewer consists of two parts:
|
|
8
|
+
|
|
9
|
+
1. **`viewer.html`** — a self-contained HTML template that renders slides in scaled iframes with keyboard navigation, fullscreen, and a thumbnail strip.
|
|
10
|
+
2. **`serve.ts`** — a lightweight Bun HTTP server that serves the presentation folder and injects the viewer at `/`.
|
|
11
|
+
|
|
12
|
+
## Why a server (not just a static HTML file)?
|
|
13
|
+
|
|
14
|
+
Standalone 1920x1080 HTML slides opened directly in a browser are an unscaled, scrollable mess. The viewer fixes this by loading slides into iframes and CSS-scaling them to fit any viewport.
|
|
15
|
+
|
|
16
|
+
The problem: **loading `<iframe src="slide_XX.html">` does not work reliably over `file://`**. Browsers enforce CORS/sandboxing restrictions on `file://` origins — Chrome blocks cross-file iframe access, Safari has different restrictions, Firefox another set. There is no consistent way to make iframe-based viewers work from the filesystem.
|
|
17
|
+
|
|
18
|
+
An HTTP server on `localhost` eliminates all of this. Iframes load cleanly, images resolve correctly, and it works identically across every browser.
|
|
19
|
+
|
|
20
|
+
## In the sandbox (container)
|
|
21
|
+
|
|
22
|
+
**The viewer is already running as a service inside the sandbox container.** It starts automatically on boot via s6-overlay and listens on **port 3210**.
|
|
23
|
+
|
|
24
|
+
- The service watches `/workspace/presentations/` for any presentation with a `metadata.json`
|
|
25
|
+
- It serves the most recently created/updated presentation at `http://localhost:3210`
|
|
26
|
+
- Port 3210 is exposed in docker-compose and mapped to the host
|
|
27
|
+
|
|
28
|
+
**The agent does not need to start the server manually.** It is already available. After creating slides, the agent can simply tell the user to open `http://localhost:3210` (or the equivalent sandbox URL) to preview.
|
|
29
|
+
|
|
30
|
+
If the service is not running for some reason (e.g. no presentations exist yet), it will auto-start once the first presentation's `metadata.json` appears. The agent can also restart it manually:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun run /opt/KORTIX-presentation-viewer/serve.ts /workspace/presentations/<name>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Container details
|
|
37
|
+
|
|
38
|
+
| Item | Value |
|
|
39
|
+
|------|-------|
|
|
40
|
+
| Service location | `/etc/services.d/KORTIX-presentation-viewer/run` |
|
|
41
|
+
| Viewer files | `/opt/KORTIX-presentation-viewer/` |
|
|
42
|
+
| Port | `3210` (mapped to host) |
|
|
43
|
+
| Presentations dir | `/workspace/presentations/` |
|
|
44
|
+
| Managed by | s6-overlay (auto-restart on crash) |
|
|
45
|
+
|
|
46
|
+
## Local development (outside container)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bun run .opencode/skills/KORTIX-presentation-viewer/serve.ts presentations/my-deck
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or via the `presentation-gen` tool:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
presentation-gen(action: "preview", presentation_name: "my-deck")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This starts the server at `http://localhost:3210` and auto-opens the browser.
|
|
59
|
+
|
|
60
|
+
## Viewer controls
|
|
61
|
+
|
|
62
|
+
| Key | Action |
|
|
63
|
+
|-----|--------|
|
|
64
|
+
| `→` / `Space` / `↓` | Next slide |
|
|
65
|
+
| `←` / `↑` | Previous slide |
|
|
66
|
+
| `Home` | First slide |
|
|
67
|
+
| `End` | Last slide |
|
|
68
|
+
| `F` | Toggle fullscreen |
|
|
69
|
+
| `T` | Toggle thumbnail strip |
|
|
70
|
+
| `?` | Toggle keyboard shortcuts |
|
|
71
|
+
| `Esc` | Exit fullscreen / close panels |
|
|
72
|
+
| Swipe left/right | Navigate (touch) |
|
|
73
|
+
|
|
74
|
+
## How scaling works
|
|
75
|
+
|
|
76
|
+
Each slide is a 1920x1080 iframe. The viewer:
|
|
77
|
+
|
|
78
|
+
1. Measures the actual stage area via `getBoundingClientRect()`
|
|
79
|
+
2. Computes a scale factor: `min(availWidth / 1920, availHeight / 1080)`
|
|
80
|
+
3. Sets the wrapper to the **displayed pixel size** (`1920 * scale` x `1080 * scale`)
|
|
81
|
+
4. Applies `transform: scale(factor)` with `transform-origin: 0 0` to each iframe
|
|
82
|
+
|
|
83
|
+
This means the wrapper's DOM size matches its visual size, so flexbox centering works perfectly. The 16:9 aspect ratio is maintained at any viewport size. A 3% proportional padding keeps space around the slide.
|
|
84
|
+
|
|
85
|
+
## Integration with presentation-gen
|
|
86
|
+
|
|
87
|
+
The `presentation-gen` tool auto-generates a `viewer.html` in each presentation folder on every `create_slide` or `delete_slide` action. This is a static fallback for quick local file:// viewing (works in some browsers), but the server is always the recommended way to preview.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight presentation preview server.
|
|
3
|
+
*
|
|
4
|
+
* Serves a presentation folder over HTTP with a polished slide viewer.
|
|
5
|
+
* Resolves the file:// iframe/CORS issues that make raw HTML viewing broken.
|
|
6
|
+
*
|
|
7
|
+
* Usage: bun run serve.ts <presentation-dir>
|
|
8
|
+
* e.g. bun run serve.ts presentations/marko-kraemer
|
|
9
|
+
* PORT=4000 bun run serve.ts presentations/my-deck
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, existsSync, statSync } from "fs";
|
|
13
|
+
import { resolve, join, extname, dirname } from "path";
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
|
|
16
|
+
const MIME_TYPES: Record<string, string> = {
|
|
17
|
+
".html": "text/html; charset=utf-8",
|
|
18
|
+
".css": "text/css; charset=utf-8",
|
|
19
|
+
".js": "application/javascript; charset=utf-8",
|
|
20
|
+
".json": "application/json; charset=utf-8",
|
|
21
|
+
".png": "image/png",
|
|
22
|
+
".jpg": "image/jpeg",
|
|
23
|
+
".jpeg": "image/jpeg",
|
|
24
|
+
".gif": "image/gif",
|
|
25
|
+
".svg": "image/svg+xml",
|
|
26
|
+
".webp": "image/webp",
|
|
27
|
+
".ico": "image/x-icon",
|
|
28
|
+
".woff": "font/woff",
|
|
29
|
+
".woff2": "font/woff2",
|
|
30
|
+
".ttf": "font/ttf",
|
|
31
|
+
".pdf": "application/pdf",
|
|
32
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function getMime(filePath: string): string {
|
|
36
|
+
return MIME_TYPES[extname(filePath).toLowerCase()] || "application/octet-stream";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function serveFile(filePath: string): Response {
|
|
40
|
+
if (!existsSync(filePath) || !statSync(filePath).isFile()) {
|
|
41
|
+
return new Response("Not found", { status: 404 });
|
|
42
|
+
}
|
|
43
|
+
const data = readFileSync(filePath);
|
|
44
|
+
return new Response(data, {
|
|
45
|
+
headers: { "Content-Type": getMime(filePath), "Cache-Control": "no-cache" },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const presArg = process.argv[2];
|
|
50
|
+
if (!presArg) {
|
|
51
|
+
console.error("Usage: bun run serve.ts <presentation-dir>");
|
|
52
|
+
console.error(" e.g. bun run serve.ts presentations/marko-kraemer");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const presDir = resolve(presArg);
|
|
57
|
+
const metaPath = join(presDir, "metadata.json");
|
|
58
|
+
|
|
59
|
+
if (!existsSync(metaPath)) {
|
|
60
|
+
console.error(`Error: metadata.json not found in ${presDir}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const metadata = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
65
|
+
const parentDir = dirname(presDir);
|
|
66
|
+
const viewerTemplatePath = resolve(dirname(new URL(import.meta.url).pathname), "viewer.html");
|
|
67
|
+
|
|
68
|
+
function buildViewerHtml(): string {
|
|
69
|
+
const slides = Object.entries(metadata.slides || {})
|
|
70
|
+
.map(([num, data]: [string, any]) => ({
|
|
71
|
+
number: parseInt(num),
|
|
72
|
+
title: data.title || `Slide ${num}`,
|
|
73
|
+
filename: data.filename || `slide_${String(num).padStart(2, "0")}.html`,
|
|
74
|
+
}))
|
|
75
|
+
.sort((a, b) => a.number - b.number);
|
|
76
|
+
|
|
77
|
+
const presData = JSON.stringify({
|
|
78
|
+
title: metadata.title || metadata.presentation_name || "Presentation",
|
|
79
|
+
slides,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const template = readFileSync(viewerTemplatePath, "utf-8");
|
|
83
|
+
return template
|
|
84
|
+
.replace("{{TITLE}}", metadata.title || "Presentation")
|
|
85
|
+
.replace("{{PRESENTATION_DATA}}", presData);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const port = parseInt(process.env.PORT || "3210");
|
|
89
|
+
|
|
90
|
+
const server = Bun.serve({
|
|
91
|
+
port,
|
|
92
|
+
fetch(req) {
|
|
93
|
+
const url = new URL(req.url);
|
|
94
|
+
let pathname = decodeURIComponent(url.pathname);
|
|
95
|
+
|
|
96
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
97
|
+
const html = buildViewerHtml();
|
|
98
|
+
return new Response(html, {
|
|
99
|
+
headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-cache" },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (pathname.startsWith("/../images/") || pathname.startsWith("/%2E%2E/images/")) {
|
|
104
|
+
const imgPath = join(parentDir, "images", pathname.replace(/^\/(\.\.\/|%2E%2E\/)images\//, ""));
|
|
105
|
+
return serveFile(imgPath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (pathname.startsWith("/images/")) {
|
|
109
|
+
const imgPath = join(parentDir, "images", pathname.replace(/^\/images\//, ""));
|
|
110
|
+
return serveFile(imgPath);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const filePath = join(presDir, pathname.slice(1));
|
|
114
|
+
return serveFile(filePath);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const url = `http://localhost:${server.port}`;
|
|
119
|
+
console.log(`\n Presentation Viewer`);
|
|
120
|
+
console.log(` ${metadata.title || metadata.presentation_name || "Presentation"}`);
|
|
121
|
+
console.log(` ${Object.keys(metadata.slides || {}).length} slides\n`);
|
|
122
|
+
console.log(` ${url}\n`);
|
|
123
|
+
console.log(` Press Ctrl+C to stop\n`);
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const platform = process.platform;
|
|
127
|
+
if (platform === "darwin") {
|
|
128
|
+
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
129
|
+
} else if (platform === "linux") {
|
|
130
|
+
execSync(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
131
|
+
} else if (platform === "win32") {
|
|
132
|
+
execSync(`start "${url}"`, { stdio: "ignore" });
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
/* browser auto-open is best-effort */
|
|
136
|
+
}
|