@takagaki/cortex-decisions-viewer 0.4.14 → 0.4.16

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.
Files changed (2) hide show
  1. package/dist/render.js +76 -16
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -371,9 +371,26 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
371
371
  }
372
372
  .modal-cancel:hover { background: var(--bg); }
373
373
  .modal-ok { color: #047857; font-weight: 600; }
374
+ .modal-status-error { color: #dc2626; font-weight: 600; }
374
375
  .modal-paths { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
375
376
  .modal-paths-label { font-size: 12px; color: var(--muted); }
376
377
  .modal-path { font-size: 12.5px; word-break: break-all; color: var(--accent); }
378
+ /* ドラッグ&ドロップ */
379
+ .modal-file-hidden { display: none; }
380
+ .dropzone {
381
+ border: 2px dashed var(--border); border-radius: 10px; padding: 22px 16px; text-align: center;
382
+ cursor: pointer; color: var(--muted); font-size: 13px; background: var(--bg); transition: border-color .15s, background .15s, color .15s;
383
+ }
384
+ .dropzone:hover, .dropzone.dragover { border-color: var(--accent); background: var(--accent-weak); color: var(--accent); }
385
+ .dropzone-text { pointer-events: none; }
386
+ .dropzone-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; justify-content: center; }
387
+ .file-chip {
388
+ display: inline-flex; align-items: center; gap: 6px; background: var(--surface);
389
+ border: 1px solid var(--border); border-radius: 999px; padding: 3px 4px 3px 10px; font-size: 12px; color: var(--text); max-width: 100%;
390
+ }
391
+ .file-chip > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 260px; }
392
+ .file-chip-x { border: none; background: none; cursor: pointer; color: var(--muted); font-size: 16px; line-height: 1; padding: 0 4px; }
393
+ .file-chip-x:hover { color: #dc2626; }
377
394
  .summary-box { background: var(--accent-weak); border: 1px solid #dce6fb; border-radius: var(--radius); padding: 12px 16px; color: #1e3a8a; }
378
395
  .deciders { display: flex; flex-wrap: wrap; gap: 8px; }
379
396
  .refs { margin: 0; padding-left: 18px; }
@@ -664,40 +681,81 @@ const CLIENT_JS = `
664
681
  var modal = el("div", { class: "modal" });
665
682
  modal.appendChild(el("h2", { class: "modal-title", text: isT ? "会議の文字起こしを追加" : "共有資料を追加" }));
666
683
  modal.appendChild(el("p", { class: "modal-desc", text: isT
667
- ? "文字起こしファイル(.txt / .docx 等)を選んで送信すると、議事録を自動生成します。"
668
- : "資料ファイル(PDF / PPT / Excel 等)を選んで送信すると、Markdown 化して取り込みます。" }));
669
- var fileInput = el("input", { type: "file", multiple: "multiple", class: "modal-file" });
684
+ ? "文字起こしファイル(テキスト・Word・PDF・字幕 等)を選んで送信してください。(送信後、数分で議事録が自動生成されます)"
685
+ : "資料ファイル(PDF・Word・PowerPoint・Excel・テキスト・画像 等)を選んで送信してください。(送信後、数分で内容が Markdown に変換されて取り込まれます)" }));
686
+
687
+ var selectedFiles = [];
688
+ var allowedExt = isT
689
+ ? { txt: 1, md: 1, docx: 1, doc: 1, rtf: 1, pdf: 1, vtt: 1, srt: 1 }
690
+ : { pdf: 1, docx: 1, doc: 1, pptx: 1, ppt: 1, xlsx: 1, xls: 1, csv: 1, txt: 1, md: 1, rtf: 1, png: 1, jpg: 1, jpeg: 1, gif: 1, webp: 1, svg: 1, bmp: 1, heic: 1, tiff: 1 };
691
+ var allowedLabel = isT ? "テキスト・Word・PDF・字幕(.vtt/.srt)" : "PDF・Word・PowerPoint・Excel・テキスト・画像";
692
+ function extOf(name) { var m = String(name).toLowerCase().match(/\.([a-z0-9]+)$/); return m ? m[1] : ""; }
693
+ var fileInput = el("input", { type: "file", multiple: "multiple", class: "modal-file-hidden" });
694
+ var fileListEl = el("div", { class: "dropzone-files" });
695
+ var dropzone = el("div", { class: "dropzone" }, [
696
+ el("div", { class: "dropzone-text", text: "📄 ファイルをドラッグ&ドロップ、またはクリックして選択" }),
697
+ fileListEl,
698
+ fileInput,
699
+ ]);
670
700
  var nameInput = el("input", { type: "text", class: "modal-name", placeholder: "投入者として記録されます" });
671
- modal.appendChild(fieldRow("ファイル(必須)", fileInput));
701
+ modal.appendChild(fieldRow("ファイル(必須)", dropzone));
672
702
  modal.appendChild(fieldRow("お名前(必須)", nameInput));
673
703
  var status = el("div", { class: "modal-status" });
674
704
  var cancelBtn = el("button", { class: "modal-cancel", text: "閉じる" });
675
705
  var submitBtn = el("button", { class: "ops-card-btn", text: "送信" });
706
+ submitBtn.disabled = true;
676
707
  modal.appendChild(status);
677
708
  modal.appendChild(el("div", { class: "modal-actions" }, [cancelBtn, submitBtn]));
678
709
  overlay.appendChild(modal);
679
710
  document.body.appendChild(overlay);
711
+
712
+ function showStatus(msg, isError) { status.className = "modal-status" + (isError ? " modal-status-error" : ""); status.textContent = msg; }
713
+ function update() { submitBtn.disabled = !(nameInput.value.trim() && selectedFiles.length); }
714
+ function renderFiles() {
715
+ fileListEl.textContent = "";
716
+ selectedFiles.forEach(function (f, idx) {
717
+ var x = el("button", { class: "file-chip-x", type: "button", text: "×" });
718
+ x.addEventListener("click", function (e) { e.stopPropagation(); selectedFiles.splice(idx, 1); renderFiles(); update(); });
719
+ fileListEl.appendChild(el("span", { class: "file-chip" }, [el("span", { text: f.name }), x]));
720
+ });
721
+ }
722
+ function addFiles(list) {
723
+ var rejected = [];
724
+ Array.prototype.slice.call(list || []).forEach(function (f) {
725
+ var ext = extOf(f.name);
726
+ if (!ext || !allowedExt[ext]) { rejected.push(f.name); return; }
727
+ selectedFiles.push(f);
728
+ });
729
+ renderFiles();
730
+ update();
731
+ if (rejected.length) showStatus("⚠ 対応していない形式のため追加できませんでした: " + rejected.join(", ") + "(対応形式: " + allowedLabel + ")", true);
732
+ else showStatus("", false);
733
+ }
734
+ dropzone.addEventListener("click", function () { fileInput.click(); });
735
+ fileInput.addEventListener("change", function () { addFiles(fileInput.files); fileInput.value = ""; });
736
+ dropzone.addEventListener("dragover", function (e) { e.preventDefault(); dropzone.classList.add("dragover"); });
737
+ dropzone.addEventListener("dragleave", function () { dropzone.classList.remove("dragover"); });
738
+ dropzone.addEventListener("drop", function (e) { e.preventDefault(); dropzone.classList.remove("dragover"); addFiles(e.dataTransfer.files); });
739
+ nameInput.addEventListener("input", update);
740
+
680
741
  function close() { if (overlay.parentNode) document.body.removeChild(overlay); }
681
742
  cancelBtn.addEventListener("click", close);
682
743
  overlay.addEventListener("click", function (e) { if (e.target === overlay) close(); });
683
- submitBtn.addEventListener("click", function () {
684
- submitIntake(kind, fileInput.files, nameInput.value, status, submitBtn);
685
- });
744
+ submitBtn.addEventListener("click", function () { submitIntake(kind, selectedFiles, nameInput.value, status, submitBtn); });
686
745
  }
687
- async function submitIntake(kind, fileList, submitter, status, submitBtn) {
746
+ async function submitIntake(kind, files, submitter, status, submitBtn) {
688
747
  var intake = SITE.intake;
689
- var files = fileList ? Array.prototype.slice.call(fileList) : [];
690
- // 必須: お名前 ファイル
691
- if (!submitter || !submitter.trim()) { status.textContent = "⚠ お名前を入力してください。"; return; }
692
- if (!files.length) { status.textContent = "⚠ ファイルを選択してください。"; return; }
748
+ function setStatus(msg, isError) { status.className = "modal-status" + (isError ? " modal-status-error" : ""); status.textContent = msg; }
749
+ if (!submitter || !submitter.trim()) { setStatus("⚠ お名前を入力してください。", true); return; }
750
+ if (!files || !files.length) { setStatus("⚠ ファイルを選択してください。", true); return; }
693
751
  submitBtn.disabled = true;
694
752
  var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
695
- function fail(msg) { status.textContent = "✗ " + msg; submitBtn.disabled = false; }
753
+ function fail(msg) { setStatus("✗ " + msg, true); submitBtn.disabled = false; }
696
754
  try {
697
755
  var uploaded = [];
698
756
  for (var i = 0; i < files.length; i++) {
699
757
  var f = files[i];
700
- status.textContent = "アップロード中… (" + (i + 1) + "/" + files.length + ") " + f.name;
758
+ setStatus("アップロード中… (" + (i + 1) + "/" + files.length + ") " + f.name, false);
701
759
  var pres;
702
760
  try {
703
761
  pres = await fetch(pfx + "op=presign&t=" + encodeURIComponent(intake.token), {
@@ -713,7 +771,7 @@ const CLIENT_JS = `
713
771
  if (!put.ok) return fail("ファイルのアップロードに失敗しました(" + put.status + ")。ファイルのサイズ・形式をご確認ください。");
714
772
  uploaded.push({ key: pres.j.key, filename: f.name });
715
773
  }
716
- status.textContent = "取り込み中…";
774
+ setStatus("取り込み中…", false);
717
775
  var sub;
718
776
  try {
719
777
  sub = await fetch(pfx + "op=submit&t=" + encodeURIComponent(intake.token), {
@@ -722,8 +780,9 @@ const CLIENT_JS = `
722
780
  }).then(function (r) { return r.json().then(function (j) { return { ok: r.ok, j: j }; }); });
723
781
  } catch (net3) { return fail("サーバーに接続できませんでした。もう一度お試しください。"); }
724
782
  if (!sub.ok) return fail((sub.j && sub.j.message) || "取り込みに失敗しました。時間をおいて再度お試しください。");
783
+ status.className = "modal-status";
725
784
  status.textContent = "";
726
- status.appendChild(el("div", { class: "modal-ok", text: "✓ 送信しました。数分後に" + (kind === "transcript" ? "議事録" : "資料") + "が反映されます。" }));
785
+ status.appendChild(el("div", { class: "modal-ok", text: "✓ 送信しました。数分後に" + (kind === "transcript" ? "議事録が自動生成され" : "内容がMarkdownに変換されて") + "反映されます。" }));
727
786
  var paths = (sub.j && sub.j.committed) || [];
728
787
  if (paths.length && sub.j.repo) {
729
788
  var br = SITE.branch || "main";
@@ -735,6 +794,7 @@ const CLIENT_JS = `
735
794
  status.appendChild(box);
736
795
  }
737
796
  submitBtn.textContent = "完了";
797
+ submitBtn.disabled = true;
738
798
  } catch (e) {
739
799
  fail(e && e.message ? e.message : "予期しないエラーが発生しました。");
740
800
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.14",
3
+ "version": "0.4.16",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,