<?php
$saved_file = 'notebook.txt';
$archive_dir = '/notepad/archive/';
$export_dir = '/notepad/exports/';
$bookmarks_file = 'bookmarks.html';
// ── Helper: wrap notebook content in a standalone HTML shell ──────────────
function make_html_document(string $body_content, string $title): string {
return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{$title}</title>
<style>
body { background:#fff; color:#000; font-family:sans-serif; padding:20px; max-width:900px; margin:auto; }
.journal-entry { border-bottom:1px dashed #000; margin-bottom:25px; padding-bottom:15px; }
.journal-entry p, .journal-entry div { line-height:1.05; margin-top:2px; margin-bottom:6px; }
small { color:#000; font-family:monospace; }
</style>
</head>
<body>
{$body_content}
</body>
</html>
HTML;
}
// ── Helper: append a bookmark entry ──────────────────────────────────────
function append_bookmark(string $bookmarks_file, string $label, string $href): void {
$timestamp = (new DateTime('now', new DateTimeZone('America/Chicago')))->format('Y-m-d H:i:s');
$entry = "<li data-ts=\"{$timestamp}\"><a href=\"" . htmlspecialchars($href, ENT_QUOTES) . "\">"
. htmlspecialchars($label, ENT_QUOTES) . "</a> <small style='color:#000;'>[{$timestamp} CST]</small></li>\n";
$current = file_exists($bookmarks_file) ? file_get_contents($bookmarks_file) : "<ul id=\"bookmarks\">\n</ul>";
// Insert new entry right after the opening <ul> tag
$updated = preg_replace('/<ul id="bookmarks">\n/', "<ul id=\"bookmarks\">\n" . $entry, $current, 1);
file_put_contents($bookmarks_file, $updated);
}
// ── Helper: read the 10 most recent bookmark <li> entries ─────────────────
function get_recent_bookmarks(string $bookmarks_file, int $limit = 10): string {
if (!file_exists($bookmarks_file)) return '';
$html = file_get_contents($bookmarks_file);
preg_match_all('/<li data-ts="[^"]*">.*?<\/li>/s', $html, $matches);
if (empty($matches[0])) return '';
$items = array_slice($matches[0], 0, $limit);
return '<ul style="margin:0; padding-left:18px; font-size:1.4 em;">' . implode("\n", $items) . '</ul>';
}
// ── POST handling ─────────────────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// ACTION: Clear + archive + optional export
if (isset($_POST['action']) && $_POST['action'] === 'clear') {
if (file_exists($saved_file)) {
$tz = new DateTimeZone('America/Chicago');
$dt = new DateTime('now', $tz);
$ts = $dt->format('Y.m.d.H.i.s'); // e.g. 2026.09.21.14.22.13
$body = file_get_contents($saved_file);
// ── Auto-archive ──────────────────────────────────────────────
$archive_name = "notebook_{$ts}.html";
$archive_path = $archive_dir . $archive_name;
file_put_contents($archive_path, make_html_document($body, "Notebook archive {$ts}"));
// ── Optional export ───────────────────────────────────────────
$stem = isset($_POST['export_stem']) ? trim($_POST['export_stem']) : '';
// Sanitize: allow only alphanumeric, dash, underscore, dot
$stem = preg_replace('/[^a-zA-Z0-9_\-.]/', '', $stem);
if ($stem !== '') {
$export_name = "{$stem}.html";
$export_path = $export_dir . $export_name;
file_put_contents($export_path, make_html_document($body, $stem));
// Add bookmark — path relative to /notepad so the web link works
$web_href = "exports/{$export_name}";
append_bookmark($bookmarks_file, $stem, $web_href);
}
unlink($saved_file);
}
exit;
}
// ACTION: Append new entry
if (isset($_POST['content_html']) && trim($_POST['content_html']) !== '') {
$new_snippet = $_POST['content_html'];
$tz = new DateTimeZone('America/Chicago');
$timestamp = (new DateTime('now', $tz))->format('Y-m-d H:i:s');
$entry_wrapper = "<div class='journal-entry' style='border-bottom:1px dshed #000; margin-bottom:25px; padding-bottom:15px;'>";
$entry_wrapper .= "<small style='color:#000; font-family:monospace;'>[{$timestamp} CST]</small><br>";
$entry_wrapper .= $new_snippet;
$entry_wrapper .= "</div>";
$old_content = file_exists($saved_file) ? file_get_contents($saved_file) : '';
file_put_contents($saved_file, $entry_wrapper . $old_content);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
}
}
// ── Load display data ─────────────────────────────────────────────────────
$notebook_history = "<p style='color:#000;'>No entries logged yet. Write your first snippet above!</p>";
if (file_exists($saved_file)) {
$notebook_history = file_get_contents($saved_file);
}
$recent_bookmarks = get_recent_bookmarks($bookmarks_file);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Notebook</title>
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.css"/>
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<style>
.toastui-editor-contents,
.toastui-editor-ww-container .toastui-editor-contents p,
.toastui-editor-ww-container .toastui-editor-contents div {
color:#000; line-height:1.05 !important;
}
.notebook-render, .notebook-render p,
.journal-entry, .journal-entry p, .journal-entry div {
color:#000; line-height:1.0 !important;
margin-top:2px !important; margin-bottom:6px !important;
}
.toastui-editor-contents li, .notebook-render li, .journal-entry li {
color:#000; line-height:1.0 !important; margin-bottom:3px !important;
}
.toastui-editor-defaultUI-toolbar {
height:28px !important; padding:0 2px !important;
background-color:#fff !important;
display:flex !important; align-items:center !important; overflow:hidden !important;
}
.toastui-editor-defaultUI-toolbar .toastui-editor-toolbar-icons {
transform:scale(0.75) !important; transform-origin:center !important; margin:0 -2px !important;
}
.toastui-editor-defaultUI-toolbar .toastui-editor-toolbar-divider {
height:14px !important; margin:0 3px !important;
}
.toastui-editor-toolbar-group {
height:28px !important; display:flex !important; align-items:center !important;
}
/* Export inline prompt */
#clear-section { margin-top:10px; }
#export-row { display:none; margin-top:6px; font-size:0.85em; }
#export-row input[type="text"] {
border:1px solid #000; padding:3px 6px; font-size:1.0em; width:180px;
}
#export-row button, #clear-btn {
background:#fff; border:1px solid #000; color:#000;
padding:5px 10px; cursor:pointer; font-size:1.0em;
}
/* Bookmarks strip */
#bookmarks-strip {
font-size:1.0em; border:1px solid #ccc;
padding:8px 12px; margin-top:18px; margin-bottom:0;
background:#fafafa;
}
#bookmarks-strip summary {
cursor:pointer; font-weight:bold; font-size:1.0em; color:#444;
}
</style>
</head>
<body style="background:#fff; color:#000; font-family:sans-serif; padding:10px;">
<p style="font-size:x-small; font-style:italic; text-align:right;">
Quick notes - code snippets, ndcs, etc (no PHI, PII, financial info)
</p>
<!-- ── Entry form ─────────────────────────────────────────────── -->
<form id="notebook-form" action="index.php" method="POST">
<div id="editor"></div>
<input type="hidden" id="hidden-content-input" name="content_html">
<p style="margin-top:15px;">
<button type="button" id="save-btn"
style="background:#fff; border:1px solid #000; color:#000; padding:8px 16px; cursor:pointer; font-weight:bold;">
Add Entry
</button>
</p>
</form>
<!-- ── Clear + export section ────────────────────────────────── -->
<div id="clear-section">
<button type="button" id="clear-btn">Clear Content</button>
<!-- Inline export prompt — shown after clicking Clear -->
<div id="export-row">
<label>Export filename stem (leave blank to skip):
<input type="text" id="export-stem-input" placeholder="e.g. weekly_notes_sep21" autocomplete="off">
</label>
<button type="button" id="confirm-clear-btn">Archive & Clear</button>
<button type="button" id="cancel-clear-btn">Cancel</button>
</div>
</div>
<a href = "bookmarks.html">bookmarks.html</a>
<!-- <div> <iframe src = "bookmarks.html"> </iframe></div> -->
<!-- Hidden form submitted programmatically for clear action -->
<form id="clear-form" action="index.php" method="POST" style="display:none;">
<input type="hidden" name="action" value="clear">
<input type="hidden" id="export-stem-hidden" name="export_stem" value="">
</form>
<!-- ── Recent bookmarks ───────────────────────────────────────── -->
<?php if ($recent_bookmarks): ?>
<details id="bookmarks-strip" open>
<summary>Recent exports / bookmarks</summary>
<div style="margin-top:6px;">
<?php echo $recent_bookmarks; ?>
</div>
</details>
<?php endif; ?>
<!-- ── Notebook viewer ────────────────────────────────────────── -->
<div style="background:#fff; border:1px solid #000; padding:25px; margin-top:30px;">
<div class="notebook-render">
<?php echo $notebook_history; ?>
</div>
</div>
<script>
// ── ToastUI editor ────────────────────────────────────────────
const editor = new toastui.Editor({
el: document.querySelector('#editor'),
previewStyle: 'vertical',
height: '250px',
initialValue: '',
initialEditType: 'wysiwyg'
});
document.querySelector('#save-btn').addEventListener('click', function () {
if (editor.getMarkdown().trim() === '') {
alert('Please write something before logging it.');
return;
}
document.querySelector('#hidden-content-input').value = editor.getHTML();
document.getElementById('notebook-form').submit();
});
// ── Clear / export flow ───────────────────────────────────────
const clearBtn = document.getElementById('clear-btn');
const exportRow = document.getElementById('export-row');
const confirmClearBtn = document.getElementById('confirm-clear-btn');
const cancelClearBtn = document.getElementById('cancel-clear-btn');
const exportStemInput = document.getElementById('export-stem-input');
const exportStemHidden = document.getElementById('export-stem-hidden');
const clearForm = document.getElementById('clear-form');
// Step 1: reveal inline prompt
clearBtn.addEventListener('click', function () {
exportRow.style.display = 'block';
exportStemInput.focus();
clearBtn.style.display = 'none';
});
// Step 2a: user confirms (with or without a stem)
confirmClearBtn.addEventListener('click', function () {
exportStemHidden.value = exportStemInput.value.trim();
clearForm.submit();
});
// Step 2b: user cancels — restore original state
cancelClearBtn.addEventListener('click', function () {
exportRow.style.display = 'none';
exportStemInput.value = '';
clearBtn.style.display = 'inline-block';
});
// Allow Enter key in the stem field to confirm
exportStemInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') { e.preventDefault(); confirmClearBtn.click(); }
if (e.key === 'Escape') { cancelClearBtn.click(); }
});
</script>
</body>
</html>