@takagaki/cortex-decisions-viewer 0.4.14 → 0.4.15

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 +59 -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,64 @@ 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
+ ? "文字起こしファイル(.txt / .docx 等)を選んで送信してください。(送信後、数分で議事録が自動生成されます)"
685
+ : "資料ファイル(PDF / PPT / Excel 等)を選んで送信してください。(送信後、数分でファイルの内容が Markdown に変換されて取り込まれます)" }));
686
+
687
+ var selectedFiles = [];
688
+ var fileInput = el("input", { type: "file", multiple: "multiple", class: "modal-file-hidden" });
689
+ var fileListEl = el("div", { class: "dropzone-files" });
690
+ var dropzone = el("div", { class: "dropzone" }, [
691
+ el("div", { class: "dropzone-text", text: "📄 ファイルをドラッグ&ドロップ、またはクリックして選択" }),
692
+ fileListEl,
693
+ fileInput,
694
+ ]);
670
695
  var nameInput = el("input", { type: "text", class: "modal-name", placeholder: "投入者として記録されます" });
671
- modal.appendChild(fieldRow("ファイル(必須)", fileInput));
696
+ modal.appendChild(fieldRow("ファイル(必須)", dropzone));
672
697
  modal.appendChild(fieldRow("お名前(必須)", nameInput));
673
698
  var status = el("div", { class: "modal-status" });
674
699
  var cancelBtn = el("button", { class: "modal-cancel", text: "閉じる" });
675
700
  var submitBtn = el("button", { class: "ops-card-btn", text: "送信" });
701
+ submitBtn.disabled = true;
676
702
  modal.appendChild(status);
677
703
  modal.appendChild(el("div", { class: "modal-actions" }, [cancelBtn, submitBtn]));
678
704
  overlay.appendChild(modal);
679
705
  document.body.appendChild(overlay);
706
+
707
+ function update() { submitBtn.disabled = !(nameInput.value.trim() && selectedFiles.length); }
708
+ function renderFiles() {
709
+ fileListEl.textContent = "";
710
+ selectedFiles.forEach(function (f, idx) {
711
+ var x = el("button", { class: "file-chip-x", type: "button", text: "×" });
712
+ x.addEventListener("click", function (e) { e.stopPropagation(); selectedFiles.splice(idx, 1); renderFiles(); update(); });
713
+ fileListEl.appendChild(el("span", { class: "file-chip" }, [el("span", { text: f.name }), x]));
714
+ });
715
+ }
716
+ function addFiles(list) { Array.prototype.slice.call(list || []).forEach(function (f) { selectedFiles.push(f); }); renderFiles(); update(); }
717
+ dropzone.addEventListener("click", function () { fileInput.click(); });
718
+ fileInput.addEventListener("change", function () { addFiles(fileInput.files); fileInput.value = ""; });
719
+ dropzone.addEventListener("dragover", function (e) { e.preventDefault(); dropzone.classList.add("dragover"); });
720
+ dropzone.addEventListener("dragleave", function () { dropzone.classList.remove("dragover"); });
721
+ dropzone.addEventListener("drop", function (e) { e.preventDefault(); dropzone.classList.remove("dragover"); addFiles(e.dataTransfer.files); });
722
+ nameInput.addEventListener("input", update);
723
+
680
724
  function close() { if (overlay.parentNode) document.body.removeChild(overlay); }
681
725
  cancelBtn.addEventListener("click", close);
682
726
  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
- });
727
+ submitBtn.addEventListener("click", function () { submitIntake(kind, selectedFiles, nameInput.value, status, submitBtn); });
686
728
  }
687
- async function submitIntake(kind, fileList, submitter, status, submitBtn) {
729
+ async function submitIntake(kind, files, submitter, status, submitBtn) {
688
730
  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; }
731
+ function setStatus(msg, isError) { status.className = "modal-status" + (isError ? " modal-status-error" : ""); status.textContent = msg; }
732
+ if (!submitter || !submitter.trim()) { setStatus("⚠ お名前を入力してください。", true); return; }
733
+ if (!files || !files.length) { setStatus("⚠ ファイルを選択してください。", true); return; }
693
734
  submitBtn.disabled = true;
694
735
  var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
695
- function fail(msg) { status.textContent = "✗ " + msg; submitBtn.disabled = false; }
736
+ function fail(msg) { setStatus("✗ " + msg, true); submitBtn.disabled = false; }
696
737
  try {
697
738
  var uploaded = [];
698
739
  for (var i = 0; i < files.length; i++) {
699
740
  var f = files[i];
700
- status.textContent = "アップロード中… (" + (i + 1) + "/" + files.length + ") " + f.name;
741
+ setStatus("アップロード中… (" + (i + 1) + "/" + files.length + ") " + f.name, false);
701
742
  var pres;
702
743
  try {
703
744
  pres = await fetch(pfx + "op=presign&t=" + encodeURIComponent(intake.token), {
@@ -713,7 +754,7 @@ const CLIENT_JS = `
713
754
  if (!put.ok) return fail("ファイルのアップロードに失敗しました(" + put.status + ")。ファイルのサイズ・形式をご確認ください。");
714
755
  uploaded.push({ key: pres.j.key, filename: f.name });
715
756
  }
716
- status.textContent = "取り込み中…";
757
+ setStatus("取り込み中…", false);
717
758
  var sub;
718
759
  try {
719
760
  sub = await fetch(pfx + "op=submit&t=" + encodeURIComponent(intake.token), {
@@ -722,8 +763,9 @@ const CLIENT_JS = `
722
763
  }).then(function (r) { return r.json().then(function (j) { return { ok: r.ok, j: j }; }); });
723
764
  } catch (net3) { return fail("サーバーに接続できませんでした。もう一度お試しください。"); }
724
765
  if (!sub.ok) return fail((sub.j && sub.j.message) || "取り込みに失敗しました。時間をおいて再度お試しください。");
766
+ status.className = "modal-status";
725
767
  status.textContent = "";
726
- status.appendChild(el("div", { class: "modal-ok", text: "✓ 送信しました。数分後に" + (kind === "transcript" ? "議事録" : "資料") + "が反映されます。" }));
768
+ status.appendChild(el("div", { class: "modal-ok", text: "✓ 送信しました。数分後に" + (kind === "transcript" ? "議事録が自動生成され" : "内容がMarkdownに変換されて") + "反映されます。" }));
727
769
  var paths = (sub.j && sub.j.committed) || [];
728
770
  if (paths.length && sub.j.repo) {
729
771
  var br = SITE.branch || "main";
@@ -735,6 +777,7 @@ const CLIENT_JS = `
735
777
  status.appendChild(box);
736
778
  }
737
779
  submitBtn.textContent = "完了";
780
+ submitBtn.disabled = true;
738
781
  } catch (e) {
739
782
  fail(e && e.message ? e.message : "予期しないエラーが発生しました。");
740
783
  }
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.15",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,