diff --git a/scripts/migrate_fa_to_lucide.py b/scripts/migrate_fa_to_lucide.py new file mode 100644 index 00000000..9515adc8 --- /dev/null +++ b/scripts/migrate_fa_to_lucide.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +""" +migrate_fa_to_lucide.py — Replace FontAwesome with Lucide components. + +Usage: + python3 scripts/migrate_fa_to_lucide.py src/routes/events/[event_id]/\(pres_mgmt\)/ + +Skips content inside HTML comments. Adds/merges lucide-svelte imports. +""" +import re +import sys +import os +from pathlib import Path + +# ── FA icon → Lucide component name ───────────────────────────────────────── +FA_TO_LUCIDE = { + 'fa-spinner': 'LoaderCircle', + 'fa-cog': 'LoaderCircle', # only when fa-spin + 'fa-sync-alt': 'RefreshCw', + 'fa-times': 'X', + 'fa-exclamation-triangle': 'TriangleAlert', + 'fa-check': 'Check', + 'fa-check-circle': 'CheckCircle', + 'fa-plus': 'Plus', + 'fa-minus': 'Minus', + 'fa-save': 'Save', + 'fa-edit': 'Pencil', + 'fa-eye': 'Eye', + 'fa-eye-slash': 'EyeOff', + 'fa-toggle-on': 'ToggleRight', + 'fa-toggle-off': 'ToggleLeft', + 'fa-star-of-life': 'Asterisk', + 'fa-id-card': 'IdCard', + 'fa-paper-plane': 'Send', + 'fa-map-marker-alt': 'MapPin', + 'fa-file-alt': 'FileText', + 'fa-envelope': 'Mail', + 'fa-book': 'BookOpen', + 'fa-angle-right': 'ChevronRight', + 'fa-user': 'User', + 'fa-tasks': 'ListChecks', + 'fa-plane': 'Plane', + 'fa-list': 'List', + 'fa-link': 'Link', + 'fa-file-archive': 'Archive', + 'fa-comment-dots': 'MessageCircle', + 'fa-chevron-up': 'ChevronUp', + 'fa-chevron-down': 'ChevronDown', + 'fa-camera': 'Camera', + 'fa-barcode': 'Barcode', + 'fa-upload': 'Upload', + 'fa-search': 'Search', + 'fa-mail-bulk': 'Mails', + 'fa-laptop-code': 'Laptop', + 'fa-copy': 'Copy', + 'fa-user-tag': 'Tag', + 'fa-user-secret': 'UserRound', + 'fa-users': 'Users', + 'fa-user-circle': 'CircleUser', + 'fa-sort': 'ArrowUpDown', + 'fa-question': 'HelpCircle', + 'fa-map-marked': 'MapPinned', + 'fa-list-ol': 'ListOrdered', + 'fa-laptop': 'Laptop', + 'fa-info': 'Info', + 'fa-building': 'Building2', + 'fa-user-slash': 'UserX', + 'fa-user-check': 'UserCheck', + 'fa-unlink': 'Unlink', + 'fa-star': 'Star', + 'fa-search-location': 'MapPin', + 'fa-remove-format': 'RemoveFormatting', + 'fa-qrcode': 'QrCode', + 'fa-key': 'Key', + 'fa-heartbeat': 'HeartPulse', + 'fa-hat-wizard': 'Wand2', + 'fa-fingerprint': 'Fingerprint', + 'fa-file-csv': 'FileSpreadsheet', + 'fa-file': 'File', + 'fa-clock': 'Clock', + 'fa-clipboard-list': 'ClipboardList', + 'fa-chart-line': 'TrendingUp', + 'fa-chalkboard-teacher': 'Presentation', + 'fa-calendar-day': 'CalendarDays', + 'fa-bell-slash': 'BellOff', + 'fa-bell': 'Bell', +} + +# Skip modifiers — not real icon names +FA_MODIFIERS = {'fas', 'far', 'fab', 'fa-spin', 'fa-fw', 'fa-lg', 'fa-2x', 'fa-sm'} + +# ── Pattern: ─────────── +# [^>]* matches newlines too (character class, not dot) +SPAN_RE = re.compile( + r']*>\s*' +) + +# ── Comment splitter ───────────────────────────────────────────────────────── +COMMENT_RE = re.compile(r'()') + +# ── Lucide import line ──────────────────────────────────────────────────────── +IMPORT_RE = re.compile(r"import\s*\{([^}]+)\}\s*from\s*'lucide-svelte'\s*;?") + + +def parse_fa_class(class_str): + """Return (icon_name, extra_classes, has_spin) from a FA class string.""" + parts = class_str.split() + icon_name = None + has_spin = 'fa-spin' in parts + extra = [] + for p in parts: + if p in FA_MODIFIERS: + continue + elif p.startswith('fa-'): + if icon_name is None: + icon_name = p # first real icon name wins + else: + extra.append(p) + return icon_name, extra, has_spin + + +def replace_span(m): + """Regex sub callback: replace a single FA span with a Lucide component.""" + icon_name, extra, has_spin = parse_fa_class(m.group(1)) + if icon_name is None: + return m.group(0) + + lucide = FA_TO_LUCIDE.get(icon_name) + if lucide is None: + print(f' ⚠ no mapping for {icon_name!r} — left as-is', file=sys.stderr) + return m.group(0) + + classes = extra[:] + if has_spin: + classes.append('animate-spin') + + class_attr = f' class="{" ".join(classes)}"' if classes else '' + return f'<{lucide} size="1em"{class_attr} />' + + +def process_content(content): + """Replace FA spans, skip HTML comments. Return (new_content, used_icons).""" + used_icons = set() + + def track_and_replace(m): + result = replace_span(m) + if result != m.group(0): + # Extract lucide name from result + lucide_name = result.split()[0].lstrip('<') + used_icons.add(lucide_name) + return result + + # Split by comments; only process non-comment segments + parts = COMMENT_RE.split(content) + new_parts = [] + for part in parts: + if part.startswith(' @@ -147,7 +148,7 @@ class="text-sm text-gray-500 bg-yellow-100 p-1 rounded-md border border-yellow-200" title="Device code {event_device_obj?.code}" > - + {event_device_obj?.code ?? ''} {/if} @@ -161,9 +162,7 @@ class="text-red-500 bg-red-200 p-1 rounded-md border border-red-200 text-center" title={event_device_obj.alert_msg} > - + {/if} {#if event_device_obj?.status} @@ -172,8 +171,7 @@ class="text-blue-500 bg-blue-200 p-1 rounded-md border border-blue-200 text-center" title={event_device_obj.status_msg} > - + {/if} {#if event_device_obj?.record_status} @@ -182,7 +180,7 @@ class="text-orange-500 bg-orange-200 p-1 rounded-md border border-orange-200 text-center" title={event_device_obj.record_status_msg} > - + {/if} @@ -209,14 +207,11 @@ class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 transition-all hover:transition-all *:hover:inline text-xs" > {#await ae_promises.load_ae_obj_id__event_device[event_device_obj.event_device_id]} - + {:then load_results} - + {:catch error} - + {/await} @@ -258,11 +253,10 @@ > {#if $events_loc.pres_mgmt.device_kv && $events_loc.pres_mgmt.device_kv[event_device_obj.event_device_id]?.collapse} - + {:else} - + {/if} @@ -285,21 +279,16 @@
- + {event_device_obj?.alert ? 'Alert' : 'No Alert'} - +
- + {@html event_device_obj?.alert_msg ?? 'No message'}
@@ -321,19 +310,16 @@
- + {event_device_obj?.status ? event_device_obj?.status : 'No Status'} - +
- + {@html event_device_obj?.status_msg ?? 'No message'}
@@ -355,7 +341,7 @@
- + {#if event_device_obj?.record_status && event_device_obj?.record_status.length > 10} {ae_util.iso_datetime_formatter( @@ -368,11 +354,10 @@ {:else} No Recording Status {/if} - +
- + {@html event_device_obj?.record_status_msg ?? 'No message'}
@@ -446,7 +431,7 @@ {#if event_device_obj?.heartbeat} - + Heartbeat: {#if $events_sess.pres_mgmt.show_content__device_description == event_device_obj.event_device_id} - + Hide Description {:else} - + Show {/if} diff --git a/src/routes/events/[event_id]/(pres_mgmt)/event_page_menu.svelte b/src/routes/events/[event_id]/(pres_mgmt)/event_page_menu.svelte index 23afc5f2..a98550ee 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/event_page_menu.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/event_page_menu.svelte @@ -25,6 +25,7 @@ import Comp__events_menu_nav from '../../ae_comp__events_menu_nav.svelte'; import Comp__pres_mgmt_menu_opts from '../../ae_comp__events_menu_opts.svelte'; import AE_Record_Controls from '$lib/ae_elements/AE_Record_Controls.svelte'; + import { MapPin, Plane, Save, Send } from 'lucide-svelte'; let show_modal = $state(false); let show_help = $state(false); @@ -165,7 +166,7 @@ > - + {$events_loc.pres_mgmt.save_search_text ? 'Do Not Save Search' : 'Save Search Text?'} @@ -180,7 +181,7 @@ > - + {$events_loc.pres_mgmt.hide__launcher_link ? 'Show Launcher Links' : 'Hide Launcher Links?'} @@ -195,7 +196,7 @@ > - + {$events_loc.pres_mgmt.hide__launcher_link_legacy ? 'Show Legacy Launcher Links' : 'Hide Legacy Launcher Links?'} @@ -210,7 +211,7 @@ > - + {$events_loc.pres_mgmt.hide__location_link ? 'Show Location Links' : 'Hide Location Links?'} diff --git a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/+page.svelte b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/+page.svelte index fdd2f23e..c7521af0 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/+page.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/+page.svelte @@ -36,6 +36,7 @@ import Element_manage_event_file_li_wrap from '$lib/elements/element_manage_event_file_li_direct.svelte'; import Location_view from './location_view.svelte'; import Location_page_menu from './location_page_menu.svelte'; + import { LoaderCircle, TriangleAlert } from 'lucide-svelte'; // Variables // Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other. @@ -111,14 +112,14 @@ {#if !$lq__event_location_obj}
- + Loading location...
{:else} {#if !$lq__event_location_obj.enable && !$ae_loc.trusted_access}

- + Location Disabled

This location is currently disabled. Please contact the event organizer for more information.

diff --git a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_page_menu.svelte b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_page_menu.svelte index bd7dc5a8..826b399d 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_page_menu.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_page_menu.svelte @@ -29,6 +29,7 @@ import Element_data_store from '$lib/elements/element_data_store_v3.svelte'; import Comp__events_menu_nav from '../../../../ae_comp__events_menu_nav.svelte'; import AE_Record_Controls from '$lib/ae_elements/AE_Record_Controls.svelte'; + import { List, MapPin, Pencil, Plane, Send, Wand2 } from 'lucide-svelte'; let show_modal = $state(false); let show_help = $state(false); @@ -134,7 +135,7 @@ > - + {$events_loc.pres_mgmt.hide__launcher_link ? 'Show Launcher Links' : 'Hide Launcher Links?'} @@ -149,7 +150,7 @@ > - + {$events_loc.pres_mgmt.hide__launcher_link_legacy ? 'Show Legacy Launcher Links' : 'Hide Legacy Launcher Links?'} @@ -164,7 +165,7 @@ > - + {$events_loc.pres_mgmt.hide__location_link ? 'Show Location Links' : 'Hide Location Links?'} @@ -183,7 +184,7 @@ > - + {$events_loc.pres_mgmt.show_content__session_files ? 'Hide Linked Files (testing)' : 'Show Linked Files? (testing)'} @@ -201,7 +202,7 @@ > - + {$events_loc.pres_mgmt.show_content__session_presentations ? 'Hide Linked Presentations (testing)' : 'Show Linked Presentations? (testing)'} @@ -221,7 +222,7 @@ class:ae_btn_warning={$ae_loc.edit_mode} class:ae_btn_warning_outlined={!$ae_loc.edit_mode} > - + {$ae_loc.edit_mode ? 'Edit Mode On' : 'Edit Mode?'} @@ -232,7 +233,7 @@ class:ae_btn_warning={$ae_loc.adv_mode} class:ae_btn_warning_outlined={!$ae_loc.adv_mode} > - + {$ae_loc.adv_mode ? 'Advanced Mode On' : 'Advanced Mode?'}
diff --git a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_view.svelte b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_view.svelte index 79591cde..caf2c43e 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_view.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/location/[event_location_id]/location_view.svelte @@ -45,6 +45,7 @@ events_trig_kv } from '$lib/stores/ae_events_stores'; import { events_func } from '$lib/ae_events_functions'; + import { Barcode, Eye, EyeOff, Key, Plane, Send } from 'lucide-svelte'; // Exports // export let event_location_id: string; @@ -200,7 +201,7 @@ title="Location code {$lq__event_location_obj.code}" > code: - + {$lq__event_location_obj.code}
@@ -214,7 +215,7 @@ class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500" title="Launcher: {$lq__event_location_obj?.name} {$lq__event_location_obj?.event_location_id}" > - + {@html $lq__event_location_obj?.name ? $lq__event_location_obj?.name : ae_snip.html__not_set} @@ -228,7 +229,7 @@ class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500" title="Launcher: {$lq__event_location_obj?.name} {$lq__event_location_obj?.event_location_id}" > - + @@ -236,7 +237,7 @@ {#if $ae_loc.administrator_access}
  • Location passcode: - + {@html $lq__event_location_obj.passcode ? $lq__event_location_obj.passcode : ae_snip.html__not_set} @@ -279,10 +280,10 @@ class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 text-xs" > {#if $events_loc.pres_mgmt.show_content__location_description} - + Hide Description {:else} - + Show {/if} diff --git a/src/routes/events/[event_id]/(pres_mgmt)/locations/+page.svelte b/src/routes/events/[event_id]/(pres_mgmt)/locations/+page.svelte index 0584c024..dbb19353 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/locations/+page.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/locations/+page.svelte @@ -46,6 +46,7 @@ // import Sign_in_out from './../../sign_in_out.svelte'; import { browser } from '$app/environment'; + import { LoaderCircle, MapPinned } from 'lucide-svelte'; // Variables $effect(() => { @@ -106,7 +107,7 @@ {#if !$lq__event_location_obj_li}
    - + Loading locations...
    {:else} @@ -118,8 +119,7 @@ " > - + Locations/Rooms diff --git a/src/routes/events/[event_id]/(pres_mgmt)/locations/ae_comp__event_location_obj_li.svelte b/src/routes/events/[event_id]/(pres_mgmt)/locations/ae_comp__event_location_obj_li.svelte index 4c61fc58..4adddc73 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/locations/ae_comp__event_location_obj_li.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/locations/ae_comp__event_location_obj_li.svelte @@ -38,6 +38,7 @@ import Comp_event_session_obj_li from '../../../ae_comp__event_session_obj_li_wrapper.svelte'; import Element_ae_obj_field_editor_v3 from '$lib/elements/element_ae_obj_field_editor_v3.svelte'; import Comp_event_device_obj_li from '../device/device/ae_comp__event_device_obj_li_wrapper.svelte'; + import { Barcode, ChevronDown, ChevronUp, Eye, EyeOff, MapPin, MapPinned, Plane, Plus, Send, TriangleAlert } from 'lucide-svelte'; // if (log_lvl) { // console.log(`link_to_type: ${link_to_type}; link_to_id: ${link_to_id}`); @@ -95,7 +96,7 @@ }} class="btn btn-sm preset-tonal-warning hover:preset-filled-warning-500" > - + Add Location {/if} @@ -108,7 +109,7 @@ class:hidden={!$lq__event_location_obj_li?.length} title="Locations: {$lq__event_location_obj_li?.length ?? 'None'}" > - + {@html $lq__event_location_obj_li?.length ? `${$lq__event_location_obj_li?.length}×` : ''} @@ -118,7 +119,7 @@ - + No locations available. {/if} @@ -150,9 +151,7 @@ class_li={'m-1'} on_success={() => events_func.load_ae_obj_id__event_location({ api_cfg: $ae_api, event_location_id: event_location_obj.event_location_id })} > - + "{event_location_obj?.name ?? '-- not set --'}" @@ -162,7 +161,7 @@ class="text-sm text-gray-500 bg-yellow-100 p-1 rounded-md border border-yellow-200" title="Location code {event_location_obj?.code}" > - + {event_location_obj?.code ?? ''} {/if} @@ -174,7 +173,7 @@ class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all hover:transition-all *:hover:inline text-xs" title="View this location" > - + @@ -183,7 +182,7 @@ class="btn btn-sm preset-tonal-secondary hover:preset-filled-secondary-500 transition-all hover:transition-all *:hover:inline text-xs" title="The legacy launcher that not actively being developed. Use with the native launcher for oral sessions." > - + @@ -192,7 +191,7 @@ class="btn btn-sm preset-tonal-tertiary border border-tertiary-500 hover:preset-filled-secondary-500 transition-all hover:transition-all *:hover:inline text-xs" title="The new launcher that is actively being developed. Do not use with the native launcher." > - + @@ -233,10 +232,10 @@ > {#if $events_loc.pres_mgmt.location_kv && $events_loc.pres_mgmt.location_kv[event_location_obj.event_location_id]?.collapse} - + {:else} - + {/if} @@ -360,12 +359,10 @@ class="btn btn-sm preset-tonal-surface hover:preset-filled-surface-500 text-xs" > {#if $events_sess.pres_mgmt.show_content__location_description == event_location_obj.event_location_id} - + Hide Description {:else} - + Show {/if} diff --git a/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte b/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte index 262157ea..ec4b2fbb 100644 --- a/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte +++ b/src/routes/events/[event_id]/(pres_mgmt)/pres_mgmt/+page.svelte @@ -37,6 +37,7 @@ import Comp_event_files_upload from '../../../ae_comp__event_files_upload.svelte'; import Element_manage_event_file_li_wrap from '$lib/elements/element_manage_event_file_li_direct.svelte'; import Event_page_menu from '../event_page_menu.svelte'; + import { Archive, CalendarDays, ChevronRight, FileText, ListChecks, LoaderCircle, Mails, MapPin, RemoveFormatting, Search, TriangleAlert, Upload } from 'lucide-svelte'; // Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other. // NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store) @@ -364,13 +365,13 @@ {#if !$lq__event_obj}
    - + Loading event information...
    {:else if $lq__event_obj?.enable || $ae_loc.trusted_access}
    - +