windmill-components 1.430.6 → 1.434.1

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 (37) hide show
  1. package/package/components/AppConnectInner.svelte +27 -2
  2. package/package/components/ConfirmButton.svelte +31 -0
  3. package/package/components/ConfirmButton.svelte.d.ts +20 -0
  4. package/package/components/DiffEditor.svelte +0 -6
  5. package/package/components/FlowStatusViewerInner.svelte +74 -49
  6. package/package/components/InstanceSettings.svelte +20 -1
  7. package/package/components/Login.svelte +31 -6
  8. package/package/components/ServiceLogsInner.svelte +365 -337
  9. package/package/components/apps/components/buttons/AppSchemaForm.svelte +1 -1
  10. package/package/components/apps/components/display/AppNavbarItem.svelte +1 -1
  11. package/package/components/apps/components/helpers/RunnableComponent.svelte +2 -2
  12. package/package/components/apps/components/inputs/AppS3FileInput.svelte +1 -1
  13. package/package/components/apps/editor/AppEditor.svelte +18 -8
  14. package/package/components/apps/editor/AppEditor.svelte.d.ts +5 -0
  15. package/package/components/apps/editor/AppEditorHeader.svelte +61 -61
  16. package/package/components/apps/editor/AppEditorHeader.svelte.d.ts +2 -0
  17. package/package/components/apps/editor/AppPreview.svelte +6 -1
  18. package/package/components/apps/editor/AppReportsDrawer.svelte +3 -613
  19. package/package/components/apps/editor/AppReportsDrawerInner.svelte +622 -0
  20. package/package/components/apps/editor/AppReportsDrawerInner.svelte.d.ts +17 -0
  21. package/package/components/apps/editor/component/components.d.ts +79 -79
  22. package/package/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte +1 -1
  23. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +1 -1
  24. package/package/components/apps/editor/settingsPanel/GridNavbar.svelte +1 -1
  25. package/package/components/apps/types.d.ts +1 -1
  26. package/package/components/graph/FlowGraphV2.svelte +11 -1
  27. package/package/components/graph/graphBuilder.js +1 -0
  28. package/package/components/splitPanes/SplitPanesOrColumnOnMobile.svelte +34 -0
  29. package/package/components/splitPanes/SplitPanesOrColumnOnMobile.svelte.d.ts +23 -0
  30. package/package/components/wizards/AppPicker.svelte +4 -4
  31. package/package/gen/core/OpenAPI.js +1 -1
  32. package/package/gen/schemas.gen.d.ts +2 -2
  33. package/package/gen/schemas.gen.js +2 -2
  34. package/package/gen/services.gen.d.ts +0 -1
  35. package/package/gen/services.gen.js +0 -2
  36. package/package/gen/types.gen.d.ts +2 -4
  37. package/package.json +1 -1
@@ -1,5 +1,4 @@
1
1
  <script>import { IndexSearchService, ServiceLogsService } from '../gen';
2
- import { Pane, Splitpanes } from 'svelte-splitpanes';
3
2
  import ManuelDatePicker from './runs/ManuelDatePicker.svelte';
4
3
  import CalendarPicker from './common/calendarPicker/CalendarPicker.svelte';
5
4
  import LogViewer from './LogViewer.svelte';
@@ -13,7 +12,7 @@ import { Button, Drawer, DrawerContent } from './common';
13
12
  import ClipboardCopy from 'lucide-svelte/icons/clipboard-copy';
14
13
  import AnsiUp from 'ansi_up';
15
14
  import { scroll_into_view_if_needed_polyfill } from './multiselect/utils';
16
- import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte';
15
+ import SplitPanesOrColumnOnMobile from './splitPanes/SplitPanesOrColumnOnMobile.svelte';
17
16
  export let searchTerm;
18
17
  export let queryParseErrors = [];
19
18
  let minTs = undefined;
@@ -238,11 +237,13 @@ const debouncePeriod = 400;
238
237
  let loadingLogs = false;
239
238
  let loadingLogCounts = false;
240
239
  let countsPerHost;
240
+ let sumOtherDocCount = 0;
241
241
  async function searchLogs(searchTerm, selected, minTs, maxTs, allLogs) {
242
242
  if (searchTerm.trim() === '') {
243
243
  debounceTimeout && clearTimeout(debounceTimeout);
244
244
  logs = undefined;
245
245
  countsPerHost = undefined;
246
+ sumOtherDocCount = 0;
246
247
  loadingLogs = false;
247
248
  loadingLogCounts = false;
248
249
  return;
@@ -258,7 +259,9 @@ async function searchLogs(searchTerm, selected, minTs, maxTs, allLogs) {
258
259
  minTs,
259
260
  maxTs
260
261
  });
261
- const buckets = countLogsResponse.count_per_host['count_per_host']['buckets'];
262
+ const res = countLogsResponse.count_per_host['count_per_host'];
263
+ const buckets = res['buckets'];
264
+ sumOtherDocCount = res['sum_other_doc_count'];
262
265
  countsPerHost = new Map(buckets.map(({ key, doc_count }) => [key, doc_count]));
263
266
  countsPerHost = buckets.reduce((acc, { key, doc_count }) => {
264
267
  acc[key] = { doc_count };
@@ -297,6 +300,26 @@ async function seeLogContext(lineNumber, path, hostname, jsonFmt) {
297
300
  scroll_into_view_if_needed_polyfill(el, false);
298
301
  }
299
302
  $: searchLogs(searchTerm, selected, minTsManual, maxTsManual, allLogs);
303
+ function allLogsOrQueryResults(allLogs, countsPerHost) {
304
+ if (countsPerHost == undefined) {
305
+ return allLogs;
306
+ }
307
+ let ret = {};
308
+ for (const hk of Object.keys(countsPerHost)) {
309
+ let u = hk.split(",");
310
+ let [mode, wg, hn] = [u[0], u[1], u[2]];
311
+ if (!ret[mode]) {
312
+ ret[mode] = {};
313
+ }
314
+ if (!ret[mode][wg]) {
315
+ ret[mode][wg] = {};
316
+ }
317
+ if (!ret[mode][wg][hn]) {
318
+ ret[mode][wg][hn] = [];
319
+ }
320
+ }
321
+ return ret;
322
+ }
300
323
  </script>
301
324
 
302
325
  <Drawer bind:this={logDrawer} bind:open={logDrawerOpen} size="1400px">
@@ -325,366 +348,371 @@ $: searchLogs(searchTerm, selected, minTsManual, maxTsManual, allLogs);
325
348
  </DrawerContent>
326
349
  </Drawer>
327
350
 
328
- <SplitPanesWrapper class="hidden md:block">
329
- <Splitpanes>
330
- <Pane size={30} minSize={25}>
331
- <div class="p-1">
332
- <div
333
- class="flex flex-col lg:flex-row gap-y-1 justify-between w-full relative pb-4 gap-x-0.5"
334
- id="service-logs-date-pickers"
335
- >
336
- <div class="flex relative">
337
- <input
338
- type="text"
339
- value={minTsManual
340
- ? new Date(minTsManual).toLocaleTimeString([], {
341
- day: '2-digit',
342
- month: '2-digit',
343
- hour: '2-digit',
344
- minute: '2-digit'
345
- })
346
- : 'min datetime'}
347
- disabled
348
- />
349
- <CalendarPicker
350
- label="min datetime"
351
- date={minTsManual}
352
- on:change={({ detail }) => {
353
- minTs = undefined
354
- maxTs = undefined
355
- allLogs = undefined
356
- minTsManual = detail
357
- getAllLogs(minTsManual, maxTsManual)
358
- }}
359
- />
360
- </div>
361
- <ManuelDatePicker
362
- bind:minTs={minTsManual}
363
- bind:maxTs={maxTsManual}
364
- bind:this={manualPicker}
365
- {loading}
366
- on:loadJobs={() => {
351
+ <SplitPanesOrColumnOnMobile>
352
+ <svelte:fragment slot="left-pane">
353
+ <div class="p-1">
354
+ <div
355
+ class="flex flex-col lg:flex-row gap-y-1 justify-between w-full relative pb-4 gap-x-0.5"
356
+ id="service-logs-date-pickers"
357
+ >
358
+ <div class="flex relative">
359
+ <input
360
+ type="text"
361
+ value={minTsManual
362
+ ? new Date(minTsManual).toLocaleTimeString([], {
363
+ day: '2-digit',
364
+ month: '2-digit',
365
+ hour: '2-digit',
366
+ minute: '2-digit'
367
+ })
368
+ : 'min datetime'}
369
+ disabled
370
+ />
371
+ <CalendarPicker
372
+ label="min datetime"
373
+ date={minTsManual}
374
+ on:change={({ detail }) => {
367
375
  minTs = undefined
368
376
  maxTs = undefined
369
377
  allLogs = undefined
378
+ minTsManual = detail
370
379
  getAllLogs(minTsManual, maxTsManual)
371
380
  }}
372
- serviceLogsChoices
373
- loadText={searchTerm === '' ? 'Last 1000 logfiles' : 'All time'}
381
+ placement="top-start"
374
382
  />
375
- <div class="flex relative">
376
- <input
377
- type="text"
378
- value={maxTsManual
379
- ? new Date(maxTsManual).toLocaleTimeString([], {
380
- day: '2-digit',
381
- month: '2-digit',
382
- hour: '2-digit',
383
- minute: '2-digit'
384
- })
385
- : 'max datetime'}
386
- disabled
387
- />
388
- <CalendarPicker
389
- label="max datetime"
390
- date={maxTsManual}
391
- on:change={({ detail }) => {
392
- minTs = undefined
393
- maxTs = undefined
394
- allLogs = undefined
395
- maxTsManual = detail
396
- getAllLogs(minTsManual, maxTsManual)
397
- }}
398
- />
399
- </div>
400
383
  </div>
401
- <div class="flex w-full flex-row-reverse pb-4 -mt-2 gap-2"
402
- ><Toggle
403
- size="xs"
404
- bind:checked={withError}
405
- options={{ right: 'errors > 0' }}
406
- on:change={() => {
384
+ <ManuelDatePicker
385
+ bind:minTs={minTsManual}
386
+ bind:maxTs={maxTsManual}
387
+ bind:this={manualPicker}
388
+ {loading}
389
+ on:loadJobs={() => {
390
+ minTs = undefined
391
+ maxTs = undefined
392
+ allLogs = undefined
393
+ getAllLogs(minTsManual, maxTsManual)
394
+ }}
395
+ serviceLogsChoices
396
+ loadText={searchTerm === '' ? 'Last 1000 logfiles' : 'All time'}
397
+ />
398
+ <div class="flex relative">
399
+ <input
400
+ type="text"
401
+ value={maxTsManual
402
+ ? new Date(maxTsManual).toLocaleTimeString([], {
403
+ day: '2-digit',
404
+ month: '2-digit',
405
+ hour: '2-digit',
406
+ minute: '2-digit'
407
+ })
408
+ : 'max datetime'}
409
+ disabled
410
+ />
411
+ <CalendarPicker
412
+ label="max datetime"
413
+ date={maxTsManual}
414
+ on:change={({ detail }) => {
415
+ minTs = undefined
416
+ maxTs = undefined
407
417
  allLogs = undefined
408
- getAllLogs(minTs, maxTs)
418
+ maxTsManual = detail
419
+ getAllLogs(minTsManual, maxTsManual)
409
420
  }}
410
421
  />
411
- <Toggle
412
- size="xs"
413
- bind:checked={autoRefresh}
414
- disabled={searchTerm != ''}
415
- on:change={(e) => {
416
- if (e.detail) {
417
- getAllLogs(maxTs, undefined)
418
- } else {
419
- timeout && clearTimeout(timeout)
420
- }
421
- }}
422
- options={{ right: 'auto-refresh' }}
423
- /></div
424
- >
425
- {#if allLogs == undefined}
426
- <div class="text-center pb-2"><Loader2 class="animate-spin" /></div>
427
- {:else if Object.keys(allLogs).length == 0}
428
- <div class="flex justify-center items-center h-full">No logs</div>
429
- {:else if minTs && maxTs}
430
- {@const minTsN = new Date(minTs).getTime()}
431
- {@const maxTsN = new Date(maxTs).getTime()}
432
- {@const diff = maxTsN - minTsN}
433
- {#if searchTerm === ''}
434
- <div class="flex w-full text-2xs text-tertiary pb-6">
435
- <div style="width: 60px;" />
422
+ </div>
423
+ </div>
424
+ <div class="flex w-full flex-row-reverse pb-4 -mt-2 gap-2"
425
+ ><Toggle
426
+ size="xs"
427
+ bind:checked={withError}
428
+ options={{ right: 'errors > 0' }}
429
+ on:change={() => {
430
+ allLogs = undefined
431
+ getAllLogs(minTs, maxTs)
432
+ }}
433
+ />
434
+ <Toggle
435
+ size="xs"
436
+ bind:checked={autoRefresh}
437
+ disabled={searchTerm != ''}
438
+ on:change={(e) => {
439
+ if (e.detail) {
440
+ getAllLogs(maxTs, undefined)
441
+ } else {
442
+ timeout && clearTimeout(timeout)
443
+ }
444
+ }}
445
+ options={{ right: 'auto-refresh' }}
446
+ /></div
447
+ >
448
+ {#if allLogs == undefined}
449
+ <div class="text-center pb-2"><Loader2 class="animate-spin" /></div>
450
+ {:else if Object.keys(allLogs).length == 0}
451
+ <div class="flex justify-center items-center h-full">No logs</div>
452
+ {:else if minTs && maxTs}
453
+ {@const minTsN = new Date(minTs).getTime()}
454
+ {@const maxTsN = new Date(maxTs).getTime()}
455
+ {@const diff = maxTsN - minTsN}
456
+ {#if searchTerm === ''}
457
+ <div class="flex w-full text-2xs text-tertiary pb-6">
458
+ <div style="width: 60px;" />
436
459
 
437
- <div class="flex justify-between w-full"
438
- ><div
439
- >{new Date(minTs).toLocaleTimeString([], {
440
- day: '2-digit',
441
- month: '2-digit',
442
- hour: '2-digit',
443
- minute: '2-digit'
444
- })}</div
445
- ><div
446
- >{new Date(maxTs).toLocaleTimeString([], {
447
- day: '2-digit',
448
- month: '2-digit',
449
- hour: '2-digit',
450
- minute: '2-digit'
451
- })}</div
452
- ></div
453
- >
454
- </div>
455
- {/if}
456
- {#each Object.entries(allLogs) as [mode, o1]}
457
- <div class="w-full pb-8">
458
- <h2 class="pb-2 text-2xl">{mode}s</h2>
459
- {#each Object.entries(o1) as [wg, o2]}
460
- <div class="w-full px-1">
461
- {#if wg && wg != ''}
462
- <h4 class="pt-4">{wg}</h4>
463
- {/if}
464
- <div class="divide-y flex flex-col">
465
- {#each Object.entries(o2).filter(([hn, files]) => {
466
- if (selected && selected[0] === mode && selected[1] === wg && selected[2] === hn) {
467
- return true
468
- }
469
- const hostKey = `${mode},${wg},${hn}`
470
- if (countsPerHost && (countsPerHost[hostKey] == undefined || countsPerHost[hostKey].doc_count === 0)) {
471
- return false
472
- }
460
+ <div class="flex justify-between w-full"
461
+ ><div
462
+ >{new Date(minTs).toLocaleTimeString([], {
463
+ day: '2-digit',
464
+ month: '2-digit',
465
+ hour: '2-digit',
466
+ minute: '2-digit'
467
+ })}</div
468
+ ><div
469
+ >{new Date(maxTs).toLocaleTimeString([], {
470
+ day: '2-digit',
471
+ month: '2-digit',
472
+ hour: '2-digit',
473
+ minute: '2-digit'
474
+ })}</div
475
+ ></div
476
+ >
477
+ </div>
478
+ {/if}
479
+ {#each Object.entries(allLogsOrQueryResults(allLogs, countsPerHost)) as [mode, o1]}
480
+ <div class="w-full pb-8">
481
+ <h2 class="pb-2 text-2xl">{mode}s</h2>
482
+ {#each Object.entries(o1) as [wg, o2]}
483
+ <div class="w-full px-1">
484
+ {#if wg && wg != ''}
485
+ <h4 class="pt-4">{wg}</h4>
486
+ {/if}
487
+ <div class="divide-y flex flex-col">
488
+ {#each Object.entries(o2).filter(([hn, files]) => {
489
+ if (selected && selected[0] === mode && selected[1] === wg && selected[2] === hn) {
473
490
  return true
474
- }) as [hn, files]}
475
- {@const hostKey = `${mode},${wg},${hn}`}
476
- <!-- svelte-ignore a11y-click-events-have-key-events -->
477
- <!-- svelte-ignore a11y-no-static-element-interactions -->
491
+ }
492
+ const hostKey = `${mode},${wg},${hn}`
493
+ if (countsPerHost && (countsPerHost[hostKey] == undefined || countsPerHost[hostKey].doc_count === 0)) {
494
+ return false
495
+ }
496
+ return true
497
+ }) as [hn, files]}
498
+ {@const hostKey = `${mode},${wg},${hn}`}
499
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
500
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
501
+ <div
502
+ class="w-full flex items-baseline justify-between rounded px-1 hover:bg-surface-hover cursor-pointer {selected &&
503
+ selected[0] == mode &&
504
+ selected[1] == wg &&
505
+ selected[2] == hn
506
+ ? 'bg-surface-secondary'
507
+ : ''}"
508
+ on:click={() => {
509
+ selected = [mode, wg, hn]
510
+ upToIsLatest = true
511
+ upTo = getLatestUpTo(selected)
512
+ scrollToBottom()
513
+ }}
514
+ >
478
515
  <div
479
- class="w-full flex items-baseline justify-between rounded px-1 hover:bg-surface-hover cursor-pointer {selected &&
480
- selected[0] == mode &&
481
- selected[1] == wg &&
482
- selected[2] == hn
483
- ? 'bg-surface-secondary'
484
- : ''}"
485
- on:click={() => {
486
- selected = [mode, wg, hn]
487
- upToIsLatest = true
488
- upTo = getLatestUpTo(selected)
489
- scrollToBottom()
490
- }}
516
+ class="text-sm pt-2 pl-0.5 whitespace-nowrap"
517
+ title={hn}
518
+ style="width: 90px;">{truncateRev(hn, 8)}</div
491
519
  >
492
- <div
493
- class="text-sm pt-2 pl-0.5 whitespace-nowrap"
494
- title={hn}
495
- style="width: 90px;">{truncateRev(hn, 8)}</div
496
- >
497
- {#if loadingLogCounts}
498
- <Loader2 size={15} class="animate-spin" />
499
- {:else if countsPerHost}
500
- <div class="text-tertiary text-xs">
501
- {countsPerHost[hostKey]?.doc_count ?? 0} matches
502
- </div>
503
- {:else}
504
- <div class="relative grow h-8 mr-2">
505
- {#each files as file}
506
- {@const okHeight = 100.0 * ((file.ok_lines * 1.0) / (max_lines ?? 1))}
507
- {@const errHeight =
508
- 100.0 * ((file.err_lines * 1.0) / (max_lines ?? 1))}
509
- <div
510
- class=" w-2 bg-red-400 absolute"
511
- style="left: {((file.ts - minTsN) / diff) *
512
- 100}%; height: {errHeight}%; bottom: {okHeight}%;"
513
- />
514
- <div
515
- class="w-2 bg-surface-secondary-inverse absolute bottom-0"
516
- style="left: {((file.ts - minTsN) / diff) *
517
- 100}%; height: {okHeight}%"
518
- />
519
- {/each}
520
- </div>
521
- {/if}
522
- </div>
523
- {/each}
524
- </div>
525
- </div>
526
- {/each}
527
- </div>
528
- {/each}
529
- {/if}
530
- </div>
531
- </Pane>
532
- <Pane size={70} minSize={25}
533
- ><div class="relative h-full flex flex-col gap-1"
534
- ><div class="w-full bg-surface-primary-inverse text-tertiary text-xs text-center"
535
- >1 min delay: logs are compacted before being available</div
536
- >
537
- {#if selected}
538
- <div class="grow overflow-auto" id="logviewer">
539
- {#if loadingLogs}
540
- <div class="flex w-full justify-center items-center h-48">
541
- <div class="text-tertiary text-center">
542
- <Loader2 size={34} class="animate-spin" />
520
+ {#if loadingLogCounts}
521
+ <Loader2 size={15} class="animate-spin" />
522
+ {:else if countsPerHost}
523
+ <div class="text-tertiary text-xs">
524
+ {countsPerHost[hostKey]?.doc_count ?? 0} matches
525
+ </div>
526
+ {:else}
527
+ <div class="relative grow h-8 mr-2">
528
+ {#each files as file}
529
+ {@const okHeight = 100.0 * ((file.ok_lines * 1.0) / (max_lines ?? 1))}
530
+ {@const errHeight = 100.0 * ((file.err_lines * 1.0) / (max_lines ?? 1))}
531
+ <div
532
+ class=" w-2 bg-red-400 absolute"
533
+ style="left: {((file.ts - minTsN) / diff) *
534
+ 100}%; height: {errHeight}%; bottom: {okHeight}%;"
535
+ />
536
+ <div
537
+ class="w-2 bg-surface-secondary-inverse absolute bottom-0"
538
+ style="left: {((file.ts - minTsN) / diff) *
539
+ 100}%; height: {okHeight}%"
540
+ />
541
+ {/each}
542
+ </div>
543
+ {/if}
544
+ </div>
545
+ {/each}
543
546
  </div>
544
547
  </div>
545
- {:else if logs != undefined}
546
- <div class="flex flex-col min-w-full w-fit">
547
- {#each logs.hits as { snippet_fragment, snippet_highlighted, document }}
548
- <LogSnippetViewer
549
- content={snippet_fragment || document.logs[0]}
550
- highlighted={snippet_highlighted}
551
- on:click={() => {
552
- let logLineNumber = document.line_number[0]
553
- let logFile = document.file_name[0]
554
- let host = document.host[0]
555
- let jsonFmt = document.json_fmt[0]
556
- seeLogContext(logLineNumber, logFile, host, jsonFmt)
557
- }}
558
- />
559
- {/each}
560
- {#if logs.hits.length === 0}
561
- <div class="text-center py-20 text-bold text-xl text-tertiary"> No logs </div>
562
- {/if}
563
- {#if logs.hits.length === 30}
564
- <div class="pl-6 py-6 text-sm text-secondary">
565
- Older matches were truncated from this search, try refining your filters to get
566
- more precise results.
567
- </div>
568
- {/if}
569
- <div class="py-20" />
548
+ {/each}
549
+ </div>
550
+ {/each}
551
+ {#if !loadingLogCounts && sumOtherDocCount != 0}
552
+ <div class="text-tertiary italic text-sm">
553
+ Note: {sumOtherDocCount} additional matches weren't grouped into any of the above hosts.
554
+ </div>
555
+ {/if}
556
+ {/if}
557
+ </div>
558
+ </svelte:fragment>
559
+ <svelte:fragment slot="right-pane">
560
+ <div class="relative h-full flex flex-col gap-1 pb-2">
561
+ {#if selected}
562
+ {#if !loadingLogs && logs == undefined}
563
+ <div class="w-full bg-surface-primary-inverse text-tertiary text-xs text-center">
564
+ 1 min delay: logs are compacted before being available
565
+ </div>
566
+ {/if}
567
+ <div class="grow overflow-auto" id="logviewer">
568
+ {#if loadingLogs}
569
+ <div class="flex w-full justify-center items-center h-48">
570
+ <div class="text-tertiary text-center">
571
+ <Loader2 size={34} class="animate-spin" />
570
572
  </div>
571
- {:else}
572
- {#each getLogs(selected, upTo) as file}
573
- <div
574
- style="min-height: {logsContent[file.file_path]
575
- ? 10
576
- : (file.ok_lines + file.err_lines) / 20}px;"
573
+ </div>
574
+ {:else if logs != undefined}
575
+ <div class="flex flex-col min-w-full w-fit">
576
+ {#each logs.hits as { snippet_fragment, snippet_highlighted, document }}
577
+ <LogSnippetViewer
578
+ content={snippet_fragment || document.logs[0]}
579
+ highlighted={snippet_highlighted}
580
+ on:click={() => {
581
+ let logLineNumber = document.line_number[0]
582
+ let logFile = document.file_name[0]
583
+ let host = document.host[0]
584
+ let jsonFmt = document.json_fmt[0]
585
+ seeLogContext(logLineNumber, logFile, host, jsonFmt)
586
+ }}
587
+ />
588
+ {/each}
589
+ {#if logs.hits.length === 0}
590
+ <div class="text-center py-20 text-bold text-xl text-tertiary"> No logs </div>
591
+ {/if}
592
+ {#if logs.hits.length === 1000}
593
+ <div class="pl-6 py-6 text-sm text-secondary">
594
+ Older matches were truncated from this search, try refining your filters to get
595
+ more precise results.
596
+ </div>
597
+ {/if}
598
+ <div class="py-20" />
599
+ </div>
600
+ {:else}
601
+ {#each getLogs(selected, upTo) as file}
602
+ <div
603
+ style="min-height: {logsContent[file.file_path]
604
+ ? 10
605
+ : Math.min(file.ok_lines + file.err_lines, 30) * 16}px;"
606
+ >
607
+ <div class="bg-surface-primary-inverse text-sm font-semibold px-1"
608
+ >{new Date(file.ts).toLocaleTimeString([], {
609
+ day: '2-digit',
610
+ month: '2-digit',
611
+ hour: '2-digit',
612
+ minute: '2-digit'
613
+ })}</div
577
614
  >
578
- <div class="bg-surface-primary-inverse text-sm font-semibold px-1"
579
- >{new Date(file.ts).toLocaleTimeString([], {
580
- day: '2-digit',
581
- month: '2-digit',
582
- hour: '2-digit',
583
- minute: '2-digit'
584
- })}</div
585
- >
586
- {#if logsContent[file.file_path] == undefined}
587
- <div
588
- class="animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms] h-full w-full"
589
- />
590
- {:else if logsContent[file.file_path]}
591
- {#if logsContent[file.file_path].error}
592
- {#if logsContent[file.file_path].error?.startsWith('Not Found')}
593
- <div class="text-xs pb-4 pt-2 text-secondary"
594
- >Log file is missing. Log files require a shared log volume to be mounted
595
- across servers and workers or to use the EE S3/object storage integration
596
- for logs. To avoid mounting a shared volume, set the EE object store logs
597
- in the instance settings</div
598
- >
599
- {:else}
600
- <div class="text-xs text-red-400 pb-4"
601
- >{logsContent[file.file_path].error}</div
602
- >
603
- {/if}
604
- {:else if logsContent[file.file_path].content}
605
- <!-- svelte-ignore a11y-click-events-have-key-events -->
606
- <!-- svelte-ignore a11y-no-static-element-interactions -->
607
- <div on:click|preventDefault class="pr-2"
608
- ><LogViewer
609
- noAutoScroll
610
- noMaxH
611
- isLoading={false}
612
- tag={undefined}
613
- content={processLogWithJsonFmt(
614
- logsContent[file.file_path].content,
615
- file.json_fmt
616
- )}
617
- /></div
615
+ {#if logsContent[file.file_path] == undefined}
616
+ <div
617
+ class="animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms] h-full w-full"
618
+ />
619
+ {:else if logsContent[file.file_path]}
620
+ {#if logsContent[file.file_path].error}
621
+ {#if logsContent[file.file_path].error?.startsWith('Not Found')}
622
+ <div class="text-xs pb-4 pt-2 text-secondary"
623
+ >Log file is missing. Log files require a shared log volume to be mounted
624
+ across servers and workers or to use the EE S3/object storage integration
625
+ for logs. To avoid mounting a shared volume, set the EE object store logs in
626
+ the instance settings</div
618
627
  >
619
628
  {:else}
620
- <div>No logs</div>
629
+ <div class="text-xs text-red-400 pb-4"
630
+ >{logsContent[file.file_path].error}</div
631
+ >
621
632
  {/if}
633
+ {:else if logsContent[file.file_path].content}
634
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
635
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
636
+ <div on:click|preventDefault class="pr-2"
637
+ ><LogViewer
638
+ noAutoScroll
639
+ noMaxH
640
+ isLoading={false}
641
+ tag={undefined}
642
+ content={processLogWithJsonFmt(
643
+ logsContent[file.file_path].content,
644
+ file.json_fmt
645
+ )}
646
+ /></div
647
+ >
648
+ {:else}
649
+ <div>No logs</div>
622
650
  {/if}
623
- </div>
624
- {/each}
625
- {/if}
626
- </div>
627
- {#if searchTerm == ''}
628
- <div class="flex w-full items-center gap-4">
629
- <div class="text-tertiary px-1 text-2xs">Last 5 log files up to:</div>
630
- <div class="flex grow text-xs justify-center px-2 items-center gap-2">
631
- {#if upTo}
632
- <button
633
- on:click={() => {
634
- if (upTo) {
635
- upToIsLatest = false
636
- upTo = new Date(new Date(upTo).getTime() - 5 * 60 * 1000).toISOString()
637
- }
638
- }}>{'<'} 5m</button
639
- >
640
- {:else}
641
- <div />
642
- {/if}
643
-
644
- <div class="flex gap-1 relative items-center"
645
- ><div class="flex gap-1 relative">
646
- <input
647
- type="text"
648
- value={upTo
649
- ? new Date(upTo).toLocaleTimeString([], {
650
- day: '2-digit',
651
- month: '2-digit',
652
- hour: '2-digit',
653
- minute: '2-digit'
654
- })
655
- : ''}
656
- disabled
657
- /><CalendarPicker bind:date={upTo} label="Logs up to" /></div
658
- ></div
659
- >
660
- {#if upTo}
661
- <button
662
- on:click={() => {
663
- if (upTo) {
664
- upToIsLatest = false
665
- upTo = new Date(new Date(upTo).getTime() + 5 * 60 * 1000).toISOString()
666
- }
667
- }}>5m {'>'}</button
668
- >
669
- {:else}
670
- <div />
671
651
  {/if}
672
652
  </div>
673
- <div>
653
+ {/each}
654
+ {/if}
655
+ </div>
656
+ {#if searchTerm == ''}
657
+ <div class="flex w-full items-center gap-4">
658
+ <div class="text-tertiary px-1 text-2xs">Last 5 log files up to:</div>
659
+ <div class="flex grow text-xs justify-center px-2 items-center gap-2">
660
+ {#if upTo}
674
661
  <button
675
- class="text-xs"
676
662
  on:click={() => {
677
- upTo = new Date().toISOString()
678
- upToIsLatest = true
679
- }}>now</button
663
+ if (upTo) {
664
+ upToIsLatest = false
665
+ upTo = new Date(new Date(upTo).getTime() - 5 * 60 * 1000).toISOString()
666
+ }
667
+ }}>{'<'} 5m</button
680
668
  >
681
- </div>
669
+ {:else}
670
+ <div />
671
+ {/if}
672
+
673
+ <div class="flex gap-1 relative items-center"
674
+ ><div class="flex gap-1 relative">
675
+ <input
676
+ type="text"
677
+ value={upTo
678
+ ? new Date(upTo).toLocaleTimeString([], {
679
+ day: '2-digit',
680
+ month: '2-digit',
681
+ hour: '2-digit',
682
+ minute: '2-digit'
683
+ })
684
+ : ''}
685
+ disabled
686
+ /><CalendarPicker bind:date={upTo} label="Logs up to" /></div
687
+ ></div
688
+ >
689
+ {#if upTo}
690
+ <button
691
+ on:click={() => {
692
+ if (upTo) {
693
+ upToIsLatest = false
694
+ upTo = new Date(new Date(upTo).getTime() + 5 * 60 * 1000).toISOString()
695
+ }
696
+ }}>5m {'>'}</button
697
+ >
698
+ {:else}
699
+ <div />
700
+ {/if}
682
701
  </div>
683
- {/if}
684
- {:else}
685
- <div class="flex justify-center items-center pt-8">Select a host to see its logs</div>
686
- {/if}</div
687
- ></Pane
702
+ <div>
703
+ <button
704
+ class="text-xs"
705
+ on:click={() => {
706
+ upTo = new Date().toISOString()
707
+ upToIsLatest = true
708
+ }}>now</button
709
+ >
710
+ </div>
711
+ </div>
712
+ {/if}
713
+ {:else}
714
+ <div class="flex justify-center items-center pt-8">Select a host to see its logs</div>
715
+ {/if}</div
688
716
  >
689
- </Splitpanes>
690
- </SplitPanesWrapper>
717
+ </svelte:fragment>
718
+ </SplitPanesOrColumnOnMobile>