OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
<?php
namespace Leantime\Core\Events\Htmx;
/**
* Contract for client (HTMX) event enums.
*
* Client events travel from the server to the browser on the `HX-Trigger` response header and are
* consumed either declaratively (`hx-trigger="<event> from:body"`) for data events, or by a JS
* listener for UI command events. Implementations are string-backed enums whose backing value IS
* the wire name, following the convention:
*
* - data events: lt:{domain}:{entity}.{verb} e.g. lt:tickets:ticket.updated
* - UI commands: lt:ui:{command} e.g. lt:ui:modal.close
*
* Use the {@see InteractsWithHtmxEvents} trait to satisfy this interface. Note: PHP enums cannot
* implement Stringable / __toString, so use {@see event()} (or `->value`) to get the wire name in
* PHP. In Blade, `{{ MyEvents::Case }}` renders the value (Laravel's e() unwraps backed enums).
*/
interface HtmxEvent
{
/**
* The wire name (the enum's backing value), e.g. "lt:tickets:ticket.updated".
*/
public function event(): string;
/**
* Wire name scoped to a single entity, e.g. "lt:reactions:sentiment.updated#42".
* Lets a specific component listen for changes to one entity while broad listeners use the
* unscoped name.
*/
public function scoped(int|string $id): string;
/**
* The value formatted for an `hx-trigger` attribute, e.g. "lt:tickets:ticket.updated from:body".
*/
public function trigger(string $from = 'body'): string;
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Leantime\Core\Events\Htmx;
/**
* Helpers for building the `HX-Trigger` response header from client event names.
*
* MIGRATION WINDOW: while emitters and listeners move to the lt:{domain}:{entity}.{verb} convention
* we dual-emit legacy names alongside their canonical replacement so existing declarative listeners
* (hx-trigger="<name> from:body") and (phar) plugins keep working without coordinated releases. Each
* group is bidirectional — emitting ANY member puts the whole group on the wire, so old and new
* listeners both fire regardless of which name the emitter used. Delete LEGACY_ALIASES once every
* emitter and listener has been migrated.
*
* Only DOMAIN data events are aliased here. UI command events (lt:ui:*) are intentionally NOT
* aliased: they're consumed by JS addEventListener handlers that listen for each name directly, so
* dual-emitting them would fire the same handler once per alias (double growl, multiple modal-close
* callbacks) for a single response.
*/
final class HtmxEvents
{
/**
* Bidirectional alias groups. The first entry of each group is the canonical lt:* name.
*
* @var array<int, array<int, string>>
*/
private const LEGACY_ALIASES = [
['lt:tickets:ticket.updated', 'ticket_update'],
['lt:tickets:subtask.updated', 'subtasks_update', 'subtasksUpdated'],
['lt:projects:project.updated', 'HTMX.updateProjectList'],
['lt:timesheets:timer.updated', 'timerUpdate'],
];
/**
* Expand event names to include their legacy/canonical aliases, de-duplicated and order-stable.
* Scoped names (e.g. "lt:reactions:sentiment.updated#42") pass through unchanged.
*
* @param array<int, string|HtmxEvent> $names
* @return array<int, string>
*/
public static function expand(array $names): array
{
$expanded = [];
foreach ($names as $name) {
$name = $name instanceof HtmxEvent ? $name->event() : (string) $name;
$expanded[] = $name;
foreach (self::LEGACY_ALIASES as $group) {
if (in_array($name, $group, true)) {
array_push($expanded, ...$group);
}
}
}
return array_values(array_unique($expanded));
}
/**
* Build the comma-separated `HX-Trigger` header value from queued event names.
*
* @param array<int, string|HtmxEvent> $names
*/
public static function triggerHeader(array $names): string
{
return implode(',', self::expand($names));
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Leantime\Core\Events\Htmx;
/**
* Canonical client UI command events (the `lt:ui:*` plane).
*
* These are imperative commands to the JS layer (show a toast, close the modal, refresh the page),
* as opposed to domain data events ("entity X changed") which live in per-domain Htmx{Domain}Events
* enums. There is exactly one home for UI commands so casing/naming never drifts again — plugins
* reuse these cases rather than minting their own `lt:ui:*` strings.
*
* Legacy string equivalents (e.g. HTMX.ShowNotification, closeModal) are dual-emitted during the
* migration window via {@see HtmxEvents::expand()} so existing listeners keep working.
*/
enum HtmxUiEvents: string implements HtmxEvent
{
use InteractsWithHtmxEvents;
/** Fetch + show the latest growl notification. Replaces 'HTMX.ShowNotification'. */
case Notify = 'lt:ui:notify';
/** Close the top-most modal. Replaces 'closeModal' / 'HTMX.closemodal' / 'Htmx.CloseModal'. */
case ModalClose = 'lt:ui:modal.close';
/** Open a modal for the current url hash. */
case ModalOpen = 'lt:ui:modal.open';
/** Refresh the main page url in the background. */
case UrlRefresh = 'lt:ui:url.refresh';
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Leantime\Core\Events\Htmx;
/**
* Shared behavior for string-backed client (HTMX) event enums.
*
* The backing value of each case is the wire name. PHP enums cannot define __toString, so use
* {@see event()} to obtain the wire name in PHP code; Blade's `{{ }}` renders the value directly
* because Laravel's e() helper unwraps backed enums.
*/
trait InteractsWithHtmxEvents
{
/**
* The wire name (the enum's backing value).
*/
public function event(): string
{
return $this->value;
}
/**
* Wire name scoped to a single entity id, e.g. "lt:reactions:sentiment.updated#42".
*/
public function scoped(int|string $id): string
{
return $this->value.'#'.$id;
}
/**
* Format for an `hx-trigger` attribute, e.g. "lt:tickets:ticket.updated from:body".
*/
public function trigger(string $from = 'body'): string
{
return $this->value.($from !== '' ? ' from:'.$from : '');
}
}