|
10 | 10 |
|
11 | 11 | import {setOutline} from '../lib/pdf-outliner'; |
12 | 12 | import {PDFService, type PDFState} from '../lib/pdf-service'; |
13 | | - import {tocItems, pdfService, showNumberedList} from '../stores'; |
| 13 | + import {tocItems, pdfService, type TocConfig} from '../stores'; |
14 | 14 | import {debounce} from '../lib'; |
15 | 15 |
|
| 16 | + import {tocConfig} from '../stores'; |
| 17 | +
|
| 18 | + let config: TocConfig; |
| 19 | + tocConfig.subscribe((value) => (config = value)); |
| 20 | +
|
| 21 | + function updateTocField(fieldPath, value) { |
| 22 | + tocConfig.update((cfg) => { |
| 23 | + const keys = fieldPath.split('.'); |
| 24 | + let target = cfg; |
| 25 | + keys.slice(0, -1).forEach((key) => (target = target[key])); |
| 26 | + target[keys[keys.length - 1]] = value; |
| 27 | + return cfg; |
| 28 | + }); |
| 29 | + } |
| 30 | +
|
16 | 31 | let isDragging = false; |
17 | 32 |
|
18 | 33 | let pdfState: PDFState = { |
19 | 34 | doc: null, |
20 | 35 | newDoc: null, |
| 36 | + instance: null, |
21 | 37 | filename: '', |
22 | 38 | currentPage: 1, |
23 | 39 | totalPages: 0, |
|
58 | 74 |
|
59 | 75 | const debouncedUpdatePDF = debounce(updatePDF, 300); |
60 | 76 | tocItems.subscribe(debouncedUpdatePDF); |
61 | | - showNumberedList.subscribe(debouncedUpdatePDF); |
| 77 | + tocConfig.subscribe(debouncedUpdatePDF); |
62 | 78 |
|
63 | 79 | const handleFileDrop = async (e: CustomEvent) => { |
64 | 80 | const {acceptedFiles} = e.detail; |
|
116 | 132 | <span class="text-3xl font-semibold">PDF Outliner</span> |
117 | 133 | <Logo /> |
118 | 134 | </div> |
119 | | - <div class="border-dashed border-gray-100 rounded border-2 my-4 p-2"> |
120 | | - <input |
121 | | - bind:checked={$showNumberedList} |
122 | | - type="checkbox" |
123 | | - id="show_numbered_list" |
124 | | - name="show_numbered_list" |
125 | | - /> |
126 | | - <label for="show_numbered_list">with numbered list</label> |
| 135 | + <div class="border-dashed border-gray-100 rounded border-2 p-2 my-4"> |
| 136 | + <div class="border-dashed border-gray-100 rounded border-2 my-1 p-2"> |
| 137 | + <input |
| 138 | + bind:checked={config.showNumberedList} |
| 139 | + type="checkbox" |
| 140 | + id="show_numbered_list" |
| 141 | + on:change={(e) => updateTocField('showNumberedList', e.target.checked)} |
| 142 | + /> |
| 143 | + <label for="show_numbered_list">with numbered list</label> |
| 144 | + </div> |
| 145 | + |
| 146 | + <div class="border-dashed border-gray-100 rounded border-2 my-2 p-2 flex gap-6"> |
| 147 | + <label |
| 148 | + class="whitespace-nowrap" |
| 149 | + for="page_offset">Page offset</label |
| 150 | + > |
| 151 | + <input |
| 152 | + type="number" |
| 153 | + id="page_offset" |
| 154 | + bind:value={config.pageOffset} |
| 155 | + on:input={(e) => updateTocField('pageOffset', parseInt(e.target.value, 10) || 0)} |
| 156 | + class=" w-[80%]" |
| 157 | + /> |
| 158 | + </div> |
| 159 | + |
| 160 | + <div class="flex"> |
| 161 | + <div class="w-36 inline-block mr-3"> |
| 162 | + <h3 class="my-4 font-bold">First Level</h3> |
| 163 | + |
| 164 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2 mr-2 w-32"> |
| 165 | + <label for="first_level_font_size">Font Size</label> |
| 166 | + <input |
| 167 | + type="number" |
| 168 | + id="first_level_font_size" |
| 169 | + bind:value={config.firstLevel.fontSize} |
| 170 | + on:input={(e) => updateTocField('firstLevel.fontSize', parseInt(e.target.value, 10) || 0)} |
| 171 | + class="w-[80%]" |
| 172 | + /> |
| 173 | + </div> |
| 174 | + |
| 175 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2 mr-2 w-32"> |
| 176 | + <label for="first_level_dot_leader">Dot Leader</label> |
| 177 | + <input |
| 178 | + type="text" |
| 179 | + id="first_level_dot_leader" |
| 180 | + bind:value={config.firstLevel.dotLeader} |
| 181 | + on:input={(e) => updateTocField('firstLevel.dotLeader', e.target.value)} |
| 182 | + class=" w-[80%]" |
| 183 | + /> |
| 184 | + </div> |
| 185 | + |
| 186 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2 mr-2 w-32"> |
| 187 | + <label for="first_level_color">Color</label> |
| 188 | + <input |
| 189 | + type="color" |
| 190 | + id="first_level_color" |
| 191 | + bind:value={config.firstLevel.color} |
| 192 | + on:input={(e) => updateTocField('firstLevel.color', e.target.value)} |
| 193 | + class=" w-[80%]" |
| 194 | + /> |
| 195 | + </div> |
| 196 | + |
| 197 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2 mr-2 w-32"> |
| 198 | + <label for="first_level_line_spacing">Spacing</label> |
| 199 | + <input |
| 200 | + type="number" |
| 201 | + step="0.1" |
| 202 | + id="first_level_line_spacing" |
| 203 | + bind:value={config.firstLevel.lineSpacing} |
| 204 | + on:input={(e) => updateTocField('firstLevel.lineSpacing', parseFloat(e.target.value) || 1)} |
| 205 | + class=" w-[80%]" |
| 206 | + /> |
| 207 | + </div> |
| 208 | + </div> |
| 209 | + <div class="w-36 inline-block"> |
| 210 | + <h3 class="my-4 font-bold">Other Levels</h3> |
| 211 | + |
| 212 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2"> |
| 213 | + <label for="other_levels_font_size">Font Size</label> |
| 214 | + <input |
| 215 | + type="number" |
| 216 | + id="other_levels_font_size" |
| 217 | + bind:value={config.otherLevels.fontSize} |
| 218 | + on:input={(e) => updateTocField('otherLevels.fontSize', parseInt(e.target.value, 10) || 0)} |
| 219 | + class=" w-[80%]" |
| 220 | + /> |
| 221 | + </div> |
| 222 | + |
| 223 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2"> |
| 224 | + <label for="other_levels_dot_leader">Dot Leader</label> |
| 225 | + <input |
| 226 | + type="text" |
| 227 | + id="other_levels_dot_leader" |
| 228 | + bind:value={config.otherLevels.dotLeader} |
| 229 | + on:input={(e) => updateTocField('otherLevels.dotLeader', e.target.value)} |
| 230 | + class=" w-[80%]" |
| 231 | + /> |
| 232 | + </div> |
| 233 | + |
| 234 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2"> |
| 235 | + <label for="other_levels_color">Color</label> |
| 236 | + <input |
| 237 | + type="color" |
| 238 | + id="other_levels_color" |
| 239 | + bind:value={config.otherLevels.color} |
| 240 | + on:input={(e) => updateTocField('otherLevels.color', e.target.value)} |
| 241 | + class=" w-[80%]" |
| 242 | + /> |
| 243 | + </div> |
| 244 | + |
| 245 | + <div class="border-dashed border-gray-100 rounded border-2 my-3 p-2"> |
| 246 | + <label for="other_levels_line_spacing">Spacing</label> |
| 247 | + <input |
| 248 | + type="number" |
| 249 | + step="0.1" |
| 250 | + id="other_levels_line_spacing" |
| 251 | + bind:value={config.otherLevels.lineSpacing} |
| 252 | + on:input={(e) => updateTocField('otherLevels.lineSpacing', parseFloat(e.target.value) || 1)} |
| 253 | + class=" w-[80%]" |
| 254 | + /> |
| 255 | + </div> |
| 256 | + </div> |
| 257 | + </div> |
127 | 258 | </div> |
128 | 259 | <TocEditor /> |
129 | 260 | </div> |
130 | 261 | <div class="flex flex-col flex-1"> |
131 | | - <div class="relative h-fit pb-8 min-h-[85vh]"> |
| 262 | + <div class="h-fit pb-8 min-h-[85vh] top-5 sticky"> |
132 | 263 | <Dropzone |
133 | 264 | containerClasses={pdfState.instance ? '' : 'h-full'} |
134 | 265 | accept=".pdf" |
|
0 commit comments