File size: 7,981 Bytes
3f3daa2
 
 
03479c0
 
 
 
 
8dd3dd4
03479c0
 
 
8dd3dd4
 
 
3f3daa2
03479c0
 
 
3f3daa2
 
03479c0
 
8dd3dd4
03479c0
 
 
 
 
 
 
3f3daa2
03479c0
 
8dd3dd4
 
 
d0bd0a2
8dd3dd4
 
03479c0
3f3daa2
03479c0
 
8dd3dd4
d0bd0a2
8dd3dd4
 
 
 
 
 
 
 
 
 
d0bd0a2
03479c0
3f3daa2
 
 
8dd3dd4
03479c0
 
8dd3dd4
03479c0
8dd3dd4
 
 
03479c0
 
 
 
 
3f3daa2
d0bd0a2
8dd3dd4
d0bd0a2
8dd3dd4
d0bd0a2
3f3daa2
8dd3dd4
03479c0
 
8dd3dd4
03479c0
 
 
 
 
8dd3dd4
3f3daa2
 
8dd3dd4
03479c0
 
 
 
 
 
 
 
3f3daa2
8dd3dd4
03479c0
8dd3dd4
d0bd0a2
 
8dd3dd4
 
 
 
 
 
 
 
3f3daa2
 
8dd3dd4
03479c0
 
 
8dd3dd4
03479c0
 
8dd3dd4
03479c0
 
 
8dd3dd4
 
 
d0bd0a2
03479c0
3f3daa2
8dd3dd4
 
 
 
 
 
 
 
 
03479c0
3f3daa2
d0bd0a2
 
 
 
 
 
 
8dd3dd4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f3daa2
03479c0
3f3daa2
03479c0
 
 
3f3daa2
 
d0bd0a2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>DocSite — Markdown Documentation</title>
  <!-- Tailwind via CDN -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- Heroicons -->
  <script src="https://unpkg.com/heroicons@2.0.18/dist/outline.js"></script>
  <!-- Marked.js -->
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <!-- Highlight.js -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
  <style>
    /* custom scrollbar */
    .scrollbar-thin::-webkit-scrollbar { width: 6px; }
    .scrollbar-thin::-webkit-scrollbar-thumb { background-color: rgba(100,100,100,0.4); border-radius: 3px; }
  </style>
</head>
<body class="h-screen flex overflow-hidden font-sans">
  <!-- Sidebar: File Tree -->
  <aside id="sidebar" class="w-64 bg-gray-800 text-gray-200 p-4 flex flex-col transition-transform duration-300">
    <div class="flex items-center justify-between mb-4">
      <h2 class="text-lg font-semibold">Pages</h2>
      <button id="newPageBtn" class="p-1 hover:bg-gray-700 rounded"><svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path d="M12 4v16m8-8H4"/></svg></button>
    </div>
    <ul id="fileTree" class="flex-1 overflow-auto scrollbar-thin space-y-1"></ul>
    <button id="deletePageBtn" class="mt-4 px-3 py-2 bg-red-600 hover:bg-red-700 rounded-md">Delete Page</button>
  </aside>

  <!-- Main Content: Editor & Preview -->
  <main class="flex-1 flex flex-col">
    <!-- Top Controls -->
    <div id="toolbar" class="flex items-center bg-gray-100 p-2 border-b transition-transform duration-300">
      <button id="toggleSidebarBtn" class="p-1 mr-2 hover:bg-gray-200 rounded"><svg class="w-5 h-5 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path d="M4 6h16M4 12h16M4 18h16"/></svg></button>
      <button id="toggleEditorBtn" class="p-1 mr-2 hover:bg-gray-200 rounded"><svg class="w-5 h-5 text-gray-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path d="M8 4h8v16H8z"/></svg></button>
      <input id="pageTitle" class="flex-1 px-2 py-1 border rounded focus:outline-none mr-2" placeholder="Page Title..." />
      <button id="saveBtn" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded">Save</button>
    </div>

    <!-- Editor & Preview Panes -->
    <div class="flex flex-1 overflow-hidden">
      <!-- Editor Pane -->
      <div id="editorPane" class="relative w-1/2 transition-all duration-300">
        <div id="editorToolbar" class="absolute top-2 left-2 flex space-x-1 z-10">
          <button data-action="bold" class="px-2 py-1 bg-gray-200 hover:bg-gray-300 rounded">B</button>
          <button data-action="italic" class="px-2 py-1 bg-gray-200 hover:bg-gray-300 rounded">I</button>
          <button data-action="h1" class="px-2 py-1 bg-gray-200 hover:bg-gray-300 rounded">H1</button>
          <button data-action="ul" class="px-2 py-1 bg-gray-200 hover:bg-gray-300 rounded">• List</button>
        </div>
        <textarea id="editor" class="w-full h-full p-12 pt-10 text-sm font-mono outline-none border-r resize-none scrollbar-thin" placeholder="Write Markdown..."></textarea>
      </div>

      <!-- Preview Pane -->
      <div id="previewPane" class="w-1/2 p-6 overflow-auto scrollbar-thin prose prose-indigo max-w-none transition-all duration-300"></div>
    </div>
  </main>

  <script>
    // Persistence
    let pages = JSON.parse(localStorage.getItem('docPages') || '{}');
    let currentId = null;
    const AUTO_SAVE_INTERVAL = 5000;

    // Elements
    const sidebar = document.getElementById('sidebar');
    const toolbarDiv = document.getElementById('toolbar');
    const fileTree = document.getElementById('fileTree');
    const newPageBtn = document.getElementById('newPageBtn');
    const deletePageBtn = document.getElementById('deletePageBtn');
    const saveBtn = document.getElementById('saveBtn');
    const pageTitleInput = document.getElementById('pageTitle');
    const editor = document.getElementById('editor');
    const previewPane = document.getElementById('previewPane');
    const toggleSidebarBtn = document.getElementById('toggleSidebarBtn');
    const toggleEditorBtn = document.getElementById('toggleEditorBtn');
    const editorToolbar = document.getElementById('editorToolbar');
    const editorPane = document.getElementById('editorPane');

    // Render file tree
    function renderTree() {
      fileTree.innerHTML = '';
      for (let id of Object.keys(pages)) {
        const li = document.createElement('li');
        li.textContent = pages[id].title;
        li.className = 'px-2 py-1 hover:bg-gray-700 rounded cursor-pointer ' + (id === currentId ? 'bg-gray-700' : '');
        li.onclick = () => loadPage(id);
        fileTree.append(li);
      }
    }

    // Load a page
    function loadPage(id) {
      currentId = id;
      const { title, content } = pages[id];
      pageTitleInput.value = title;
      editor.value = content;
      updatePreview();
      renderTree();
    }

    // Update preview with syntax highlighting
    function updatePreview() {
      const html = marked.parse(editor.value || '');
      previewPane.innerHTML = html;
      previewPane.querySelectorAll('pre code').forEach(block => hljs.highlightBlock(block));
    }

    // Save current page
    function savePage() {
      if (!currentId) return;
      pages[currentId] = { title: pageTitleInput.value || 'Untitled', content: editor.value };
      localStorage.setItem('docPages', JSON.stringify(pages));
      renderTree();
    }

    // Create new page
    newPageBtn.onclick = () => {
      const id = Date.now().toString();
      pages[id] = { title: 'Untitled', content: '' };
      savePage(); loadPage(id);
    };

    // Delete page
    deletePageBtn.onclick = () => {
      if (!currentId) return;
      delete pages[currentId];
      localStorage.setItem('docPages', JSON.stringify(pages));
      const ids = Object.keys(pages);
      if (ids.length) loadPage(ids[0]);
      else { currentId = null; editor.value = ''; pageTitleInput.value = ''; previewPane.innerHTML = ''; renderTree(); }
    };

    saveBtn.onclick = () => { savePage(); alert('Saved!'); };

    // Auto-save every interval
    setInterval(savePage, AUTO_SAVE_INTERVAL);

    // Toggle sidebar visibility
    toggleSidebarBtn.onclick = () => {
      sidebar.classList.toggle('-translate-x-full');
      toolbarDiv.classList.toggle('pl-0');
    };

    // Toggle editor visibility
    toggleEditorBtn.onclick = () => {
      const hidden = editorPane.classList.toggle('hidden');
      if (hidden) previewPane.classList.replace('w-1/2', 'w-full');
      else previewPane.classList.replace('w-full', 'w-1/2');
    };

    // Editor formatting toolbar
    editorToolbar.onclick = e => {
      if (!e.target.dataset.action) return;
      const action = e.target.dataset.action;
      const start = editor.selectionStart;
      const end = editor.selectionEnd;
      let selected = editor.value.slice(start, end);
      let insert = '';
      switch(action) {
        case 'bold': insert = `**${selected || 'bold'}**`; break;
        case 'italic': insert = `*${selected || 'italic'}*`; break;
        case 'h1': insert = `# ${selected || 'Heading'}`; break;
        case 'ul': insert = `- ${selected || 'List item'}`; break;
      }
      editor.setRangeText(insert, start, end, 'end');
      updatePreview();
      editor.focus();
    };

    editor.addEventListener('input', updatePreview);

    // Initialize
    renderTree();
    if (!currentId && Object.keys(pages).length) loadPage(Object.keys(pages)[0]);
  </script>
</body>
</html>