chore: migrate all FA icons to Lucide (@lucide/svelte)

- Replaced all active FontAwesome <span class="fas fa-*"> icons with
  Lucide components across 145 files (excluding /idaa/ which is intentional)
- Fixed merge script bug: consolidated lucide-svelte imports into @lucide/svelte
- Replaced dynamic toggle patterns (fa-toggle-on/off) with ToggleRight/ToggleLeft
- Replaced fa-eye/fa-eye-slash with Eye/EyeOff
- Replaced fa-bug/fa-bug-slash with Bug/BugOff
- Replaced fa-sync fa-spin with RefreshCw + animate-spin
- Replaced fa-microchip with Cpu
- Fixed {@const} placement in element_manage_event_file_li.svelte
- Removed obsolete CSS hover rules for .unlock_icon/.lock_icon
- svelte-check: 0 errors, 0 warnings
This commit is contained in:
Scott Idem
2026-03-16 18:07:43 -04:00
parent c9050264a5
commit b543c8a930
147 changed files with 587 additions and 754 deletions

View File

@@ -11,8 +11,7 @@
import type { key_val } from '$lib/stores/ae_stores';
// Icons (Standardized to Lucide where possible, or FontAwesome placeholders)
import { Bold, Italic, List, Code, Maximize2 } from 'lucide-svelte';
import { Bold, Code, Italic, List, Maximize2 } from '@lucide/svelte';
interface Props {
content?: string;
new_content?: string;

View File

@@ -7,10 +7,7 @@
*/
import { onMount, untrack } from 'svelte';
import { browser } from '$app/environment';
import {
Bold, Italic, List, ListOrdered,
RemoveFormatting, Type, Code, AlignLeft
} from 'lucide-svelte';
import { AlignLeft, Bold, Code, Italic, List, ListOrdered, RemoveFormatting, Type } from '@lucide/svelte';
import { ae_util } from '$lib/ae_utils/ae_utils';
interface Props {

View File

@@ -10,6 +10,7 @@
// import { api } from '$lib/api';
// import { update_ae_obj_id_crud } from '$lib/ae_core/core__crud_generic';
import { update_ae_obj } from '$lib/ae_core/core__crud_generic';
import { Check, LoaderCircle, Pencil, Save, X } from '@lucide/svelte';
// import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/ae_stores';
// *** Import Aether core components
@@ -43,7 +44,7 @@
hide_edit_btn?: boolean;
outline_element?: boolean;
show_crud?: boolean;
btn_label?: any; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
btn_label?: any; // '<Check size="1em" class="mx-1" /> Save'; // PATCH
// export let remove_breaks = false;
class_li?: string;
children?: import('svelte').Snippet;
@@ -186,7 +187,7 @@
}}
title="Double click to edit property"
>
<span class="fas fa-edit"></span>
<Pencil size="1em" />
<span class="hidden">Edit</span>
</button>
</span>
@@ -208,7 +209,7 @@
}}
title="Close field editing"
>
<span class="fas fa-window-close"></span>
<X size="1em" />
<span class="hidden sm:inline">Close</span>
</button>
<!-- </span> -->
@@ -272,25 +273,25 @@
{#if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<Check size="1em" class="mx-1" />
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<Save size="1em" class="mx-1" />
<span>Save</span>
{/if}
</button>
<div class="field_patch_result" class:show_crud>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<LoaderCircle size="1em" class="mx-1 animate-spin" />
<span>Processing...</span>
{:then}
{#if patch_result}
<span class="fas fa-check mx-1"></span>
<Check size="1em" class="mx-1" />
<span>{patch_result}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->

View File

@@ -41,7 +41,7 @@
outline_element?: boolean;
btn_label?: any; // '<span class="fas fa-check mx-1"></span> Save'; // PATCH
btn_label?: any; // '<Check size="1em" class="mx-1" /> Save'; // PATCH
// export let remove_breaks = false;
class_li?: string;
children?: import('svelte').Snippet;
@@ -102,7 +102,7 @@
// *** Import Aether core components
import AE_Comp_Editor_TipTap from '$lib/elements/AE_Comp_Editor_TipTap.svelte';
import { Check, LoaderCircle, Pencil, Save, X } from '@lucide/svelte';
// *** Import Aether module variables and functions
// *** Import Aether module components
@@ -270,7 +270,7 @@
}}
title="Double click to edit property"
>
<span class="fas fa-edit m-0 p-0"></span>
<Pencil size="1em" class="m-0 p-0" />
<span class="hidden">Edit</span>
</button>
</span>
@@ -313,7 +313,7 @@
}}
title="Close field editing. This does not save any changes."
>
<span class="fas fa-window-close"></span>
<X size="1em" />
<span class="hidden sm:inline">Close</span>
</button>
</span>
@@ -405,25 +405,25 @@
{#if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-check mx-1"></span>
<Check size="1em" class="mx-1" />
<span>Save</span>
{/if}
<!-- <span>{patch_result}</span> -->
{:else if btn_label}
{@html btn_label}
{:else}
<span class="fas fa-save mx-1"></span>
<Save size="1em" class="mx-1" />
<span>Save</span>
{/if}
</button>
<div class="field_patch_result" class:hidden={!patch_status}>
{#await ae_promises.api_update__ae_obj}
<span class="fas fa-spinner fa-spin mx-1"></span>
<LoaderCircle size="1em" class="mx-1 animate-spin" />
<span>Processing...</span>
{:then}
{#if patch_status}
<span class="fas fa-check mx-1"></span>
<Check size="1em" class="mx-1" />
<span>{patch_status}</span>
{:else}
<!-- <div>Nothing to show yet...</div> -->

View File

@@ -2,7 +2,7 @@
// import { browser } from '$app/environment';
import { untrack } from 'svelte';
import type { Snippet } from 'svelte';
import { LoaderCircle, SquarePen, Save, X, Trash2, Check, CircleAlert } from 'lucide-svelte';
import { Check, CircleAlert, LoaderCircle, Save, SquarePen, Trash2, X } from '@lucide/svelte';
import type { key_val } from '$lib/stores/ae_stores';
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
import { api } from '$lib/api/api';

View File

@@ -10,7 +10,7 @@
import { ae_util } from '$lib/ae_utils/ae_utils';
import type { key_val } from '$lib/stores/ae_stores';
import type { ae_DataStore } from '$lib/types/ae_types';
import { LoaderCircle, Pencil, Save, Trash2 } from '@lucide/svelte';
interface Props {
log_lvl?: number;
expire_minutes?: number;
@@ -379,13 +379,13 @@
<div class="flex justify-between items-center pt-4">
<button type="button" class="btn variant-filled-error" onclick={handle_delete}>
<span class="fas fa-trash mr-2"></span> Delete
<Trash2 size="1em" class="mr-2" /> Delete
</button>
<div class="flex gap-2">
<button type="button" class="btn variant-soft" onclick={() => show_edit = false}>Cancel</button>
<button type="submit" class="btn variant-filled-primary">
<span class="fas fa-save mr-2"></span> Save
<Save size="1em" class="mr-2" /> Save
</button>
</div>
</div>
@@ -409,7 +409,7 @@
ondblclick={() => { show_edit = true; show_view = false; }}
title="Edit Data Store: {ds_code}"
>
<span class="fas fa-edit"></span>
<Pencil size="1em" />
</button>
{/if}
{:else if ds_loading_status === 'not found'}
@@ -424,7 +424,7 @@
{#if ds_loading_status === 'loading'}
<div class="absolute bottom-0 left-0 p-1 opacity-50">
<span class="fas fa-spinner fa-spin text-xs"></span>
<LoaderCircle size="1em" class="text-xs animate-spin" />
</div>
{/if}
</div>

View File

@@ -15,7 +15,7 @@
slct,
slct_trigger
} from '$lib/stores/ae_stores';
import { LoaderCircle, Minus, Upload } from '@lucide/svelte';
const dispatch = createEventDispatcher();
interface Props {
@@ -367,7 +367,7 @@
>
<label for={element_id} class="svelte_input_file_label text-center">
<div>
<span class="fas fa-upload"></span>
<Upload size="1em" />
<!-- Select files to upload -->
<!-- <span class="fas fa-file-archive"></span> -->
<strong>Upload your files</strong>
@@ -394,7 +394,7 @@
<div
class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1"
>
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file
<LoaderCircle size="1em" class="m-1 animate-spin" /> Processing selected file
list...
</div>
{/if}
@@ -426,7 +426,7 @@
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list"
>
<span class="fas fa-minus"></span>
<Minus size="1em" />
<span class="hidden">Remove</span>
</button>
</td>

View File

@@ -8,7 +8,7 @@
// import { api } from '$lib/api';
import { check_hosted_file_obj_w_hash } from '$lib/ae_core/core__check_hosted_file_obj_w_hash';
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
import { LoaderCircle, Minus } from '@lucide/svelte';
interface Props {
// export let element_id = 'svelte_input_file_element';
container_class_li?: string[];
@@ -335,7 +335,7 @@
<div
class="file_list_status ae_warning preset-tonal-warning border border-warning-500 p-1 m-1"
>
<span class="fas fa-spinner fa-spin m-1"></span> Processing selected file list...
<LoaderCircle size="1em" class="m-1 animate-spin" /> Processing selected file list...
</div>
{/if}
@@ -366,7 +366,7 @@
class="btn btn-md preset-tonal-warning hover:preset-filled-secondary-500 m-1"
title="Remove file from upload list"
>
<span class="fas fa-minus"></span>
<Minus size="1em" />
<span class="hidden">Remove</span>
</button>
</td>

View File

@@ -25,7 +25,7 @@
} from '$lib/stores/ae_events_stores';
import { events_func } from '$lib/ae_events_functions';
import AE_Comp_Hosted_Files_Download_Button from '$lib/ae_core/ae_comp__hosted_files_download_button.svelte';
import { Check, Clock, Download, Eye, EyeOff, FileImage, FolderOpen, Laptop, LoaderCircle, Monitor, Pencil, RefreshCw, Save, Trash2, TriangleAlert } from '@lucide/svelte';
interface Props {
log_lvl?: number;
container_class_li?: string | Array<string>;
@@ -156,7 +156,7 @@
class:hidden={!$ae_loc.edit_mode || !$ae_loc.authenticated_access}
title="Refresh the list of files"
>
<span class="fas fa-sync-alt m-1"></span>
<RefreshCw size="1em" class="m-1" />
<div class="hidden">Files</div>
</button>
@@ -170,7 +170,7 @@
class:hidden={!$ae_loc.edit_mode || !$ae_loc.trusted_access}
title="Toggle direct download link and copy link button"
>
<span class="fas fa-download m-1"></span>
<Download size="1em" class="m-1" />
<div class="hidden">
{ae_tmp.show__direct_download ? 'Alt On' : 'Alt Download Off'}
</div>
@@ -193,7 +193,7 @@
'-- not set --'} (files: {$lq__event_file_obj_li?.length ??
'None'})"
>
<span class="fas fa-folder-open mx-1"></span>
<FolderOpen size="1em" class="mx-1" />
{@html $lq__event_file_obj_li
? `${$lq__event_file_obj_li.length}&times;`
: '-- none --'}
@@ -228,6 +228,7 @@
<tbody>
{#each $lq__event_file_obj_li as event_file_obj (event_file_obj.event_file_id)}
{@const ExtIcon = ae_util.file_extension_icon_lucide(event_file_obj.extension)}
<tr
class="ae_obj obj_event_file border-t border-b border-surface-200-800 hover:bg-surface-100-900 hover:border-surface-300-700 transition-colors duration-200"
class:dim={event_file_obj?.hide}
@@ -254,17 +255,17 @@
title="Convert this PDF to a high-res webp image for use in the Launcher poster display."
onclick={() => handle_convert_pdf_to_image(event_file_obj)}
>
<span class="fas fa-file-image mx-1"></span>
<FileImage size="1em" class="mx-1" />
Convert PDF → Image
</button>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'converting'}
<span class="btn btn-sm preset-tonal-surface opacity-60 cursor-wait">
<span class="fas fa-spinner fa-spin mx-1"></span>
<LoaderCircle size="1em" class="mx-1 animate-spin" />
Converting…
</span>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'done'}
<span class="btn btn-sm preset-tonal-success" title="Conversion complete. New webp hosted_file created: {convert_result_kv[event_file_obj.event_file_id]?.filename ?? ''}">
<span class="fas fa-check mx-1"></span>
<Check size="1em" class="mx-1" />
Done — {convert_result_kv[event_file_obj.event_file_id]?.filename ?? 'image created'}
</span>
{:else if convert_status_kv[event_file_obj.event_file_id] === 'error'}
@@ -276,7 +277,7 @@
convert_status_kv[event_file_obj.event_file_id] = 'idle';
}}
>
<span class="fas fa-exclamation-triangle mx-1"></span>
<TriangleAlert size="1em" class="mx-1" />
Failed — Retry?
</button>
{/if}
@@ -391,16 +392,12 @@
title="Save changes"
>
{#await ae_promises.update__event_file_obj}
<span
class="fas fa-spinner fa-spin mx-1"
></span>
<LoaderCircle size="1em" class="mx-1 animate-spin" />
<span class=""
>Saving {event_file_obj.extension}</span
>
{:then}
<span
class="fas fa-save mx-1"
></span>
<Save size="1em" class="mx-1" />
Save {event_file_obj.extension}
filename?
{/await}
@@ -444,8 +441,7 @@
event_file_obj.event_file_id}
title={`Rename this file? "${event_file_obj.filename}"}`}
>
<span class="fas fa-edit mx-1"
></span>
<Pencil size="1em" class="mx-1" />
{#if $events_sess.pres_mgmt?.show_field_edit__filename == event_file_obj.event_file_id}
Cancel?
{:else}
@@ -515,20 +511,15 @@
-->
{#await ae_promises.update__event_file_obj}
<span
class="fas fa-spinner fa-spin mx-1"
></span>
<LoaderCircle size="1em" class="mx-1 animate-spin" />
<span class=""
>Saving {event_file_obj.extension}</span
>
{:then}
{#if event_file_obj.hide}
<span class="fas fa-eye m-1"
></span> Unhide File
<Eye size="1em" class="m-1" /> Unhide File
{:else}
<span
class="fas fa-eye-slash m-1"
></span> Hide
<EyeOff size="1em" class="m-1" /> Hide
{/if}
{/await}
</button>
@@ -561,8 +552,7 @@
class="btn btn-sm preset-tonal-tertiary hover:preset-tonal-success"
title="Delete this file"
>
<span class="fas fa-trash-alt mx-1"
></span>
<Trash2 size="1em" class="mx-1" />
<!-- <span class="fas fa-minus mx-1"></span> -->
Delete
</button>
@@ -581,13 +571,9 @@
>
<div class="">
{#if event_file_obj.open_in_os == 'win'}
MS Windows <span
class="fab fa-windows"
></span>
MS Windows <Monitor size="1em" class="inline-block" />
{:else if event_file_obj.open_in_os == 'mac'}
Apple macOS <span
class="fab fa-apple"
></span>
Apple macOS <Laptop size="1em" class="inline-block" />
{/if}
</div>
@@ -698,11 +684,7 @@
Type:
<strong
>{event_file_obj.extension}
<span
class="fas fa-{ae_util.file_extension_icon(
event_file_obj.extension
)}"
></span>
<ExtIcon size="1em" class="inline-block" />
</strong>
<!-- {#if event_file_obj.open_in_os == 'win'}
<strong>
@@ -794,8 +776,7 @@
<!-- <span class="fas fa-cloud-upload-alt mx-1"></span> -->
<!-- Uploaded: -->
<!-- <span class="fas fa-calendar-day mx-1"></span> -->
<span class="fas fa-clock mx-1"
></span>
<Clock size="1em" class="mx-1" />
<strong>
{ae_util.iso_datetime_formatter(
event_file_obj.created_on,

View File

@@ -8,7 +8,7 @@
import { ae_loc, ae_sess, ae_api, ae_trig, slct, slct_trigger } from '$lib/stores/ae_stores';
import { db_core } from '$lib/ae_core/db_core';
import { core_func } from '$lib/ae_core/ae_core_functions';
import { CalendarDays, Download, FolderOpen, MinusCircle, Pencil, PlusCircle, RefreshCw, Trash2 } from '@lucide/svelte';
// export let allow_basic: boolean = false;
interface Props {
@@ -60,7 +60,7 @@
title="Files for {link_to_type ?? '-- not set --'}: {link_to_id ??
'-- not set --'} (files: {$lq__hosted_file_obj_li?.length ?? 'None'})"
>
<span class="fas fa-folder-open mx-1"></span>
<FolderOpen size="1em" class="mx-1" />
{@html $lq__hosted_file_obj_li
? `${$lq__hosted_file_obj_li.length}&times;`
: '-- none --'}
@@ -101,7 +101,7 @@
class:hidden={!$ae_loc.edit_mode || !$ae_loc.authenticated_access}
title="Refresh the list of files"
>
<span class="fas fa-sync-alt m-1"></span>
<RefreshCw size="1em" class="m-1" />
<div class="hidden">Files</div>
</button>
@@ -115,7 +115,7 @@
class:hidden={!$ae_loc.edit_mode || !$ae_loc.trusted_access}
title="Toggle direct download link and copy link button"
>
<span class="fas fa-download m-1"></span>
<Download size="1em" class="m-1" />
<div class="hidden">
{ae_tmp.show__direct_download ? 'Alt On' : 'Alt Download Off'}
</div>
@@ -163,10 +163,10 @@
title="Add/Remove file to/from the locally stored uploaded file list. This is referenced by other AE components."
>
{#if $ae_loc.files.uploaded_file_kv[hosted_file_obj.hosted_file_id]}
<span class="fas fa-minus-circle m-1"></span>
<MinusCircle size="1em" class="m-1" />
<span class="hidden">Remove</span>
{:else}
<span class="fas fa-plus-circle m-1"></span>
<PlusCircle size="1em" class="m-1" />
<span class="hidden">Add</span>
{/if}
</button>
@@ -198,7 +198,7 @@
class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500"
title="Delete a file from the host server."
>
<span class="fas fa-trash-alt m-1"></span>
<Trash2 size="1em" class="m-1" />
<span class="hidden">Delete</span>
</button>
@@ -212,19 +212,19 @@
class="btn btn-sm preset-tonal-primary hover:preset-filled-primary-500"
title="Edit file details"
>
<span class="fas fa-edit m-1"></span>
<Pencil size="1em" class="m-1" />
<span class="hidden">Edit</span>
</button>
<span class="text-xs text-gray-500">
<span class="fas fa-calendar-day mx-1"></span>
<CalendarDays size="1em" class="mx-1" />
<span class="hidden">Created on:</span>
{ae_util.iso_datetime_formatter(
hosted_file_obj.created_on,
'datetime_medium_sec'
)}
{#if hosted_file_obj.updated_on}
<span class="fas fa-sync-alt mx-1"></span>
<RefreshCw size="1em" class="mx-1" />
Updated on
{ae_util.iso_datetime_formatter(
hosted_file_obj.updated_on,

View File

@@ -0,0 +1,93 @@
<script lang="ts">
/**
* src/lib/elements/element_pwa_install_prompt.svelte
*
* Reusable PWA install nudge. Drop this anywhere in the app to surface
* a platform-aware "Add to Home Screen" prompt.
*
* - Chrome / Android / Desktop Chrome: shows a button that triggers the
* native browser install flow (via the captured beforeinstallprompt event).
* - iOS Safari: shows manual step-by-step "Share → Add to Home Screen" instructions
* since iOS does not support the beforeinstallprompt API.
* - Already installed (standalone mode) or dismissed: renders nothing.
*
* The pwa_install singleton must be initialised first (done in root +layout.svelte).
*/
import { Download, Plus, Share2, Smartphone, X } from '@lucide/svelte';
import { pwa_install } from '$lib/pwa/pwa_install.svelte';
interface Props {
class?: string;
}
let { class: extra_class = '' }: Props = $props();
</script>
{#if pwa_install.should_show}
<div
class="relative w-full rounded-2xl overflow-hidden
border border-primary-500/25
bg-primary-500/8 dark:bg-primary-500/12
{extra_class}"
>
<!-- Dismiss button -->
<button
class="absolute top-2 right-2 p-2 rounded-lg
opacity-40 hover:opacity-90
hover:bg-surface-500/10
transition-opacity"
onclick={() => pwa_install.dismiss()}
aria-label="Dismiss install prompt"
>
<X size="1rem" />
</button>
<div class="p-4 pr-10 space-y-3">
<!-- Header row: icon + text -->
<div class="flex items-center gap-3">
<div class="shrink-0 p-2.5 rounded-xl bg-primary-500/15 text-primary-500">
<Smartphone size="1.35em" />
</div>
<div>
<p class="font-bold text-sm leading-tight">Install Aether Leads</p>
<p class="text-xs opacity-55 leading-snug mt-0.5">
Add to your home screen for the best mobile experience
</p>
</div>
</div>
{#if pwa_install.can_prompt}
<!-- Chrome / Android / Desktop Chrome — native one-tap install -->
<button
class="btn preset-filled-primary w-full"
onclick={() => pwa_install.prompt()}
>
<Download size="1em" />
<span>Add to Home Screen</span>
</button>
{:else}
<!-- iOS Safari — manual instructions (no API available) -->
<ol class="space-y-2 text-sm">
<li class="flex items-center gap-2.5">
<span class="shrink-0 flex items-center justify-center w-5 h-5 rounded-full
bg-primary-500/20 text-primary-600 dark:text-primary-300
text-[10px] font-bold leading-none">1</span>
<span class="opacity-75">
Tap the
<Share2 size="0.85em" class="inline align-middle mx-0.5 text-primary-500" />
<strong>Share</strong> button in Safari
</span>
</li>
<li class="flex items-center gap-2.5">
<span class="shrink-0 flex items-center justify-center w-5 h-5 rounded-full
bg-primary-500/20 text-primary-600 dark:text-primary-300
text-[10px] font-bold leading-none">2</span>
<span class="opacity-75">
Tap <strong>Add to Home Screen</strong>
<Plus size="0.85em" class="inline align-middle mx-0.5 text-primary-500" />
</span>
</li>
</ol>
{/if}
</div>
</div>
{/if}