diff --git a/assets/editor.js b/assets/editor.js
index 17de39c..93e4a32 100644
--- a/assets/editor.js
+++ b/assets/editor.js
@@ -58,6 +58,7 @@
var T = EditorTables;
var L = EditorLists;
var D = EditorDates;
+ var M = EditorMovie;
var actions = {
save: function () { form.submit(); },
@@ -100,6 +101,7 @@
tbldeleterow: function () { applyTableOp(T.deleteRow); },
dateiso: function () { insertAtCursor(D.isoDate()); },
datelong: function () { insertAtCursor(D.longDate()); },
+ movie: function () { M.run(textarea); },
};
// --- Keyboard shortcut registration ---
diff --git a/assets/editor/movie.js b/assets/editor/movie.js
new file mode 100644
index 0000000..491a319
--- /dev/null
+++ b/assets/editor/movie.js
@@ -0,0 +1,121 @@
+window.EditorMovie = (function () {
+ 'use strict';
+
+ // OMDb API key. Shipped to the browser; acceptable for a single-user LAN tool.
+ var OMDB_API_KEY = '';
+
+ var BEGIN = '';
+ var END = '';
+
+ function firstHeading(text) {
+ var m = text.match(/^#{1,6}\s+(.+?)\s*$/m);
+ return m ? m[1].trim() : '';
+ }
+
+ function parseTitleYear(raw) {
+ var m = raw.match(/^(.+?)\s*\((\d{4})\)\s*$/);
+ return m ? { title: m[1].trim(), year: m[2] } : { title: raw.trim(), year: null };
+ }
+
+ function safe(v) { return (!v || v === 'N/A') ? '' : String(v); }
+
+ function esc(s) {
+ return String(s)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+ }
+
+ function buildBlock(m) {
+ var out = [BEGIN, '', END);
+ return out.join('\n');
+ }
+
+ function insertOrReplace(ta, markup) {
+ var t = ta.value || '';
+ var b = t.indexOf(BEGIN);
+ var e = t.indexOf(END);
+ if (b !== -1 && e !== -1 && e > b) {
+ ta.value = t.slice(0, b) + markup + t.slice(e + END.length);
+ } else {
+ var h = t.match(/^#{1,6}\s+.+?\s*$/m);
+ if (h) {
+ var idx = t.indexOf(h[0]) + h[0].length;
+ ta.value = t.slice(0, idx) + '\n\n' + markup + t.slice(idx);
+ } else {
+ ta.value = markup + (t ? '\n\n' + t : '');
+ }
+ }
+ ta.dispatchEvent(new Event('input'));
+ }
+
+ function fetchMovie(title, year) {
+ var url = 'https://www.omdbapi.com/?apikey=' + encodeURIComponent(OMDB_API_KEY) +
+ '&type=movie&t=' + encodeURIComponent(title);
+ if (year) url += '&y=' + encodeURIComponent(year);
+ return fetch(url).then(function (r) { return r.json(); });
+ }
+
+ function showMessage(title, msg) {
+ openModal({ title: title, body: msg, confirm: { label: 'OK' } });
+ }
+
+ function run(textarea) {
+ if (!OMDB_API_KEY) {
+ showMessage('Movie import', 'OMDb API key is not set. Edit assets/editor/movie.js.');
+ return;
+ }
+
+ var input = document.createElement('input');
+ input.type = 'text';
+ input.className = 'modal-input';
+ input.placeholder = 'Title, optionally with (YYYY)';
+ input.value = firstHeading(textarea.value || '');
+
+ openModal({
+ title: 'Import movie',
+ body: input,
+ confirm: {
+ label: 'IMPORT',
+ onConfirm: function () {
+ var raw = input.value.trim();
+ if (!raw) return;
+ var parsed = parseTitleYear(raw);
+ closeModal();
+ fetchMovie(parsed.title, parsed.year)
+ .then(function (data) {
+ if (!data || data.Response === 'False') {
+ showMessage('Not found',
+ (data && data.Error) || 'Movie not found.');
+ return;
+ }
+ insertOrReplace(textarea, buildBlock(data));
+ })
+ .catch(function () {
+ showMessage('Import failed', 'OMDb lookup failed.');
+ });
+ },
+ },
+ });
+ }
+
+ return { run: run };
+})();
diff --git a/assets/page.html b/assets/page.html
index 67ecba8..d368cb7 100644
--- a/assets/page.html
+++ b/assets/page.html
@@ -62,12 +62,15 @@
+
+
+
{{else}}
{{if .Content}}
diff --git a/assets/style.css b/assets/style.css
index c03ea43..462c8cf 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -584,6 +584,31 @@ hr {
word-break: break-all;
}
+/* === Movie info box === */
+.movie-info {
+ margin: 0.75rem 0;
+}
+.movie-info::after {
+ content: "";
+ display: block;
+ clear: both;
+}
+.movie-info .movie-poster {
+ float: right;
+ max-width: 200px;
+ margin: 0 0 0.75rem 1rem;
+}
+.movie-info table {
+ width: auto;
+}
+@media (max-width: 600px) {
+ .movie-info .movie-poster {
+ float: none;
+ display: block;
+ margin: 0 auto 0.75rem;
+ }
+}
+
/* === Diary Calendar === */
.diary-cal {
position: fixed;