diff --git a/README.md b/README.md index ddeadd9f..77110b7a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ ## What you get | 📅  **One calendar, every network** | Plan a month at a glance, drag any post to a new slot, and publish natively to 12 platforms. No redirects, no "finish in the mobile app." | | ✨  **An AI copilot that knows your brand** | Captions, hooks, full drafts, and multi-slide carousels in your tone, voice, and colors. It reads your brand profile on every generation. | | 🤖  **Built for AI agents** | A first-class MCP server and REST API. Claude, Cursor, ChatGPT, or your own scripts can draft, schedule, and publish for you. | -| ⚙️  **Automations that run themselves** | A visual workflow builder: triggers, conditions, RSS, HTTP requests, and AI generation, all server-side. Set it once, let it post. | | 🗂️  **Made for many clients** | Workspaces, roles, and approval flows so an agency or freelancer can run a roster of brands without the spreadsheets. | ## Features @@ -47,7 +46,6 @@ ## Features | **AI generate & review** | Draft from a prompt, get inline feedback before you publish. | | **AI carousel builder** | Prompt to a multi-slide carousel with images, on-brand. | | **Brand profile** | Tone, voice, language, and colors applied to every AI call. | -| **Automations** | Schedule / RSS triggers, conditions, publish steps, and HTTP requests. | | **Asset library** | Reusable workspace media, plus Unsplash and Giphy search built in. | | **Signatures & labels** | Reusable hashtag and CTA blocks, color-coded post tags. | | **Team collaboration** | Owner / Admin / Member roles, comments with @mentions on drafts. | diff --git a/app/Actions/Automation/Automation/ActivateAutomation.php b/app/Actions/Automation/Automation/ActivateAutomation.php deleted file mode 100644 index a06d340f..00000000 --- a/app/Actions/Automation/Automation/ActivateAutomation.php +++ /dev/null @@ -1,52 +0,0 @@ -validate($automation); - - $automation->update([ - 'status' => Status::Active, - 'activated_at' => now(), - 'paused_at' => null, - ]); - - return $automation; - } - - private function validate(Automation $automation): void - { - $nodes = $automation->nodes ?? []; - $connections = $automation->connections ?? []; - - $triggers = collect($nodes)->where('type', NodeType::Trigger->value); - if ($triggers->count() !== 1) { - throw new DomainException(__('automations.errors.must_have_one_trigger')); - } - - $trigger = $triggers->first(); - $hasTargetFromTrigger = collect($connections)->contains('source', $trigger['id']); - if (! $hasTargetFromTrigger) { - throw new DomainException(__('automations.errors.trigger_must_be_connected')); - } - - $issue = $this->configValidator->firstMessage($nodes); - - if ($issue !== null) { - throw new DomainException($issue); - } - } -} diff --git a/app/Actions/Automation/Automation/CreateAutomation.php b/app/Actions/Automation/Automation/CreateAutomation.php deleted file mode 100644 index 41d29fb0..00000000 --- a/app/Actions/Automation/Automation/CreateAutomation.php +++ /dev/null @@ -1,53 +0,0 @@ - $workspace->id, - 'user_id' => $user->id, - 'name' => $name ?: __('automations.default_name'), - 'status' => Status::Draft, - 'nodes' => [$this->defaultTriggerNode()], - 'connections' => [], - ]); - } - - /** - * Every automation has exactly one trigger — its entry point — so we seed it - * on creation. The trigger can't be added or deleted from the editor; only - * its type (schedule / post published / post scheduled) is configurable. - * - * @return array - */ - private function defaultTriggerNode(): array - { - return [ - 'id' => (string) Str::uuid(), - 'type' => 'trigger', - 'position' => ['x' => 0, 'y' => 0], - 'data' => [ - 'trigger_type' => TriggerType::Schedule->value, - 'cron' => '0 9 * * *', - 'schedule_field' => ScheduleField::Days->value, - 'schedule_days_interval' => 1, - 'schedule_hour' => 9, - 'schedule_minute' => 0, - 'schedule_timezone' => config('app.timezone'), - ], - ]; - } -} diff --git a/app/Actions/Automation/Automation/DeleteAutomation.php b/app/Actions/Automation/Automation/DeleteAutomation.php deleted file mode 100644 index 1d4d4719..00000000 --- a/app/Actions/Automation/Automation/DeleteAutomation.php +++ /dev/null @@ -1,15 +0,0 @@ -delete(); - } -} diff --git a/app/Actions/Automation/Automation/GetAutomationEditorData.php b/app/Actions/Automation/Automation/GetAutomationEditorData.php deleted file mode 100644 index 3db30dad..00000000 --- a/app/Actions/Automation/Automation/GetAutomationEditorData.php +++ /dev/null @@ -1,59 +0,0 @@ -, - * pinterestBoards: SupportCollection, truncated: bool}>, - * tiktokCreatorInfos: SupportCollection, - * } - */ - public function __invoke(Automation $automation): array - { - $socialAccounts = $automation->workspace->socialAccounts()->active()->get(); - - $pinterestBoards = $socialAccounts - ->where('platform', Platform::Pinterest) - ->mapWithKeys(fn ($account) => [ - $account->id => rescue( - fn () => ListPinterestBoards::execute($account), - ['boards' => [], 'truncated' => false], - report: false, - ), - ]); - - $tiktokCreatorInfos = $socialAccounts - ->where('platform', Platform::TikTok) - ->mapWithKeys(fn ($account) => [ - $account->id => rescue( - fn () => $this->tikTokCreatorInfo->fetch($account), - null, - report: false, - ), - ]) - ->filter(); - - return [ - 'socialAccounts' => $socialAccounts, - 'pinterestBoards' => $pinterestBoards, - 'tiktokCreatorInfos' => $tiktokCreatorInfos, - ]; - } -} diff --git a/app/Actions/Automation/Automation/GetAutomationInvocations.php b/app/Actions/Automation/Automation/GetAutomationInvocations.php deleted file mode 100644 index d9b45f5c..00000000 --- a/app/Actions/Automation/Automation/GetAutomationInvocations.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ - public function __invoke(Automation $automation, ?string $status = null, ?string $search = null): LengthAwarePaginator|CursorPaginator - { - return $automation->runs() - ->productionRuns() - ->withCount('nodeRuns') - ->when($status !== null, fn ($query) => $query->where('status', $status)) - ->when($search !== null && $search !== '', fn ($query) => $query->whereLike('id', "%{$search}%")) - ->latest() - ->paginate((int) config('app.pagination.default')); - } -} diff --git a/app/Actions/Automation/Automation/GetAutomationMetrics.php b/app/Actions/Automation/Automation/GetAutomationMetrics.php deleted file mode 100644 index 422c0ba9..00000000 --- a/app/Actions/Automation/Automation/GetAutomationMetrics.php +++ /dev/null @@ -1,123 +0,0 @@ -, - * platforms: array, - * } - */ - public function __invoke(Automation $automation, CarbonInterface $start, CarbonInterface $end): array - { - $start = $start->copy()->startOfDay(); - $end = $end->copy()->endOfDay(); - $days = (int) $start->diffInDays($end) + 1; - - $runs = $automation->runs() - ->productionRuns() - ->whereBetween('created_at', [$start, $end]) - ->get(['id', 'status', 'generated_post_id', 'created_at', 'started_at', 'finished_at']); - - $completed = $runs->where('status', Status::Completed); - $failed = $runs->where('status', Status::Failed); - $inProgress = $runs->whereIn('status', [Status::Pending, Status::Running, Status::Waiting]); - - $finished = $completed->count() + $failed->count(); - $successRate = $finished > 0 ? (int) round($completed->count() / $finished * 100) : null; - - $durations = $completed - ->map(fn ($run) => $run->durationInMilliseconds()) - ->filter(fn ($ms) => $ms !== null); - $avgDurationMs = $durations->isNotEmpty() ? (int) round($durations->avg()) : null; - - return [ - 'totals' => [ - 'runs' => $runs->count(), - 'completed' => $completed->count(), - 'failed' => $failed->count(), - 'in_progress' => $inProgress->count(), - 'success_rate' => $successRate, - 'avg_duration_ms' => $avgDurationMs, - 'posts_created' => $runs->whereNotNull('generated_post_id')->count(), - ], - 'timeseries' => $this->buildTimeseries($runs, $start, $days), - 'platforms' => $this->buildPlatformBreakdown($runs->pluck('generated_post_id')->filter()->all()), - ]; - } - - /** - * Zero-filled daily buckets so the chart line stays continuous across days - * with no runs. - * - * @param Collection $runs - * @return array - */ - private function buildTimeseries(Collection $runs, CarbonInterface $since, int $days): array - { - $series = []; - for ($i = 0; $i < $days; $i++) { - $date = $since->copy()->addDays($i)->format('Y-m-d'); - $series[$date] = ['date' => $date, 'started' => 0, 'completed' => 0, 'failed' => 0]; - } - - foreach ($runs as $run) { - $date = $run->created_at->format('Y-m-d'); - - if (! isset($series[$date])) { - continue; - } - - $series[$date]['started']++; - - if ($run->status === Status::Completed) { - $series[$date]['completed']++; - } - - if ($run->status === Status::Failed) { - $series[$date]['failed']++; - } - } - - return array_values($series); - } - - /** - * Count published platform targets across the posts this automation - * generated, so the chart shows where its output actually went. - * - * @param array $postIds - * @return array - */ - private function buildPlatformBreakdown(array $postIds): array - { - if ($postIds === []) { - return []; - } - - return PostPlatform::query() - ->whereIn('post_id', $postIds) - ->selectRaw('platform, count(*) as total') - ->groupBy('platform') - ->orderByDesc('total') - ->get() - ->map(fn ($row) => ['platform' => $row->platform->value, 'count' => (int) $row->total]) - ->all(); - } -} diff --git a/app/Actions/Automation/Automation/ListAutomations.php b/app/Actions/Automation/Automation/ListAutomations.php deleted file mode 100644 index af9bd7f9..00000000 --- a/app/Actions/Automation/Automation/ListAutomations.php +++ /dev/null @@ -1,20 +0,0 @@ -where('workspace_id', $workspace->id) - ->orderByDesc('created_at') - ->paginate((int) config('app.pagination.default')); - } -} diff --git a/app/Actions/Automation/Automation/PauseAutomation.php b/app/Actions/Automation/Automation/PauseAutomation.php deleted file mode 100644 index 96f20cb0..00000000 --- a/app/Actions/Automation/Automation/PauseAutomation.php +++ /dev/null @@ -1,21 +0,0 @@ -update([ - 'status' => Status::Paused, - 'paused_at' => now(), - ]); - - return $automation; - } -} diff --git a/app/Actions/Automation/Automation/UpdateAutomation.php b/app/Actions/Automation/Automation/UpdateAutomation.php deleted file mode 100644 index 02195e78..00000000 --- a/app/Actions/Automation/Automation/UpdateAutomation.php +++ /dev/null @@ -1,66 +0,0 @@ -detectCycles($data['nodes'] ?? [], $data['connections'] ?? []); - - $automation->update([ - 'name' => $data['name'] ?? $automation->name, - 'nodes' => $data['nodes'] ?? $automation->nodes, - 'connections' => $data['connections'] ?? $automation->connections, - 'variables' => $data['variables'] ?? $automation->variables, - ]); - - return $automation->fresh(); - } - - private function detectCycles(array $nodes, array $connections): void - { - $adj = []; - foreach ($connections as $c) { - $adj[$c['source']][] = $c['target']; - } - - /** @var array $state state: 'white' (unvisited), 'gray' (in stack), 'black' (done) */ - $state = []; - foreach ($nodes as $node) { - $state[$node['id']] = 'white'; - } - - foreach ($nodes as $node) { - if ($state[$node['id']] === 'white' && $this->hasCycleFrom($node['id'], $adj, $state)) { - throw new DomainException(__('automations.errors.graph_contains_cycle')); - } - } - } - - private function hasCycleFrom(string $node, array $adj, array &$state): bool - { - $state[$node] = 'gray'; - - foreach ($adj[$node] ?? [] as $next) { - if (! isset($state[$next])) { - continue; - } - if ($state[$next] === 'gray') { - return true; - } - if ($state[$next] === 'white' && $this->hasCycleFrom($next, $adj, $state)) { - return true; - } - } - - $state[$node] = 'black'; - - return false; - } -} diff --git a/app/Actions/Automation/Node/RunConditionNode.php b/app/Actions/Automation/Node/RunConditionNode.php deleted file mode 100644 index 7e3ea79d..00000000 --- a/app/Actions/Automation/Node/RunConditionNode.php +++ /dev/null @@ -1,64 +0,0 @@ -resolverContext(); - $field = $this->resolver->resolve((string) data_get($config, 'field', ''), $context); - $operator = Operator::from(data_get($config, 'operator', Operator::Equals->value)); - $value = $this->resolver->resolve((string) data_get($config, 'value', ''), $context); - - $matched = match ($operator) { - Operator::Contains => str_contains($field, $value), - Operator::NotContains => ! str_contains($field, $value), - Operator::Equals => $field === $value, - Operator::NotEquals => $field !== $value, - Operator::Matches => $this->safeRegexMatch($value, $field), - Operator::GreaterThan => is_numeric($field) && is_numeric($value) && (float) $field > (float) $value, - Operator::LessThan => is_numeric($field) && is_numeric($value) && (float) $field < (float) $value, - }; - - return NodeRunResult::completed( - output: ['condition' => ['resolved_field' => $field, 'matched' => $matched]], - nextHandle: ($matched ? Handle::Yes : Handle::No)->value, - ); - } - - private function safeRegexMatch(string $pattern, string $subject): bool - { - if (strlen($pattern) > self::MAX_REGEX_LENGTH) { - return false; - } - - $escaped = str_replace('~', '\~', $pattern); - $regex = "~{$escaped}~u"; - - try { - $result = @preg_match($regex, $subject); - } catch (Throwable) { - return false; - } - - if ($result === false || preg_last_error() !== PREG_NO_ERROR) { - return false; - } - - return $result === 1; - } -} diff --git a/app/Actions/Automation/Node/RunDelayNode.php b/app/Actions/Automation/Node/RunDelayNode.php deleted file mode 100644 index 2ae4f8cc..00000000 --- a/app/Actions/Automation/Node/RunDelayNode.php +++ /dev/null @@ -1,28 +0,0 @@ -value); - - $until = match (DelayUnit::tryFrom((string) $unit)) { - DelayUnit::Minutes => now()->addMinutes($duration), - DelayUnit::Hours => now()->addHours($duration), - DelayUnit::Days => now()->addDays($duration), - default => throw new InvalidArgumentException("Unknown delay unit: {$unit}"), - }; - - return NodeRunResult::sleep($until); - } -} diff --git a/app/Actions/Automation/Node/RunEndNode.php b/app/Actions/Automation/Node/RunEndNode.php deleted file mode 100644 index f03dcba5..00000000 --- a/app/Actions/Automation/Node/RunEndNode.php +++ /dev/null @@ -1,23 +0,0 @@ - [ - 'ended_at' => now()->toIso8601String(), - 'reason' => $reason ?: null, - ], - ]); - } -} diff --git a/app/Actions/Automation/Node/RunFetchRssNode.php b/app/Actions/Automation/Node/RunFetchRssNode.php deleted file mode 100644 index 5b9866a8..00000000 --- a/app/Actions/Automation/Node/RunFetchRssNode.php +++ /dev/null @@ -1,221 +0,0 @@ -resolver->resolve((string) data_get($config, 'feed_url', ''), $run->resolverContext()); - - if ($feedUrl === '') { - return NodeRunResult::failed(__('automations.errors.fetch_rss_missing_url')); - } - - // SafeHttpFetcher::get() re-validates every redirect hop against the SSRF - // guard (not just the initial URL), so a public feed that 302s to an - // internal host is never followed. It throws on a blocked hop, connection - // failure, non-2xx status, or an excessive redirect chain — all of which - // are legitimate "this feed couldn't be fetched" failures for this node. - try { - $response = $this->safeHttp->get($feedUrl); - } catch (RuntimeException $e) { - return NodeRunResult::failed(__('automations.errors.fetch_rss_request_failed'), [ - 'message' => $e->getMessage(), - ]); - } - - $items = $this->parser->parse($response->body()); - - if ($items === null) { - return NodeRunResult::failed(__('automations.errors.fetch_rss_malformed')); - } - - $nodeId = (string) $run->current_node_id; - // Test runs (dry OR real-data) bypass the watermark entirely: they use an - // epoch watermark so every item is treated as new, process only the first - // item, and never spawn siblings or advance the watermark. This way a - // test ALWAYS shows real data flowing through (instead of "no new items") - // and never floods the feed or poisons the production watermark. - $isPreview = $run->is_manual || $run->is_dry_run; - $state = $isPreview ? null : AutomationNodeState::for($run->automation_id, $nodeId); - $watermark = $isPreview - ? CarbonImmutable::createFromTimestamp(0) - : $this->parseWatermark(data_get($state->data, 'last_item_date')); - - [$newItems, $newestSeen] = $this->collectNewItems($items, $watermark); - - if ($state !== null && $newestSeen !== null) { - $state->update(['data' => array_merge($state->data ?? [], [ - 'last_item_date' => $newestSeen->toIso8601String(), - ])]); - } - - if ($newItems === []) { - return NodeRunResult::completed(['fetch' => ['count' => 0]], nextHandle: self::NO_ITEMS_HANDLE); - } - - $total = count($newItems); - - // A preview (manual/dry test) surfaces ONE item and never fans out, so it - // shows the newest item — what the user expects to test against. A real run - // takes the oldest new item and fans the rest out as siblings, preserving - // feed chronology across branches (items are sorted oldest-first). - if ($isPreview) { - return NodeRunResult::completed([ - 'fetch' => ['count' => $total, 'spawned' => 0], - 'fetched' => end($newItems), - ]); - } - - $first = array_shift($newItems); - $this->spawnSiblings($run, $nodeId, $newItems); - - return NodeRunResult::completed([ - 'fetch' => ['count' => $total, 'spawned' => count($newItems)], - 'fetched' => $first, - ]); - } - - /** - * @param list> $parsed Normalized items from FeedParser. - * @return array{0: list>, 1: ?CarbonImmutable} - */ - private function collectNewItems(array $parsed, CarbonImmutable $watermark): array - { - $items = []; - $newestSeen = null; - - foreach ($parsed as $item) { - $key = (string) data_get($item, 'key', ''); - if ($key === '') { - continue; - } - - $date = $this->parsePubDate((string) data_get($item, 'date', '')); - if ($date === null) { - continue; - } - - if ($newestSeen === null || $date->greaterThan($newestSeen)) { - $newestSeen = $date; - } - - if (! $date->greaterThan($watermark)) { - continue; - } - - $item['_sort'] = $date->getTimestamp(); - $items[] = $item; - } - - // Process oldest-first so siblings inherit a stable order matching feed chronology. - usort($items, fn ($a, $b) => $a['_sort'] <=> $b['_sort']); - - // Drop the internal sort key — downstream nodes shouldn't see it. - $items = array_map(function (array $item): array { - unset($item['_sort']); - - return $item; - }, $items); - - return [$items, $newestSeen]; - } - - private function spawnSiblings(AutomationRun $parent, string $fetchNodeId, array $items): void - { - if ($items === []) { - return; - } - - // Each remaining item gets its own run that fans out across EVERY branch - // wired to the fetch node — matching how item[0] (the current run) fans - // out, so no branch silently drops items 2..N. - $targets = $this->advance->targetsFor($parent->automation, $fetchNodeId, self::ITEM_HANDLE); - - foreach ($items as $item) { - $sibling = AutomationRun::create([ - 'automation_id' => $parent->automation_id, - 'root_run_id' => $parent->rootId(), - 'trigger_item_id' => $parent->trigger_item_id, - 'generated_post_id' => $parent->generated_post_id, - 'is_manual' => $parent->is_manual, - 'is_dry_run' => $parent->is_dry_run, - 'status' => RunStatus::Pending, - 'context' => array_merge($parent->context ?? [], ['fetched' => $item]), - ]); - - if ($targets === []) { - $sibling->update(['status' => RunStatus::Completed, 'finished_at' => now()]); - - continue; - } - - $this->advance->dispatchBranches($sibling, $targets); - } - } - - private function parseWatermark(?string $stored): CarbonImmutable - { - if ($stored === null) { - return CarbonImmutable::now(); - } - - try { - return CarbonImmutable::parse($stored); - } catch (Throwable) { - return CarbonImmutable::now(); - } - } - - private function parsePubDate(string $raw): ?CarbonImmutable - { - if ($raw === '') { - return null; - } - - try { - return CarbonImmutable::parse($raw); - } catch (Throwable) { - return null; - } - } -} diff --git a/app/Actions/Automation/Node/RunGenerateNode.php b/app/Actions/Automation/Node/RunGenerateNode.php deleted file mode 100644 index 7e450095..00000000 --- a/app/Actions/Automation/Node/RunGenerateNode.php +++ /dev/null @@ -1,377 +0,0 @@ -resolverContext(); - $prompt = $this->resolver->resolve((string) data_get($config, 'prompt_template', ''), $context); - - $accountsConfig = $this->resolveAccountsConfig($config); - ['format' => $format, 'slide_count' => $slideCount] = $this->deriveFormat($accountsConfig, $config); - - $accountIds = array_values(array_filter(array_map( - fn ($a) => data_get($a, 'social_account_id'), - $accountsConfig, - ))); - - $workspace = $run->automation->workspace; - - $activeAccounts = SocialAccount::query() - ->whereIn('id', $accountIds) - ->where('workspace_id', $workspace->id) - ->active() - ->get() - ->keyBy('id'); - - $applyBrandVoice = (bool) data_get($config, 'use_brand_voice', true); - - $platformContext = $this->resolvePlatformContext($accountsConfig); - - $style = ContentStyle::tryFrom((string) data_get($config, 'style', ContentStyle::default()->value)) ?? ContentStyle::default(); - $styleTemplate = app(AiTemplateRegistry::class)->find($style); - - $platforms = []; - foreach ($accountsConfig as $entry) { - $accountId = data_get($entry, 'social_account_id'); - if (! $accountId || ! $activeAccounts->has($accountId)) { - if ($accountId) { - Log::warning('RunGenerateNode: account no longer active, skipping', [ - 'automation_id' => $run->automation_id, - 'social_account_id' => $accountId, - ]); - } - - continue; - } - - $platforms[] = [ - 'social_account_id' => $accountId, - 'content_type' => data_get($entry, 'content_type'), - 'meta' => data_get($entry, 'meta', []), - ]; - } - - $wantsImage = (int) data_get($config, 'target_slide_count', 1) >= 1; - - $brandAccount = $platforms !== [] - ? $activeAccounts->get(data_get($platforms[0], 'social_account_id')) - : null; - - $isCarousel = $format->isCarousel(); - $imageCount = $isCarousel ? $slideCount : ($wantsImage ? 1 : 0); - - $templateContext = new TemplateContext( - workspace: $workspace, - socialAccount: $brandAccount, - format: $platformContext ?? $format->value, - imageCount: $imageCount, - isCarousel: $isCarousel, - applyBrandVisuals: (bool) data_get($config, 'use_brand_visuals', true), - ); - - $agent = new PostContentGenerator( - workspace: $workspace, - format: $format, - slideCount: $slideCount, - platformContext: $platformContext, - applyBrandVoice: $applyBrandVoice, - template: $styleTemplate, - templateContext: $templateContext, - ); - - $generatorResponse = $agent->prompt($prompt); - - RecordAiUsage::recordText( - workspace: $workspace, - promptTokens: $generatorResponse->usage->promptTokens, - completionTokens: $generatorResponse->usage->completionTokens, - provider: (string) $generatorResponse->meta->provider, - model: (string) $generatorResponse->meta->model, - metadata: ['agent' => 'post_generator', 'format' => $format->value, 'source' => 'automation'], - ); - - $structured = $generatorResponse->structured ?? []; - - $structured = $this->humanize($workspace, $structured, $format, $style, $applyBrandVoice, $platformContext); - - $intendedImageCount = $this->intendedImageCount($format, $slideCount, $wantsImage, $structured, $brandAccount, $style); - - if ($run->is_dry_run) { - $dryContent = $this->extractContent($structured, $format, $style); - - return NodeRunResult::completed(output: [ - 'generated' => [ - 'post_id' => null, - 'content' => $dryContent, - 'dry_run' => true, - 'image_count' => $intendedImageCount, - ], - ]); - } - - $generated = $styleTemplate->assemble($structured, $templateContext); - - $user = $this->resolveUser($run); - - $post = CreatePost::execute($workspace, $user, [ - 'content' => $generated->content, - 'media' => $generated->media, - 'platforms' => $platforms, - 'created_via' => CreatedVia::Automation, - ]); - - $run->update(['generated_post_id' => $post->id]); - - return NodeRunResult::completed(output: [ - 'generated' => [ - 'post_id' => $post->id, - 'content' => $generated->content, - 'post_url' => route('app.posts.show', $post->id), - ], - ]); - } - - /** - * @param array $structured - * @return array - */ - private function humanize(Workspace $workspace, array $structured, GeneratorFormat $format, ContentStyle $style, bool $applyBrandVoice = true, ?string $platformContext = null): array - { - if (! $style->humanizes()) { - return $structured; - } - - try { - $input = $format->isCarousel() - ? [ - 'caption' => data_get($structured, 'caption', ''), - 'slides' => array_map( - fn ($s) => [ - 'title' => data_get($s, 'title', ''), - 'body' => data_get($s, 'body', ''), - ], - data_get($structured, 'slides', []), - ), - ] - : [ - 'content' => data_get($structured, 'content', ''), - 'image_title' => data_get($structured, 'image_title', ''), - 'image_body' => data_get($structured, 'image_body', ''), - ]; - - $humanizer = new PostContentHumanizer($workspace, $format, platformContext: $platformContext, applyBrandVoice: $applyBrandVoice); - $response = $humanizer->prompt(json_encode($input, JSON_UNESCAPED_UNICODE)); - $humanized = $response->structured ?? []; - - RecordAiUsage::recordText( - workspace: $workspace, - promptTokens: $response->usage->promptTokens, - completionTokens: $response->usage->completionTokens, - provider: (string) $response->meta->provider, - model: (string) $response->meta->model, - metadata: ['agent' => 'post_humanizer', 'format' => $format->value, 'source' => 'automation'], - ); - - if ($format->isCarousel()) { - $structured['caption'] = data_get($humanized, 'caption', data_get($structured, 'caption', '')); - $originalSlides = data_get($structured, 'slides', []); - $humanizedSlides = data_get($humanized, 'slides', []); - - foreach ($originalSlides as $i => $slide) { - if (isset($humanizedSlides[$i])) { - $originalSlides[$i]['title'] = data_get($humanizedSlides[$i], 'title', data_get($slide, 'title', '')); - $originalSlides[$i]['body'] = data_get($humanizedSlides[$i], 'body', data_get($slide, 'body', '')); - } - } - - $structured['slides'] = $originalSlides; - } else { - $structured['content'] = data_get($humanized, 'content', data_get($structured, 'content', '')); - $structured['image_title'] = data_get($humanized, 'image_title', data_get($structured, 'image_title', '')); - $structured['image_body'] = data_get($humanized, 'image_body', data_get($structured, 'image_body', '')); - } - } catch (Throwable $e) { - Log::warning('RunGenerateNode: PostContentHumanizer failed, using generator output as-is', [ - 'error' => $e->getMessage(), - ]); - } - - return $structured; - } - - /** - * Extract the post caption from the raw structured output without calling - * assemble() (which triggers image generation). Used for dry-run responses - * so no pipeline work happens during test runs. - * - * @param array $structured - */ - private function extractContent(array $structured, GeneratorFormat $format, ContentStyle $style): string - { - if ($style->isTweetCard()) { - return $format->isCarousel() - ? (string) data_get($structured, 'caption', '') - : (string) data_get($structured, 'tweet_text', ''); - } - - return $format->isCarousel() - ? (string) data_get($structured, 'caption', '') - : (string) data_get($structured, 'content', ''); - } - - /** - * Derive the generator format and slide count from per-account content types. - * - * Carousel-capable content types: - * - instagram_feed (Instagram feed carousel = multi-image feed post) - * - linkedin_post (LinkedIn multi-image post — 2+ images) - * - linkedin_page_post (LinkedIn page multi-image post) - * - pinterest_carousel (Pinterest carousel pin) - * - tiktok_photo (TikTok photo carousel) - * - * When at least one account has a carousel-capable content type AND - * target_slide_count > 1, the generator is told to produce a carousel with - * that many slides. Otherwise it falls back to a single-post format. - * - * @param array}> $accountsConfig - * @param array $config - * @return array{format: GeneratorFormat, slide_count: int} - */ - public function deriveFormat(array $accountsConfig, array $config): array - { - $maxImagesAcross = 0; - foreach ($accountsConfig as $entry) { - $contentType = ContentType::tryFrom((string) data_get($entry, 'content_type')); - if ($contentType instanceof ContentType && $contentType->supportsImage() && $contentType->maxMediaCount() > 1) { - $maxImagesAcross = max($maxImagesAcross, $contentType->maxMediaCount()); - } - } - - $targetSlideCount = (int) data_get($config, 'target_slide_count', 1); - - if ($maxImagesAcross > 1 && $targetSlideCount > 1) { - $cap = min(GenerateNodeValidator::MAX_GENERATED_IMAGES, $maxImagesAcross); - - return ['format' => GeneratorFormat::Carousel, 'slide_count' => min($targetSlideCount, $cap)]; - } - - return ['format' => GeneratorFormat::Single, 'slide_count' => 1]; - } - - /** - * Pick the content type the generator should write for so the copy fits - * every selected network. A Generate node can target one or many accounts, - * each with its own content type, so we feed the generator the MOST - * RESTRICTIVE platform (smallest character cap) — content that fits X (280) - * also fits LinkedIn (3000). Returns null when no account carries a known - * content type, leaving the generator platform-agnostic. - * - * @param array}> $accountsConfig - */ - private function resolvePlatformContext(array $accountsConfig): ?string - { - return collect($accountsConfig) - ->map(fn ($entry) => ContentType::tryFrom((string) data_get($entry, 'content_type'))) - ->filter() - ->sortBy(fn (ContentType $contentType) => $contentType->platform()->maxContentLength()) - ->first()?->value; - } - - /** - * Number of images that would be attached for the resolved format. Used as - * the dry-run indicator and mirrors the non-dry image generation branches: - * one per slide for carousels, one for single posts when images are enabled. - * Tweet styles always produce one image per slide/post when an account is set. - * - * @param array $structured - */ - private function intendedImageCount(GeneratorFormat $format, int $slideCount, bool $wantsImage, array $structured, ?SocialAccount $brandAccount, ContentStyle $style): int - { - if (! $brandAccount) { - return 0; - } - - if ($style->isTweetCard()) { - return $format->isCarousel() ? $slideCount : 1; - } - - if ($format->isCarousel()) { - $slides = data_get($structured, 'slides', []); - - return is_array($slides) ? count($slides) : $slideCount; - } - - return $wantsImage ? 1 : 0; - } - - private function resolveUser(AutomationRun $run): User - { - if ($run->automation->user_id) { - return $run->automation->user; - } - - return $run->automation->workspace->owner; - } - - /** - * Read the current `accounts` shape and fall back to the legacy - * `social_account_ids` array so older automations keep running until - * the user re-opens and saves the node. - * - * @param array $config - * @return array}> - */ - private function resolveAccountsConfig(array $config): array - { - $accounts = data_get($config, 'accounts'); - - if (is_array($accounts)) { - return array_values(array_map(fn ($entry) => [ - 'social_account_id' => (string) data_get($entry, 'social_account_id', ''), - 'content_type' => data_get($entry, 'content_type'), - 'meta' => (array) data_get($entry, 'meta', []), - ], $accounts)); - } - - $legacy = data_get($config, 'social_account_ids', []); - - if (! is_array($legacy)) { - return []; - } - - return array_values(array_map(fn ($id) => [ - 'social_account_id' => (string) $id, - 'content_type' => null, - 'meta' => [], - ], $legacy)); - } -} diff --git a/app/Actions/Automation/Node/RunHttpRequestNode.php b/app/Actions/Automation/Node/RunHttpRequestNode.php deleted file mode 100644 index 15063a78..00000000 --- a/app/Actions/Automation/Node/RunHttpRequestNode.php +++ /dev/null @@ -1,473 +0,0 @@ -value)); - $nodeId = (string) $run->current_node_id; - $context = $run->resolverContext(); - - if ($url === '') { - return NodeRunResult::failed(__('automations.errors.http_missing_url')); - } - - $resolvedUrl = $this->resolver->resolve($url, $context); - - try { - $this->safeHttp->guardAgainstSsrf($resolvedUrl); - } catch (RuntimeException) { - return NodeRunResult::failed(__('automations.errors.url_not_allowed'), [ - 'reason' => 'url_not_allowed', - 'url' => $resolvedUrl, - ]); - } - - $request = $this->buildRequest($config, $context); - $jsonBody = $this->buildJsonBody($method, $config, $context); - - try { - $response = match (HttpMethod::tryFrom($method)) { - HttpMethod::Get => $request->get($resolvedUrl), - HttpMethod::Delete => $request->delete($resolvedUrl), - HttpMethod::Post => $request->post($resolvedUrl, $jsonBody), - HttpMethod::Put => $request->put($resolvedUrl, $jsonBody), - HttpMethod::Patch => $request->patch($resolvedUrl, $jsonBody), - default => null, - }; - } catch (Throwable $e) { - return NodeRunResult::failed(__('automations.errors.http_request_exception'), ['message' => $e->getMessage()]); - } - - if ($response === null) { - return NodeRunResult::failed("Unsupported HTTP method: {$method}"); - } - - if (! $response->successful()) { - return NodeRunResult::failed(__('automations.errors.http_request_failed'), [ - 'status' => $response->status(), - 'body' => substr($response->body(), 0, 500), - ]); - } - - $payload = $this->decodeBody($response); - $itemsPath = is_string($raw = data_get($config, 'items_path')) ? trim($raw) : ''; - - // No path + a top-level array (or NDJSON list) → iterate it. No path + a - // single object/scalar → single-response mode: forward the whole body. - if ($itemsPath === '') { - if (! is_array($payload) || ! array_is_list($payload)) { - return NodeRunResult::completed([ - 'fetch' => ['count' => 1, 'spawned' => 0], - 'fetched' => $payload, - ]); - } - - return $this->processItems($run, $nodeId, $config, $payload); - } - - // Explicit path: dot notation, or `*` to iterate a top-level object map. - $resolved = data_get($payload, $itemsPath); - - if (! is_array($resolved)) { - return NodeRunResult::failed(__('automations.errors.http_items_path_not_array')); - } - - return $this->processItems($run, $nodeId, $config, array_values($resolved)); - } - - /** - * Decodes the response as JSON, falling back to NDJSON (one JSON value per - * line) so streaming/log-style list endpoints work too. - */ - private function decodeBody(Response $response): mixed - { - $json = $response->json(); - - if (is_array($json)) { - return $json; - } - - return $this->parseNdjson($response->body()) ?? $json; - } - - /** - * @return array|null The decoded list, or null when the body is - * not newline-delimited JSON. - */ - private function parseNdjson(string $body): ?array - { - $lines = preg_split('/\r\n|\r|\n/', trim($body)) ?: []; - $items = []; - - foreach ($lines as $line) { - $line = trim($line); - if ($line === '') { - continue; - } - - $decoded = json_decode($line, true); - if ($decoded === null && $line !== 'null') { - return null; - } - - $items[] = $decoded; - } - - return count($items) > 1 ? $items : null; - } - - /** - * @param array $config - * @param array $items - */ - private function processItems(AutomationRun $run, string $nodeId, array $config, array $items): NodeRunResult - { - $itemKeyPath = is_string($k = data_get($config, 'item_key_path')) ? trim($k) : ''; - $itemDatePath = is_string($d = data_get($config, 'item_date_path')) ? trim($d) : ''; - - // Test runs (dry OR real-data) walk a single item so the user sees data - // flow, without spawning siblings, advancing watermarks or recording keys. - if ($run->is_manual || $run->is_dry_run) { - if ($items === []) { - return NodeRunResult::completed(['fetch' => ['count' => 0]], nextHandle: self::NO_ITEMS_HANDLE); - } - - return NodeRunResult::completed([ - 'fetch' => ['count' => count($items), 'spawned' => 0], - 'fetched' => $items[0], - ]); - } - - // A date watermark is cheapest and bounded, so it wins when both are set; - // the seen-key set is the fallback for feeds without a usable date. - $newItems = match (true) { - $itemDatePath !== '' => $this->filterByDate($run->automation_id, $nodeId, $items, $itemDatePath), - $itemKeyPath !== '' => $this->filterBySeenKeys($run->automation_id, $nodeId, $items, $itemKeyPath), - default => $items, - }; - - if ($newItems === []) { - return NodeRunResult::completed(['fetch' => ['count' => 0]], nextHandle: self::NO_ITEMS_HANDLE); - } - - $first = array_shift($newItems); - $this->spawnSiblings($run, $nodeId, $newItems); - - return NodeRunResult::completed([ - 'fetch' => ['count' => count($newItems) + 1, 'spawned' => count($newItems)], - 'fetched' => $first, - ]); - } - - /** - * Keeps only items newer than the per-node date watermark, then advances the - * watermark to the newest date seen. The first poll records the baseline and - * emits nothing, so an existing feed never floods on day one. - * - * @param array $items - * @return array - */ - private function filterByDate(string $automationId, string $nodeId, array $items, string $datePath): array - { - $state = AutomationNodeState::for($automationId, $nodeId); - $isFirstPoll = ! array_key_exists('last_item_date', (array) $state->data); - $watermark = $this->parseWatermark(data_get($state->data, 'last_item_date')); - $newest = null; - $new = []; - - foreach ($items as $item) { - $date = $this->parseDate(data_get($item, $datePath)); - if ($date === null) { - continue; - } - - if ($newest === null || $date->greaterThan($newest)) { - $newest = $date; - } - - // The first poll only records the baseline (the newest item's date), - // emitting nothing — so an existing feed never floods on day one, even - // if some items are dated slightly ahead of the server clock. - if (! $isFirstPoll && $date->greaterThan($watermark)) { - $new[] = $item; - } - } - - if ($newest !== null) { - $state->update(['data' => array_merge($state->data ?? [], [ - 'last_item_date' => $newest->toIso8601String(), - ])]); - } - - return $new; - } - - /** - * Keeps only items whose key hasn't been seen before, recording the new keys - * in a FIFO-capped per-node set. The first poll records every key but emits - * nothing (baseline), matching the date-watermark semantics so pointing the - * node at an existing feed never floods on day one. - * - * @param array $items - * @return array - */ - private function filterBySeenKeys(string $automationId, string $nodeId, array $items, string $keyPath): array - { - $state = AutomationNodeState::for($automationId, $nodeId); - $isFirstPoll = ! array_key_exists('seen_keys', (array) $state->data); - $hashes = array_values((array) data_get($state->data, 'seen_keys', [])); - $seen = array_flip($hashes); - $new = []; - - foreach ($items as $item) { - $hash = $this->keyHash($item, $keyPath); - if (isset($seen[$hash])) { - continue; - } - - $seen[$hash] = true; - $hashes[] = $hash; - - if (! $isFirstPoll) { - $new[] = $item; - } - } - - if (count($hashes) > self::MAX_SEEN_KEYS) { - $hashes = array_slice($hashes, count($hashes) - self::MAX_SEEN_KEYS); - } - - $state->update(['data' => array_merge((array) $state->data, ['seen_keys' => $hashes])]); - - return $new; - } - - /** - * Stable hash for an item's dedup key. Objects use `item_key_path` (falling - * back to the whole item); scalars use their own value. Hashing keeps the - * stored set compact and avoids persisting raw payloads. - */ - private function keyHash(mixed $item, string $keyPath): string - { - if (is_array($item)) { - $key = $keyPath !== '' ? data_get($item, $keyPath) : null; - // Fall back to the whole item when no usable key is present; serialize - // guards against json_encode returning false on malformed UTF-8. - $key = ($key === null || $key === '') ? (json_encode($item) ?: serialize($item)) : (string) $key; - } else { - $key = (string) $item; - } - - return md5($key); - } - - /** - * @param array $config - * @param array $context - */ - private function buildRequest(array $config, array $context): PendingRequest - { - $request = Http::asJson(); - - $headers = []; - foreach ((array) data_get($config, 'headers', []) as $k => $v) { - $headers[$k] = $this->resolver->resolve((string) $v, $context); - } - - $authType = AuthType::tryFrom((string) data_get($config, 'auth_type', AuthType::None->value)); - if ($authType === AuthType::Bearer) { - $token = $this->decrypt((string) data_get($config, 'auth_token', '')); - if ($token !== '') { - $request = $request->withToken($this->resolver->resolve($token, $context)); - } - } elseif ($authType === AuthType::Basic) { - $user = (string) data_get($config, 'auth_username', ''); - $pass = $this->decrypt((string) data_get($config, 'auth_password', '')); - if ($user !== '' || $pass !== '') { - $request = $request->withBasicAuth( - $this->resolver->resolve($user, $context), - $this->resolver->resolve($pass, $context), - ); - } - } elseif ($authType === AuthType::ApiKey) { - $headerName = (string) data_get($config, 'auth_header_name', 'X-API-Key'); - $token = $this->decrypt((string) data_get($config, 'auth_token', '')); - if ($token !== '') { - $headers[$headerName] = $this->resolver->resolve($token, $context); - } - } - - if ($headers !== []) { - $request = $request->withHeaders($headers); - } - - return $request - ->withUserAgent(config('trypost.user_agent')) - ->withOptions($this->safeHttp->redirectGuardOptions()); - } - - /** - * @param array $config - * @param array $context - * @return array - */ - private function buildJsonBody(string $method, array $config, array $context): array - { - if (! in_array(HttpMethod::tryFrom($method), HttpMethod::withBody(), true)) { - return []; - } - - $template = (string) data_get($config, 'body_template', ''); - if ($template === '') { - return []; - } - - // Parse the JSON body template first, then resolve placeholders in its - // string leaves so data containing quotes/newlines can't corrupt it. - $decodedTemplate = json_decode($template, true); - - if (! is_array($decodedTemplate)) { - return []; - } - - return $this->resolver->resolveStructured($decodedTemplate, $context); - } - - /** - * @param array $items - */ - private function spawnSiblings(AutomationRun $parent, string $fetchNodeId, array $items): void - { - if ($items === []) { - return; - } - - // Each remaining item gets its own run that fans out across EVERY branch - // wired to this node — matching how item[0] (the current run) fans out. - $targets = $this->advance->targetsFor($parent->automation, $fetchNodeId, self::ITEM_HANDLE); - - foreach ($items as $item) { - $sibling = AutomationRun::create([ - 'automation_id' => $parent->automation_id, - 'root_run_id' => $parent->rootId(), - 'trigger_item_id' => $parent->trigger_item_id, - 'generated_post_id' => $parent->generated_post_id, - 'is_manual' => $parent->is_manual, - 'is_dry_run' => $parent->is_dry_run, - 'status' => RunStatus::Pending, - 'context' => array_merge($parent->context ?? [], ['fetched' => $item]), - ]); - - if ($targets === []) { - $sibling->update(['status' => RunStatus::Completed, 'finished_at' => now()]); - - continue; - } - - $this->advance->dispatchBranches($sibling, $targets); - } - } - - private function decrypt(string $value): string - { - if ($value === '') { - return ''; - } - - try { - return Crypt::decryptString($value); - } catch (Throwable) { - // Value isn't an encrypted payload (legacy plain text or already decrypted). - return $value; - } - } - - private function parseWatermark(?string $stored): CarbonImmutable - { - if ($stored === null) { - return CarbonImmutable::now(); - } - - try { - return CarbonImmutable::parse($stored); - } catch (Throwable) { - return CarbonImmutable::now(); - } - } - - private function parseDate(mixed $raw): ?CarbonImmutable - { - if (! is_string($raw) && ! is_numeric($raw)) { - return null; - } - - try { - return CarbonImmutable::parse((string) $raw); - } catch (Throwable) { - return null; - } - } -} diff --git a/app/Actions/Automation/Node/RunPublishNode.php b/app/Actions/Automation/Node/RunPublishNode.php deleted file mode 100644 index 0a0fddc8..00000000 --- a/app/Actions/Automation/Node/RunPublishNode.php +++ /dev/null @@ -1,59 +0,0 @@ -is_dry_run) { - return NodeRunResult::completed(output: [ - 'publish' => ['mode' => $mode->value, 'post_id' => null, 'dry_run' => true], - ]); - } - - $post = $run->generatedPost; - - if ($post === null) { - return NodeRunResult::failed(__('automations.errors.no_generated_post')); - } - - match ($mode) { - Mode::Now => $this->publishNow($post), - Mode::Scheduled => $this->schedule($post, (int) data_get($config, 'scheduled_offset')), - Mode::Draft => null, - }; - - return NodeRunResult::completed(output: [ - 'publish' => ['mode' => $mode->value, 'post_id' => $post->id], - ]); - } - - private function publishNow(Post $post): void - { - $post->update(['status' => PostStatus::Publishing]); - PublishPost::dispatch($post); - } - - private function schedule(Post $post, int $offsetMinutes): void - { - $post->update([ - 'status' => PostStatus::Scheduled, - 'scheduled_at' => now()->addMinutes($offsetMinutes), - ]); - } -} diff --git a/app/Actions/Automation/Run/AdvanceAutomationRun.php b/app/Actions/Automation/Run/AdvanceAutomationRun.php deleted file mode 100644 index 1e76581f..00000000 --- a/app/Actions/Automation/Run/AdvanceAutomationRun.php +++ /dev/null @@ -1,79 +0,0 @@ -targetsFor($run->automation, $fromNodeId, $handle); - - if ($targets === []) { - $run->update([ - 'status' => Status::Completed, - 'finished_at' => now(), - 'current_node_id' => null, - 'error' => [ - 'reason' => 'no_matching_edge', - 'handle' => $handle, - 'node_id' => $fromNodeId, - ], - ]); - - return; - } - - $this->dispatchBranches($run, $targets); - } - - /** - * Every node id connected to `$fromNodeId` via the given handle. A node can - * fan out to several targets (e.g. a trigger calling RSS and HTTP at once). - * - * @return array - */ - public function targetsFor(Automation $automation, string $fromNodeId, string $handle = 'default'): array - { - return collect($automation->connections ?? []) - ->filter(fn ($c) => data_get($c, 'source') === $fromNodeId && data_get($c, 'source_handle', 'default') === $handle) - ->pluck('target') - ->filter() - ->values() - ->all(); - } - - /** - * Continues the run on the first branch and forks a sibling run — sharing the - * accumulated context — for every additional branch, so all targets execute. - * - * @param array $targets - */ - public function dispatchBranches(AutomationRun $run, array $targets): void - { - $first = array_shift($targets); - - foreach ($targets as $target) { - $sibling = AutomationRun::create([ - 'automation_id' => $run->automation_id, - 'root_run_id' => $run->rootId(), - 'trigger_item_id' => $run->trigger_item_id, - 'generated_post_id' => $run->generated_post_id, - 'is_manual' => $run->is_manual, - 'is_dry_run' => $run->is_dry_run, - 'status' => Status::Pending, - 'context' => $run->context, - ]); - - ProcessAutomationNode::dispatch($sibling, $target); - } - - ProcessAutomationNode::dispatch($run, $first); - } -} diff --git a/app/Actions/Automation/Run/DispatchAutomationRun.php b/app/Actions/Automation/Run/DispatchAutomationRun.php deleted file mode 100644 index d7516924..00000000 --- a/app/Actions/Automation/Run/DispatchAutomationRun.php +++ /dev/null @@ -1,55 +0,0 @@ -triggerTargets($automation); - - $run = AutomationRun::create([ - 'automation_id' => $automation->id, - 'trigger_item_id' => $triggerItem->id, - 'status' => Status::Pending, - 'context' => ['trigger' => $triggerItem->payload], - ]); - - if ($targets === []) { - $run->update([ - 'status' => Status::Failed, - 'error' => ['message' => __('automations.errors.no_trigger_connection')], - 'finished_at' => now(), - ]); - - return $run; - } - - $this->advance->dispatchBranches($run, $targets); - - return $run; - } - - /** - * @return array - */ - private function triggerTargets(Automation $automation): array - { - $triggerNode = collect($automation->nodes ?? [])->firstWhere('type', 'trigger'); - - if ($triggerNode === null) { - return []; - } - - return $this->advance->targetsFor($automation, $triggerNode['id']); - } -} diff --git a/app/Actions/Automation/Run/RetryRunFromNode.php b/app/Actions/Automation/Run/RetryRunFromNode.php deleted file mode 100644 index 82f7a4a4..00000000 --- a/app/Actions/Automation/Run/RetryRunFromNode.php +++ /dev/null @@ -1,28 +0,0 @@ -status !== Status::Failed) { - throw new DomainException(__('automations.errors.only_failed_can_retry')); - } - - $run->update([ - 'status' => Status::Pending, - 'error' => null, - 'finished_at' => null, - ]); - - ProcessAutomationNode::dispatch($run, $nodeId); - } -} diff --git a/app/Actions/Automation/Run/TestAutomation.php b/app/Actions/Automation/Run/TestAutomation.php deleted file mode 100644 index 070378d1..00000000 --- a/app/Actions/Automation/Run/TestAutomation.php +++ /dev/null @@ -1,121 +0,0 @@ -configValidator->firstMessage($automation->nodes ?? []); - - if ($issue !== null) { - throw new DomainException($issue); - } - - $triggerNode = collect($automation->nodes ?? [])->firstWhere('type', 'trigger'); - $context = ['trigger' => $this->synthesizePayload($automation, $triggerNode ?? [])]; - - $targets = $triggerNode !== null - ? $this->advance->targetsFor($automation, $triggerNode['id']) - : []; - - $run = AutomationRun::create([ - 'automation_id' => $automation->id, - 'status' => Status::Pending, - 'is_manual' => true, - 'is_dry_run' => ! $withRealData, - 'context' => $context, - ]); - - if ($targets === []) { - $run->update([ - 'status' => Status::Failed, - 'error' => ['message' => __('automations.errors.no_trigger_connection')], - 'finished_at' => now(), - ]); - - return $run; - } - - $this->advance->dispatchBranches($run, $targets); - - return $run; - } - - /** - * @param array $triggerNode - * @return array - */ - private function synthesizePayload(Automation $automation, array $triggerNode): array - { - $type = data_get($triggerNode, 'data.trigger_type'); - - return match ($type) { - TriggerType::PostPublished->value, TriggerType::PostScheduled->value => $this->synthesizePostPayload($automation, (string) $type), - default => ['event' => $type ?? TriggerType::Schedule->value, 'fired_at' => now()->toIso8601String(), 'manual' => true], - }; - } - - /** - * Picks the most recent post in the automation's workspace so the test run - * reflects something the user actually sees. Falls back to a placeholder - * payload when the workspace has no posts yet. - * - * @return array - */ - private function synthesizePostPayload(Automation $automation, string $event): array - { - $post = Post::query() - ->where('workspace_id', $automation->workspace_id) - ->latest() - ->first(); - - $base = [ - 'event' => $event, - 'fired_at' => now()->toIso8601String(), - 'manual' => true, - ]; - - if ($post === null) { - return array_merge($base, ['post' => null, 'fetch_error' => 'no posts in workspace']); - } - - return array_merge($base, [ - 'post' => [ - 'id' => $post->id, - 'content' => $post->content, - 'status' => $post->status->value, - 'scheduled_at' => $post->scheduled_at?->toIso8601String(), - 'published_at' => $post->published_at?->toIso8601String(), - ], - ]); - } -} diff --git a/app/Actions/Automation/Trigger/DispatchPostTriggerAutomations.php b/app/Actions/Automation/Trigger/DispatchPostTriggerAutomations.php deleted file mode 100644 index b36316a1..00000000 --- a/app/Actions/Automation/Trigger/DispatchPostTriggerAutomations.php +++ /dev/null @@ -1,86 +0,0 @@ -where('workspace_id', $post->workspace_id) - ->where('status', AutomationStatus::Active) - ->where('trigger_type', $triggerType->value) - ->get(); - - foreach ($automations as $automation) { - $triggerNode = collect($automation->nodes ?? [])->firstWhere('type', NodeType::Trigger->value); - - if ($triggerNode === null) { - continue; - } - - $this->dispatchRun($automation, $triggerNode, $post); - } - } - - private function dispatchRun(Automation $automation, array $triggerNode, Post $post): void - { - $context = [ - 'trigger' => [ - 'event' => $triggerNode['data']['trigger_type'], - 'fired_at' => now()->toIso8601String(), - 'post' => [ - 'id' => $post->id, - 'content' => $post->content, - 'status' => $post->status->value, - 'scheduled_at' => $post->scheduled_at?->toIso8601String(), - 'published_at' => $post->published_at?->toIso8601String(), - ], - ], - ]; - - $run = AutomationRun::create([ - 'automation_id' => $automation->id, - 'status' => RunStatus::Pending, - 'context' => $context, - ]); - - $targets = $this->advance->targetsFor($automation, $triggerNode['id']); - - if ($targets === []) { - $run->update([ - 'status' => RunStatus::Failed, - 'error' => ['message' => __('automations.errors.no_trigger_connection')], - 'finished_at' => now(), - ]); - - return; - } - - $this->advance->dispatchBranches($run, $targets); - } -} diff --git a/app/Actions/Automation/Trigger/FireScheduleTrigger.php b/app/Actions/Automation/Trigger/FireScheduleTrigger.php deleted file mode 100644 index 22afe8c1..00000000 --- a/app/Actions/Automation/Trigger/FireScheduleTrigger.php +++ /dev/null @@ -1,36 +0,0 @@ -nodes ?? [])->firstWhere('type', 'trigger'); - $cron = data_get($triggerNode, 'data.cron'); - $timezone = data_get($triggerNode, 'data.schedule_timezone', config('app.timezone')); - - if ($cron === null) { - return false; - } - - $expression = new CronExpression($cron); - - if (! $expression->isDue(now(), $timezone)) { - return false; - } - - $key = now()->format('Y-m-d\TH:i'); - $payload = ['fired_at' => now()->toIso8601String()]; - - return ($this->enroll)($automation, $key, $payload) !== null; - } -} diff --git a/app/Actions/Automation/TriggerItem/EnrollTriggerItem.php b/app/Actions/Automation/TriggerItem/EnrollTriggerItem.php deleted file mode 100644 index 0afd0033..00000000 --- a/app/Actions/Automation/TriggerItem/EnrollTriggerItem.php +++ /dev/null @@ -1,35 +0,0 @@ -id) - ->where('item_key', $itemKey) - ->first(); - - if ($existing !== null) { - return null; - } - - $item = AutomationTriggerItem::create([ - 'automation_id' => $automation->id, - 'item_key' => $itemKey, - 'payload' => $payload, - 'first_seen_at' => now(), - ]); - - return ($this->dispatchRun)($automation, $item); - } -} diff --git a/app/Actions/Post/CreatePost.php b/app/Actions/Post/CreatePost.php index a0f439e8..dbfbf8fa 100644 --- a/app/Actions/Post/CreatePost.php +++ b/app/Actions/Post/CreatePost.php @@ -26,8 +26,7 @@ class CreatePost * `label_ids[]` are attached after creation so the same set of UUIDs * works for REST, MCP, and web callers. * - * `created_via` records which entry point created the post (web, mcp, - * api, or automation). Analytical only — null when omitted. + * `created_via` records which entry point created the post (web, mcp, or api). Analytical only — null when omitted. * * @param array{ * content?: ?string, diff --git a/app/Broadcasting/AutomationChannel.php b/app/Broadcasting/AutomationChannel.php deleted file mode 100644 index 03afd739..00000000 --- a/app/Broadcasting/AutomationChannel.php +++ /dev/null @@ -1,16 +0,0 @@ -workspace->hasMember($user); - } -} diff --git a/app/Console/Commands/Automation/FireScheduleTriggers.php b/app/Console/Commands/Automation/FireScheduleTriggers.php deleted file mode 100644 index ce90cdde..00000000 --- a/app/Console/Commands/Automation/FireScheduleTriggers.php +++ /dev/null @@ -1,32 +0,0 @@ -where('status', Status::Active) - ->where('trigger_type', TriggerType::Schedule->value) - ->chunkById(50, function ($automations) use ($fire) { - foreach ($automations as $automation) { - $fire($automation); - } - }); - - return self::SUCCESS; - } -} diff --git a/app/Console/Commands/Automation/ProcessAutomationDelays.php b/app/Console/Commands/Automation/ProcessAutomationDelays.php deleted file mode 100644 index 3cae03f0..00000000 --- a/app/Console/Commands/Automation/ProcessAutomationDelays.php +++ /dev/null @@ -1,48 +0,0 @@ -where('status', Status::Waiting) - ->where('next_action_at', '<=', now()) - ->where(fn ($query) => $query - ->where('is_manual', true) - ->orWhereHas('automation', fn ($inner) => $inner->where('status', AutomationStatus::Active))) - ->chunkById(50, function ($runs) use ($advance) { - foreach ($runs as $run) { - $claimed = AutomationRun::query() - ->whereKey($run->id) - ->where('status', Status::Waiting) - ->update([ - 'status' => Status::Running, - 'next_action_at' => null, - ]); - - if ($claimed === 0) { - continue; - } - - $run->refresh(); - $advance($run, $run->current_node_id); - } - }); - - return self::SUCCESS; - } -} diff --git a/app/Console/Commands/Automation/PruneDryRunAutomationRuns.php b/app/Console/Commands/Automation/PruneDryRunAutomationRuns.php deleted file mode 100644 index a8ee71af..00000000 --- a/app/Console/Commands/Automation/PruneDryRunAutomationRuns.php +++ /dev/null @@ -1,30 +0,0 @@ -where('is_dry_run', true) - ->whereNotNull('finished_at') - ->where('finished_at', '<=', now()->subMinutes(self::GRACE_MINUTES)) - ->delete(); - - $this->info("Pruned {$count} dry-run automation runs."); - - return self::SUCCESS; - } -} diff --git a/app/Console/Commands/Automation/RecoverStuckAutomationRuns.php b/app/Console/Commands/Automation/RecoverStuckAutomationRuns.php deleted file mode 100644 index 53951c42..00000000 --- a/app/Console/Commands/Automation/RecoverStuckAutomationRuns.php +++ /dev/null @@ -1,32 +0,0 @@ -whereIn('status', [Status::Running, Status::Pending]) - ->where('updated_at', '<=', now()->subHour()) - ->update([ - 'status' => Status::Failed, - 'error' => ['reason' => 'stuck'], - 'finished_at' => now(), - ]); - - $this->info("Recovered {$count} stuck automation runs."); - - return self::SUCCESS; - } -} diff --git a/app/DataTransferObjects/Automation/NodeRunResult.php b/app/DataTransferObjects/Automation/NodeRunResult.php deleted file mode 100644 index 45250b42..00000000 --- a/app/DataTransferObjects/Automation/NodeRunResult.php +++ /dev/null @@ -1,34 +0,0 @@ - $message], $extra ?? [])); - } -} diff --git a/app/Enums/Automation/AuthType.php b/app/Enums/Automation/AuthType.php deleted file mode 100644 index b631dcda..00000000 --- a/app/Enums/Automation/AuthType.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ - public static function withBody(): array - { - return [self::Post, self::Put, self::Patch]; - } -} diff --git a/app/Enums/Automation/Node/Type.php b/app/Enums/Automation/Node/Type.php deleted file mode 100644 index 5d866c36..00000000 --- a/app/Enums/Automation/Node/Type.php +++ /dev/null @@ -1,17 +0,0 @@ -run->automation_id}"), - ]; - } - - /** - * @return array - */ - public function broadcastWith(): array - { - return [ - 'run_id' => $this->run->id, - 'root_run_id' => $this->run->rootId(), - 'automation_id' => $this->run->automation_id, - 'status' => $this->run->status->value, - ]; - } - - public function broadcastQueue(): string - { - return 'broadcasts'; - } -} diff --git a/app/Http/Controllers/App/AutomationController.php b/app/Http/Controllers/App/AutomationController.php deleted file mode 100644 index b4dce4e9..00000000 --- a/app/Http/Controllers/App/AutomationController.php +++ /dev/null @@ -1,278 +0,0 @@ -authorize('viewAny', Automation::class); - - $workspace = request()->user()->currentWorkspace; - - $automations = Inertia::scroll(fn () => AutomationResource::collection( - $list($workspace) - )); - - return Inertia::render('automations/Index', [ - 'automations' => $automations, - ]); - } - - public function store(StoreAutomationRequest $request, CreateAutomation $create): RedirectResponse - { - $this->authorize('create', Automation::class); - - $automation = $create( - $request->user()->currentWorkspace, - $request->user(), - ); - - return redirect()->route('app.automations.workflow', $automation->id); - } - - public function show(Automation $automation): RedirectResponse - { - $this->authorize('view', $automation); - - // A draft opens on the builder to be set up; a live automation (active - // or paused) opens on its metrics, where the user watches it run. - $tab = $automation->status === Status::Draft ? 'workflow' : 'metrics'; - - return redirect()->route("app.automations.{$tab}", $automation->id); - } - - public function workflow(Automation $automation, GetAutomationEditorData $editorData): Response - { - $this->authorize('update', $automation); - - ['socialAccounts' => $socialAccounts, 'pinterestBoards' => $pinterestBoards, 'tiktokCreatorInfos' => $tiktokCreatorInfos] = $editorData($automation); - - $platformConfigs = $socialAccounts->mapWithKeys(fn ($account) => [ - $account->id => new PlatformConfigResource($account), - ]); - - return Inertia::render('automations/Form', [ - 'automation' => AutomationResource::make($automation), - 'socialAccounts' => SocialAccountResource::collection($socialAccounts), - 'platformConfigs' => $platformConfigs, - 'pinterestBoards' => $pinterestBoards, - 'tiktokCreatorInfos' => $tiktokCreatorInfos, - ]); - } - - public function invocations(Automation $automation, GetAutomationInvocations $invocations): Response - { - $this->authorize('view', $automation); - - $status = request()->string('status')->toString() ?: null; - $search = request()->string('search')->toString() ?: null; - - return Inertia::render('automations/Invocations', [ - 'automation' => AutomationResource::make($automation), - 'invocations' => Inertia::scroll(fn () => AutomationInvocationResource::collection( - $invocations($automation, $status, $search) - )), - 'filters' => [ - 'status' => $status, - 'search' => $search, - ], - ]); - } - - public function settings(Automation $automation): Response - { - $this->authorize('view', $automation); - - return Inertia::render('automations/Settings', [ - 'automation' => AutomationResource::make($automation), - ]); - } - - public function metrics(Automation $automation, GetAutomationMetrics $metrics): Response - { - $this->authorize('view', $automation); - - $end = (request()->date('end') ?? now())->startOfDay(); - $start = (request()->date('start') ?? now()->subDays(6))->startOfDay(); - - if ($start->greaterThan($end)) { - [$start, $end] = [$end, $start]; - } - - // Cap the window so a hand-edited URL can't request a multi-year, daily - // bucketed series. - if ($start->diffInDays($end) > 366) { - $start = $end->copy()->subDays(366); - } - - return Inertia::render('automations/Metrics', [ - 'automation' => AutomationResource::make($automation), - 'metrics' => $metrics($automation, $start, $end), - 'filters' => [ - 'start' => $start->toDateString(), - 'end' => $end->toDateString(), - ], - ]); - } - - public function update(UpdateAutomationRequest $request, Automation $automation, UpdateAutomation $update): RedirectResponse - { - $this->authorize('update', $automation); - - $update($automation, $request->validated()); - - return back(); - } - - public function destroy(Automation $automation, DeleteAutomation $delete): RedirectResponse - { - $this->authorize('delete', $automation); - $delete($automation); - - session()->flash('flash.banner', __('automations.flash.deleted')); - session()->flash('flash.bannerStyle', 'success'); - - return redirect()->route('app.automations.index'); - } - - public function activate(ActivateAutomationRequest $request, Automation $automation, ActivateAutomation $activate): RedirectResponse - { - $this->authorize('activate', $automation); - - $activate($automation); - - return back(); - } - - public function pause(PauseAutomationRequest $request, Automation $automation, PauseAutomation $pause): RedirectResponse - { - $this->authorize('pause', $automation); - - $pause($automation); - - return back(); - } - - public function retryRun( - RetryRunRequest $request, - RetryRunFromNode $retry, - Automation $automation, - AutomationRun $run, - ): HttpResponse { - $this->authorize('update', $automation); - abort_unless($run->automation_id === $automation->id, 404); - - $nodeId = $request->validated('node_id') ?? $run->current_node_id; - $retry($run, $nodeId); - - return response()->noContent(); - } - - public function test(TestAutomationRequest $request, Automation $automation, TestAutomation $test): JsonResponse - { - $this->authorize('update', $automation); - - $run = $test($automation, (bool) $request->validated('with_real_data', false)); - - return response()->json(['run_id' => $run->id]); - } - - public function inspectFeed( - InspectFeedRequest $request, - Automation $automation, - ExpressionResolver $resolver, - SafeHttpFetcher $safeHttp, - FeedParser $parser, - ): JsonResponse|FeedInspectionResource { - $this->authorize('update', $automation); - - $feedUrl = $resolver->resolve( - $request->validated('feed_url'), - ['variables' => $automation->resolvedVariables()], - ); - - // SafeHttpFetcher::get() bundles the SSRF guard, a request timeout, a - // redirect cap and a branded user-agent — so a slow or hostile feed can't - // hang this synchronous request. It throws on SSRF, timeout or non-2xx. - try { - $response = $safeHttp->get($feedUrl); - } catch (RuntimeException) { - return response()->json(['message' => __('automations.errors.fetch_rss_request_failed')], SymfonyResponse::HTTP_UNPROCESSABLE_ENTITY); - } - - $items = $parser->parse($response->body()); - - if ($items === null) { - return response()->json(['message' => __('automations.errors.fetch_rss_malformed')], SymfonyResponse::HTTP_UNPROCESSABLE_ENTITY); - } - - return new FeedInspectionResource($items[0] ?? []); - } - - public function showRun(Automation $automation, AutomationRun $run): JsonResponse - { - $this->authorize('view', $automation); - abort_unless($run->automation_id === $automation->id, 404); - - // Aggregate the node runs of every branch forked by a fan-out so the test - // panel shows the whole execution, not just the branch the root walked. - $rootId = $run->rootId(); - - $nodeRuns = AutomationNodeRun::query() - ->whereHas('run', fn ($query) => $query - ->where('id', $rootId) - ->orWhere('root_run_id', $rootId)) - ->orderBy('started_at') - ->orderBy('id') - ->get(); - - return response()->json([ - 'run' => AutomationRunResource::make($run)->resolve(), - 'node_runs' => AutomationNodeRunResource::collection($nodeRuns)->resolve(), - ]); - } -} diff --git a/app/Http/Requests/App/Automations/ActivateAutomationRequest.php b/app/Http/Requests/App/Automations/ActivateAutomationRequest.php deleted file mode 100644 index a6f718da..00000000 --- a/app/Http/Requests/App/Automations/ActivateAutomationRequest.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - public function rules(): array - { - return []; - } -} diff --git a/app/Http/Requests/App/Automations/InspectFeedRequest.php b/app/Http/Requests/App/Automations/InspectFeedRequest.php deleted file mode 100644 index e3d878e5..00000000 --- a/app/Http/Requests/App/Automations/InspectFeedRequest.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function rules(): array - { - return [ - 'feed_url' => ['required', 'string', 'max:2048'], - ]; - } -} diff --git a/app/Http/Requests/App/Automations/PauseAutomationRequest.php b/app/Http/Requests/App/Automations/PauseAutomationRequest.php deleted file mode 100644 index e0535860..00000000 --- a/app/Http/Requests/App/Automations/PauseAutomationRequest.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - public function rules(): array - { - return []; - } -} diff --git a/app/Http/Requests/App/Automations/RetryRunRequest.php b/app/Http/Requests/App/Automations/RetryRunRequest.php deleted file mode 100644 index ad8d42af..00000000 --- a/app/Http/Requests/App/Automations/RetryRunRequest.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function rules(): array - { - return [ - 'node_id' => ['nullable', 'string', 'max:255'], - ]; - } -} diff --git a/app/Http/Requests/App/Automations/StoreAutomationRequest.php b/app/Http/Requests/App/Automations/StoreAutomationRequest.php deleted file mode 100644 index 5a80a595..00000000 --- a/app/Http/Requests/App/Automations/StoreAutomationRequest.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - public function rules(): array - { - return []; - } -} diff --git a/app/Http/Requests/App/Automations/TestAutomationRequest.php b/app/Http/Requests/App/Automations/TestAutomationRequest.php deleted file mode 100644 index 0d3139ef..00000000 --- a/app/Http/Requests/App/Automations/TestAutomationRequest.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function rules(): array - { - return [ - 'with_real_data' => ['nullable', 'boolean'], - ]; - } -} diff --git a/app/Http/Requests/App/Automations/UpdateAutomationRequest.php b/app/Http/Requests/App/Automations/UpdateAutomationRequest.php deleted file mode 100644 index 8ff80f85..00000000 --- a/app/Http/Requests/App/Automations/UpdateAutomationRequest.php +++ /dev/null @@ -1,180 +0,0 @@ - - */ - public function rules(): array - { - $rules = [ - 'name' => ['sometimes', 'string', 'max:120'], - 'nodes' => ['sometimes', 'array'], - 'nodes.*.id' => ['required', 'string'], - 'nodes.*.type' => ['required', 'string', Rule::in(array_column(NodeType::cases(), 'value'))], - 'nodes.*.position' => ['required', 'array'], - 'nodes.*.position.x' => ['required', 'numeric'], - 'nodes.*.position.y' => ['required', 'numeric'], - 'nodes.*.data' => ['required', 'array'], - 'connections' => ['sometimes', 'array'], - 'connections.*.id' => ['required', 'string'], - 'connections.*.source' => ['required', 'string'], - 'connections.*.target' => ['required', 'string'], - 'connections.*.source_handle' => ['nullable', 'string'], - 'connections.*.target_handle' => ['nullable', 'string'], - 'variables' => ['sometimes', 'array', 'max:50'], - 'variables.*.key' => ['required', 'string', 'max:60', 'regex:/^[A-Za-z_][A-Za-z0-9_]*$/', 'distinct'], - 'variables.*.value' => ['nullable', 'string'], - ]; - - // Per-node data validation. We build these dynamically so each node's - // type drives the shape of its `data` payload, and so errors come back - // with full paths like `nodes.2.data.feed_url` for the frontend to map. - $nodes = $this->input('nodes', []); - if (is_array($nodes)) { - foreach ($nodes as $i => $node) { - $type = data_get($node, 'type'); - foreach ($this->dataRulesForNodeType($type, (int) $i) as $field => $fieldRules) { - $rules["nodes.{$i}.data.{$field}"] = $fieldRules; - } - } - } - - return $rules; - } - - /** - * Block saving a node whose config can't run: a Generate node whose image - * count doesn't fit a selected account's content-type. Each issue is keyed - * to the field the frontend surfaces it under. - */ - public function withValidator(Validator $validator): void - { - $validator->after(function (Validator $validator): void { - $nodes = $this->input('nodes', []); - - if (! is_array($nodes)) { - return; - } - - foreach (app(AutomationConfigValidator::class)->issues($nodes) as $issue) { - $validator->errors()->add("nodes.{$issue['node_index']}.data.{$issue['field']}", $issue['message']); - } - }); - } - - /** - * @return array - */ - public function attributes(): array - { - return [ - 'nodes.*.data.feed_url' => 'Feed URL', - 'nodes.*.data.url' => 'URL', - 'nodes.*.data.cron' => 'cron expression', - 'nodes.*.data.duration' => 'duration', - 'nodes.*.data.unit' => 'unit', - 'nodes.*.data.field' => 'field', - 'nodes.*.data.operator' => 'operator', - 'nodes.*.data.mode' => 'mode', - 'nodes.*.data.method' => 'method', - 'nodes.*.data.trigger_type' => 'trigger type', - 'nodes.*.data.prompt_template' => 'prompt template', - 'nodes.*.data.accounts' => 'accounts', - ]; - } - - /** - * @return array> - */ - private function dataRulesForNodeType(?string $type, int $i): array - { - return match ($type) { - NodeType::Trigger->value => [ - 'trigger_type' => ['required', Rule::in(array_column(TriggerType::cases(), 'value'))], - 'cron' => ['required_if:nodes.'.$i.'.data.trigger_type,'.TriggerType::Schedule->value, 'string'], - 'schedule_field' => ['sometimes', Rule::in(array_column(ScheduleField::cases(), 'value'))], - 'schedule_minutes_interval' => ['sometimes', 'integer', 'min:1', 'max:59'], - 'schedule_hours_interval' => ['sometimes', 'integer', 'min:1', 'max:23'], - 'schedule_days_interval' => ['sometimes', 'integer', 'min:1', 'max:31'], - 'schedule_hour' => ['sometimes', 'integer', 'min:0', 'max:23'], - 'schedule_minute' => ['sometimes', 'integer', 'min:0', 'max:59'], - 'schedule_weekdays' => ['sometimes', 'array'], - 'schedule_weekdays.*' => ['integer', 'min:0', 'max:6'], - 'schedule_day_of_month' => ['sometimes', 'integer', 'min:1', 'max:31'], - 'schedule_timezone' => ['sometimes', 'string', 'timezone'], - ], - NodeType::FetchRss->value => [ - 'feed_url' => ['required', new ResolvableUrl], - 'discovered_fields' => ['sometimes', 'array'], - 'discovered_fields.*.path' => ['required', 'string'], - 'discovered_fields.*.sample' => ['nullable', 'string'], - ], - NodeType::HttpRequest->value => [ - 'url' => ['required', new ResolvableUrl], - 'method' => ['required', Rule::in(array_column(HttpMethod::cases(), 'value'))], - 'auth_type' => ['required', Rule::in(array_column(AuthType::cases(), 'value'))], - 'auth_token' => ['nullable', 'string'], - 'auth_username' => ['nullable', 'string'], - 'auth_password' => ['nullable', 'string'], - 'auth_header_name' => ['nullable', 'string'], - 'body_template' => ['nullable', 'string'], - 'headers' => ['nullable', 'array'], - 'headers.*' => ['string'], - 'items_path' => ['nullable', 'string'], - 'item_key_path' => ['nullable', 'string'], - 'item_date_path' => ['nullable', 'string'], - ], - NodeType::Generate->value => [ - 'accounts' => ['required', 'array', 'min:1'], - 'prompt_template' => ['required', 'string'], - 'target_slide_count' => ['nullable', 'integer', 'min:0', 'max:'.GenerateNodeValidator::MAX_GENERATED_IMAGES], - 'use_brand_voice' => ['sometimes', 'boolean'], - 'use_brand_visuals' => ['sometimes', 'boolean'], - 'style' => ['sometimes', Rule::in(array_column(ContentStyle::cases(), 'value'))], - ], - NodeType::Delay->value => [ - 'duration' => ['required', 'integer', 'min:1'], - 'unit' => ['required', Rule::in(array_column(DelayUnit::cases(), 'value'))], - ], - NodeType::Condition->value => [ - 'field' => ['required', 'string'], - 'operator' => ['required', Rule::in(array_column(ConditionOperator::cases(), 'value'))], - 'value' => ['nullable', 'string'], - ], - NodeType::Publish->value => [ - 'mode' => ['required', Rule::in(array_column(PublishMode::cases(), 'value'))], - 'scheduled_offset' => ['required_if:nodes.'.$i.'.data.mode,'.PublishMode::Scheduled->value, 'integer', 'min:0'], - ], - NodeType::End->value => [ - 'reason' => ['nullable', 'string'], - ], - default => [], - }; - } -} diff --git a/app/Http/Resources/App/Automation/FeedInspectionResource.php b/app/Http/Resources/App/Automation/FeedInspectionResource.php deleted file mode 100644 index 7aa2aed0..00000000 --- a/app/Http/Resources/App/Automation/FeedInspectionResource.php +++ /dev/null @@ -1,69 +0,0 @@ - $resource The first normalized feed item. - */ -class FeedInspectionResource extends JsonResource -{ - private const SAMPLE_MAX_LENGTH = 120; - - /** - * @return array{fields: list} - */ - public function toArray(Request $request): array - { - return [ - 'fields' => $this->flatten((array) $this->resource), - ]; - } - - /** - * @param array $item - * @return list - */ - private function flatten(array $item, string $prefix = 'fetched'): array - { - $fields = []; - - foreach ($item as $key => $value) { - $path = "{$prefix}.{$key}"; - - if (is_array($value) && $this->isAssoc($value)) { - $fields = array_merge($fields, $this->flatten($value, $path)); - - continue; - } - - $fields[] = ['path' => $path, 'sample' => $this->sample($value)]; - } - - return $fields; - } - - /** - * @param array $value - */ - private function isAssoc(array $value): bool - { - return $value !== [] && ! array_is_list($value); - } - - private function sample(mixed $value): string - { - $text = is_array($value) - ? (json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '') - : (string) $value; - - return str($text)->squish()->limit(self::SAMPLE_MAX_LENGTH)->value(); - } -} diff --git a/app/Http/Resources/AutomationInvocationResource.php b/app/Http/Resources/AutomationInvocationResource.php deleted file mode 100644 index 503ae62e..00000000 --- a/app/Http/Resources/AutomationInvocationResource.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'status' => $this->status->value, - 'is_manual' => (bool) $this->is_manual, - 'node_run_count' => (int) ($this->node_runs_count ?? 0), - 'duration_ms' => $this->durationInMilliseconds(), - 'error_message' => is_array($this->error) ? ($this->error['message'] ?? null) : $this->error, - 'created_at' => $this->created_at, - 'started_at' => $this->started_at, - 'finished_at' => $this->finished_at, - ]; - } -} diff --git a/app/Http/Resources/AutomationNodeRunResource.php b/app/Http/Resources/AutomationNodeRunResource.php deleted file mode 100644 index bdad385c..00000000 --- a/app/Http/Resources/AutomationNodeRunResource.php +++ /dev/null @@ -1,29 +0,0 @@ - - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'node_id' => $this->node_id, - 'node_type' => $this->node_type->value, - 'status' => $this->status->value, - 'input' => $this->input, - 'output' => $this->output, - 'error' => $this->error, - 'started_at' => $this->started_at, - 'finished_at' => $this->finished_at, - ]; - } -} diff --git a/app/Http/Resources/AutomationResource.php b/app/Http/Resources/AutomationResource.php deleted file mode 100644 index d696fd69..00000000 --- a/app/Http/Resources/AutomationResource.php +++ /dev/null @@ -1,74 +0,0 @@ - - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'workspace_id' => $this->workspace_id, - 'name' => $this->name, - 'status' => $this->status->value, - 'nodes' => $this->maskSensitiveNodeFields($this->nodes ?? []), - 'connections' => $this->connections ?? [], - 'variables' => $this->maskVariables($this->variables ?? []), - 'activated_at' => $this->activated_at, - 'paused_at' => $this->paused_at, - 'created_at' => $this->created_at, - 'updated_at' => $this->updated_at, - ]; - } - - /** - * Replace any stored credentials with a placeholder before they leave - * the server. The frontend treats the placeholder as "keep current" on - * save (see Automation::booted()), so editing other fields doesn't wipe - * the stored secret. - * - * @param array> $nodes - * @return array> - */ - private function maskSensitiveNodeFields(array $nodes): array - { - foreach ($nodes as &$node) { - foreach (Automation::SENSITIVE_NODE_FIELDS as $field) { - if (data_get($node, "data.{$field}") !== null && data_get($node, "data.{$field}") !== '') { - data_set($node, "data.{$field}", Automation::SENSITIVE_PLACEHOLDER); - } - } - } - - return $nodes; - } - - /** - * Replace stored variable values with the placeholder so secrets never - * leave the server. The frontend references variables by key - * (`{{ variables.KEY }}`), so the masked value doesn't hinder reuse, and - * re-saving the placeholder keeps the stored ciphertext. - * - * @param array> $variables - * @return array> - */ - private function maskVariables(array $variables): array - { - foreach ($variables as &$variable) { - if (data_get($variable, 'value') !== null && data_get($variable, 'value') !== '') { - $variable['value'] = Automation::SENSITIVE_PLACEHOLDER; - } - } - - return $variables; - } -} diff --git a/app/Http/Resources/AutomationRunResource.php b/app/Http/Resources/AutomationRunResource.php deleted file mode 100644 index 08f434f4..00000000 --- a/app/Http/Resources/AutomationRunResource.php +++ /dev/null @@ -1,33 +0,0 @@ - - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'automation_id' => $this->automation_id, - 'trigger_item_id' => $this->trigger_item_id, - 'current_node_id' => $this->current_node_id, - 'status' => $this->status->value, - 'is_manual' => (bool) $this->is_manual, - 'is_dry_run' => (bool) $this->is_dry_run, - 'next_action_at' => $this->next_action_at, - 'generated_post_id' => $this->generated_post_id, - 'context' => $this->context, - 'error' => $this->error, - 'started_at' => $this->started_at, - 'finished_at' => $this->finished_at, - ]; - } -} diff --git a/app/Http/Resources/AutomationTriggerItemResource.php b/app/Http/Resources/AutomationTriggerItemResource.php deleted file mode 100644 index 3003a175..00000000 --- a/app/Http/Resources/AutomationTriggerItemResource.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function toArray(Request $request): array - { - return [ - 'id' => $this->id, - 'item_key' => $this->item_key, - 'payload' => $this->payload, - 'first_seen_at' => $this->first_seen_at, - 'run' => AutomationRunResource::make($this->whenLoaded('run')), - ]; - } -} diff --git a/app/Jobs/Automation/DispatchPostTriggerAutomationsJob.php b/app/Jobs/Automation/DispatchPostTriggerAutomationsJob.php deleted file mode 100644 index 7982630d..00000000 --- a/app/Jobs/Automation/DispatchPostTriggerAutomationsJob.php +++ /dev/null @@ -1,33 +0,0 @@ -onQueue('automations'); - } - - public function handle(DispatchPostTriggerAutomations $dispatch): void - { - $dispatch($this->post, $this->triggerType); - } -} diff --git a/app/Jobs/Automation/ProcessAutomationNode.php b/app/Jobs/Automation/ProcessAutomationNode.php deleted file mode 100644 index 85b48f5f..00000000 --- a/app/Jobs/Automation/ProcessAutomationNode.php +++ /dev/null @@ -1,163 +0,0 @@ -onQueue('automations'); - } - - public function handle(AdvanceAutomationRun $advance): void - { - $this->run->refresh(); - - if (! in_array($this->run->status, [RunStatus::Pending, RunStatus::Running, RunStatus::Waiting], true)) { - return; - } - - if (! $this->run->is_manual && $this->run->automation->status !== AutomationStatus::Active) { - return; - } - - $node = collect($this->run->automation->nodes ?? [])->firstWhere('id', $this->nodeId); - - if ($node === null) { - $this->run->update([ - 'status' => RunStatus::Failed, - 'error' => ['message' => __('automations.errors.node_no_longer_exists', ['node_id' => $this->nodeId])], - 'finished_at' => now(), - ]); - - return; - } - - $nodeType = NodeType::tryFrom((string) data_get($node, 'type', '')); - - if ($nodeType === null) { - $this->run->update([ - 'status' => RunStatus::Failed, - 'error' => ['message' => __('automations.errors.node_no_longer_exists', ['node_id' => $this->nodeId])], - 'finished_at' => now(), - ]); - - return; - } - - $this->run->update([ - 'status' => RunStatus::Running, - 'current_node_id' => $this->nodeId, - 'started_at' => $this->run->started_at ?? now(), - ]); - - $nodeRun = AutomationNodeRun::create([ - 'run_id' => $this->run->id, - 'node_id' => $this->nodeId, - 'node_type' => $nodeType, - 'status' => NodeRunStatus::Running, - 'input' => $this->run->context, - 'started_at' => now(), - ]); - - try { - $result = $this->executeNode($nodeType, $node['data'] ?? []); - } catch (Throwable $e) { - $result = NodeRunResult::failed($e->getMessage(), ['class' => $e::class]); - } - - $nodeRun->update([ - 'status' => $result->status, - 'output' => $result->output, - 'error' => $result->error, - 'finished_at' => now(), - ]); - - if ($result->status === NodeRunStatus::Failed) { - $this->run->update([ - 'status' => RunStatus::Failed, - 'error' => array_merge(['node_id' => $this->nodeId], $result->error ?? []), - 'finished_at' => now(), - ]); - - return; - } - - $this->run->update([ - 'context' => array_merge($this->run->context ?? [], $result->output), - ]); - - if ($result->sleepUntil !== null) { - $this->run->update([ - 'status' => RunStatus::Waiting, - 'next_action_at' => $result->sleepUntil, - ]); - - return; - } - - $advance($this->run, $this->nodeId, $result->nextHandle); - } - - public function failed(?Throwable $e): void - { - $this->run->refresh(); - - if (in_array($this->run->status, [RunStatus::Completed, RunStatus::Failed], true)) { - return; - } - - $this->run->update([ - 'status' => RunStatus::Failed, - 'error' => ['message' => $e?->getMessage() ?? 'job failed', 'node_id' => $this->nodeId], - 'finished_at' => now(), - ]); - } - - private function executeNode(NodeType $type, array $config): NodeRunResult - { - $handler = match ($type) { - NodeType::Generate => app(RunGenerateNode::class), - NodeType::Delay => app(RunDelayNode::class), - NodeType::Condition => app(RunConditionNode::class), - NodeType::Publish => app(RunPublishNode::class), - NodeType::End => app(RunEndNode::class), - NodeType::FetchRss => app(RunFetchRssNode::class), - NodeType::HttpRequest => app(RunHttpRequestNode::class), - NodeType::Trigger => throw new LogicException('Trigger nodes are not executed as run steps.'), - }; - - return $handler($this->run, $config); - } -} diff --git a/app/Models/Automation.php b/app/Models/Automation.php deleted file mode 100644 index 8189913b..00000000 --- a/app/Models/Automation.php +++ /dev/null @@ -1,214 +0,0 @@ - Status::class, - 'nodes' => 'array', - 'connections' => 'array', - 'variables' => 'array', - 'activated_at' => 'datetime', - 'paused_at' => 'datetime', - ]; - - protected static function booted(): void - { - static::saving(function (self $automation): void { - $automation->nodes = self::encryptSensitiveFields( - $automation->nodes ?? [], - $automation->getOriginal('nodes') ?? [], - ); - $automation->variables = self::encryptVariables( - $automation->variables ?? [], - $automation->getOriginal('variables') ?? [], - ); - $automation->trigger_type = self::deriveTriggerType($automation->nodes ?? []); - }); - } - - /** - * Workflow variables decrypted into a `key => value` map for use during a - * run (e.g. `{{ variables.API_KEY }}` resolution). Encrypted at rest and - * never returned to the frontend in plain text. - * - * @return array - */ - public function resolvedVariables(): array - { - $resolved = []; - - foreach ($this->variables ?? [] as $variable) { - $key = data_get($variable, 'key'); - if (! is_string($key) || $key === '') { - continue; - } - $resolved[$key] = self::decryptValue((string) data_get($variable, 'value', '')); - } - - return $resolved; - } - - public function workspace(): BelongsTo - { - return $this->belongsTo(Workspace::class); - } - - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } - - public function triggerItems(): HasMany - { - return $this->hasMany(AutomationTriggerItem::class); - } - - public function runs(): HasMany - { - return $this->hasMany(AutomationRun::class); - } - - /** - * Walks both the incoming and stored node lists and reconciles sensitive - * fields: a PLACEHOLDER value means "user didn't change it" (frontend - * never received the real value) so we keep the existing ciphertext. Plain - * text values get encrypted; already-encrypted strings pass through. - * - * Denormalize the trigger node's type into an indexed column so the - * scheduler can filter by it in SQL instead of decoding every automation's - * `nodes` JSON each minute. Recomputed on every save so it cannot drift. - * - * @param array> $nodes - */ - private static function deriveTriggerType(array $nodes): ?string - { - $triggerNode = collect($nodes)->firstWhere('type', NodeType::Trigger->value); - - return data_get($triggerNode, 'data.trigger_type'); - } - - /** - * @param array> $incoming - * @param array>|string $original - * @return array> - */ - private static function encryptSensitiveFields(array $incoming, array|string $original): array - { - $original = is_array($original) ? $original : (json_decode($original, true) ?: []); - $originalById = collect($original)->keyBy('id'); - - foreach ($incoming as &$node) { - $originalNode = $originalById->get($node['id'] ?? null); - foreach (self::SENSITIVE_NODE_FIELDS as $field) { - $value = data_get($node, "data.{$field}"); - if (! is_string($value) || $value === '') { - continue; - } - if ($value === self::SENSITIVE_PLACEHOLDER) { - data_set($node, "data.{$field}", data_get($originalNode, "data.{$field}", '')); - - continue; - } - if (self::looksEncrypted($value)) { - continue; - } - data_set($node, "data.{$field}", Crypt::encryptString($value)); - } - } - - return $incoming; - } - - /** - * Reconciles workflow variable values exactly like node credentials, matched - * by variable `key`: a PLACEHOLDER value keeps the existing ciphertext, - * plaintext gets encrypted, already-encrypted strings pass through. - * - * @param array> $incoming - * @param array>|string $original - * @return array> - */ - private static function encryptVariables(array $incoming, array|string $original): array - { - $original = is_array($original) ? $original : (json_decode($original, true) ?: []); - $originalByKey = collect($original)->keyBy('key'); - - foreach ($incoming as &$variable) { - $value = data_get($variable, 'value'); - if (! is_string($value) || $value === '') { - continue; - } - if ($value === self::SENSITIVE_PLACEHOLDER) { - $variable['value'] = (string) data_get($originalByKey->get($variable['key'] ?? null), 'value', ''); - - continue; - } - if (self::looksEncrypted($value)) { - continue; - } - $variable['value'] = Crypt::encryptString($value); - } - - return $incoming; - } - - private static function decryptValue(string $value): string - { - if ($value === '') { - return ''; - } - - try { - return Crypt::decryptString($value); - } catch (Throwable) { - return $value; - } - } - - /** - * Quick check for Laravel's `Crypt::encryptString` output without paying - * the cost of a full decrypt attempt. Laravel wraps payloads as base64 - * JSON beginning with the canonical `eyJpdiI` ("{"iv":"...) prefix. - */ - private static function looksEncrypted(string $value): bool - { - if (! str_starts_with($value, 'eyJ')) { - return false; - } - try { - Crypt::decryptString($value); - - return true; - } catch (Throwable) { - return false; - } - } -} diff --git a/app/Models/AutomationNodeRun.php b/app/Models/AutomationNodeRun.php deleted file mode 100644 index efdda37a..00000000 --- a/app/Models/AutomationNodeRun.php +++ /dev/null @@ -1,38 +0,0 @@ - Status::class, - 'node_type' => NodeType::class, - 'input' => 'array', - 'output' => 'array', - 'error' => 'array', - 'started_at' => 'datetime', - 'finished_at' => 'datetime', - ]; - - public function run(): BelongsTo - { - return $this->belongsTo(AutomationRun::class, 'run_id'); - } -} diff --git a/app/Models/AutomationNodeState.php b/app/Models/AutomationNodeState.php deleted file mode 100644 index 619a026e..00000000 --- a/app/Models/AutomationNodeState.php +++ /dev/null @@ -1,40 +0,0 @@ - 'array', - ]; - - public function automation(): BelongsTo - { - return $this->belongsTo(Automation::class); - } - - /** - * Idempotent lookup for the state row of a given node within an automation, - * creating an empty row on first access. Use this in poll/fire actions that - * need to read or update an internal watermark. - */ - public static function for(string $automationId, string $nodeId): self - { - return self::firstOrCreate( - ['automation_id' => $automationId, 'node_id' => $nodeId], - ['data' => []], - ); - } -} diff --git a/app/Models/AutomationRun.php b/app/Models/AutomationRun.php deleted file mode 100644 index c8777254..00000000 --- a/app/Models/AutomationRun.php +++ /dev/null @@ -1,106 +0,0 @@ - Status::class, - 'context' => 'array', - 'error' => 'array', - 'is_manual' => 'boolean', - 'is_dry_run' => 'boolean', - 'next_action_at' => 'datetime', - 'started_at' => 'datetime', - 'finished_at' => 'datetime', - ]; - - public function automation(): BelongsTo - { - return $this->belongsTo(Automation::class); - } - - /** - * Id of the run that started this execution. Fan-out forks sibling runs that - * all point back at the same root, so callers can treat every branch of one - * test/trigger as a single family. The root run points at itself. - */ - public function rootId(): string - { - return $this->root_run_id ?? $this->id; - } - - /** - * Wall-clock execution time, or null while the run hasn't both started and - * finished. Single source of truth for the Invocations list and metrics. - */ - public function durationInMilliseconds(): ?int - { - if ($this->started_at === null || $this->finished_at === null) { - return null; - } - - return (int) $this->started_at->diffInMilliseconds($this->finished_at); - } - - /** - * Context for template (`{{ ... }}`) resolution: the run context plus the - * automation's workflow variables, merged in-memory. Variables are NEVER - * persisted into the run context (they're encrypted at rest and would - * otherwise leak in plaintext via the run/node-run API), so we compute this - * on demand at resolve time only. - * - * @return array - */ - public function resolverContext(): array - { - return array_merge( - $this->context ?? [], - ['variables' => $this->automation->resolvedVariables()], - ); - } - - public function triggerItem(): BelongsTo - { - return $this->belongsTo(AutomationTriggerItem::class, 'trigger_item_id'); - } - - public function generatedPost(): BelongsTo - { - return $this->belongsTo(Post::class, 'generated_post_id'); - } - - public function nodeRuns(): HasMany - { - return $this->hasMany(AutomationNodeRun::class, 'run_id'); - } - - /** - * Real, production executions only — excludes manual test runs (both dry - * runs and "with real data" tests are flagged is_manual). The Invocations - * and Metrics tabs report on these, not on runs the user triggered to test - * the editor. - */ - public function scopeProductionRuns(Builder $query): Builder - { - return $query->where('is_manual', false)->where('is_dry_run', false); - } -} diff --git a/app/Models/AutomationTriggerItem.php b/app/Models/AutomationTriggerItem.php deleted file mode 100644 index 0c9abfa5..00000000 --- a/app/Models/AutomationTriggerItem.php +++ /dev/null @@ -1,34 +0,0 @@ - 'array', - 'first_seen_at' => 'datetime', - ]; - - public function automation(): BelongsTo - { - return $this->belongsTo(Automation::class); - } - - public function run(): HasOne - { - return $this->hasOne(AutomationRun::class, 'trigger_item_id'); - } -} diff --git a/app/Observers/AutomationNodeRunObserver.php b/app/Observers/AutomationNodeRunObserver.php deleted file mode 100644 index 4c045ded..00000000 --- a/app/Observers/AutomationNodeRunObserver.php +++ /dev/null @@ -1,23 +0,0 @@ -run); - } - - public function updated(AutomationNodeRun $nodeRun): void - { - if ($nodeRun->wasChanged(['status', 'output', 'error', 'finished_at'])) { - AutomationRunUpdated::dispatch($nodeRun->run); - } - } -} diff --git a/app/Observers/AutomationRunObserver.php b/app/Observers/AutomationRunObserver.php deleted file mode 100644 index 4872dbb0..00000000 --- a/app/Observers/AutomationRunObserver.php +++ /dev/null @@ -1,25 +0,0 @@ -wasChanged(['status', 'current_node_id', 'finished_at', 'next_action_at', 'error'])) { - AutomationRunUpdated::dispatch($run); - } - } -} diff --git a/app/Observers/PostObserver.php b/app/Observers/PostObserver.php index 3e067251..bfe0b34f 100644 --- a/app/Observers/PostObserver.php +++ b/app/Observers/PostObserver.php @@ -4,12 +4,10 @@ namespace App\Observers; -use App\Enums\Automation\Trigger\Type as TriggerType; use App\Enums\Post\Status as PostStatus; use App\Events\OnboardingStatusUpdated; use App\Events\PostCreated; use App\Events\PostStatusChanged; -use App\Jobs\Automation\DispatchPostTriggerAutomationsJob; use App\Models\Account; use App\Models\Post; use Illuminate\Database\Eloquent\Builder; @@ -35,16 +33,6 @@ public function saved(Post $post): void return; } - $triggerType = match ($post->status) { - PostStatus::Published => TriggerType::PostPublished, - PostStatus::Scheduled => TriggerType::PostScheduled, - default => null, - }; - - if ($triggerType !== null) { - DispatchPostTriggerAutomationsJob::dispatch($post, $triggerType)->afterCommit(); - } - $previousStatus = $this->previousStatus($post); DB::afterCommit(fn () => PostStatusChanged::dispatch($post, $previousStatus)); diff --git a/app/Policies/AutomationPolicy.php b/app/Policies/AutomationPolicy.php deleted file mode 100644 index 5c1e5bdc..00000000 --- a/app/Policies/AutomationPolicy.php +++ /dev/null @@ -1,49 +0,0 @@ -currentWorkspace !== null; - } - - public function view(User $user, Automation $automation): bool - { - return $automation->workspace_id === $user->current_workspace_id; - } - - public function create(User $user): bool - { - return $user->currentWorkspace !== null - && $user->can('createPost', $user->currentWorkspace); - } - - public function update(User $user, Automation $automation): bool - { - return $automation->workspace_id === $user->current_workspace_id - && $user->can('createPost', $user->currentWorkspace); - } - - public function delete(User $user, Automation $automation): bool - { - return $automation->workspace_id === $user->current_workspace_id - && $user->can('createPost', $user->currentWorkspace); - } - - public function activate(User $user, Automation $automation): bool - { - return $this->update($user, $automation); - } - - public function pause(User $user, Automation $automation): bool - { - return $this->update($user, $automation); - } -} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d08d1999..9bcef4af 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -8,11 +8,6 @@ use App\Models\AccessToken; use App\Models\Account; use App\Models\AiUsageLog; -use App\Models\Automation; -use App\Models\AutomationNodeRun; -use App\Models\AutomationNodeState; -use App\Models\AutomationRun; -use App\Models\AutomationTriggerItem; use App\Models\Invite; use App\Models\Media; use App\Models\Notification; @@ -100,11 +95,6 @@ protected function configureMorphMap(): void 'accessToken' => AccessToken::class, 'account' => Account::class, 'aiUsageLog' => AiUsageLog::class, - 'automation' => Automation::class, - 'automationNodeRun' => AutomationNodeRun::class, - 'automationNodeState' => AutomationNodeState::class, - 'automationRun' => AutomationRun::class, - 'automationTriggerItem' => AutomationTriggerItem::class, 'invite' => Invite::class, 'media' => Media::class, 'notification' => Notification::class, diff --git a/app/Rules/ResolvableUrl.php b/app/Rules/ResolvableUrl.php deleted file mode 100644 index 45f899bf..00000000 --- a/app/Rules/ResolvableUrl.php +++ /dev/null @@ -1,40 +0,0 @@ -translate(); - - return; - } - - $candidate = preg_replace('/\{\{\s*[\w.]+\s*\}\}/', 'placeholder', $value); - $scheme = strtolower((string) parse_url($candidate, PHP_URL_SCHEME)); - - // Require a real http(s) URL once expressions are substituted, so the rule - // is no weaker than the plain `url` rule it replaces (rejects file://, - // javascript://, etc.) while still allowing templated hosts/queries. - if (filter_var($candidate, FILTER_VALIDATE_URL) === false || ! in_array($scheme, ['http', 'https'], true)) { - $fail('validation.url')->translate(); - } - } -} diff --git a/app/Services/Automation/AutomationConfigValidator.php b/app/Services/Automation/AutomationConfigValidator.php deleted file mode 100644 index 290beafb..00000000 --- a/app/Services/Automation/AutomationConfigValidator.php +++ /dev/null @@ -1,57 +0,0 @@ -> $nodes - * @return list - */ - public function issues(array $nodes): array - { - $issues = []; - - foreach ($nodes as $index => $node) { - $config = (array) data_get($node, 'data', []); - - [$field, $message] = match (data_get($node, 'type')) { - NodeType::Generate->value => ['accounts', $this->generateValidator->issueFor($config)], - default => [null, null], - }; - - if ($message !== null) { - $issues[] = ['node_index' => $index, 'field' => $field, 'message' => $message]; - } - } - - return $issues; - } - - /** - * The first config issue's message, or null when every node is runnable. - * - * @param array> $nodes - */ - public function firstMessage(array $nodes): ?string - { - return $this->issues($nodes)[0]['message'] ?? null; - } -} diff --git a/app/Services/Automation/ExpressionResolver.php b/app/Services/Automation/ExpressionResolver.php deleted file mode 100644 index f86b1557..00000000 --- a/app/Services/Automation/ExpressionResolver.php +++ /dev/null @@ -1,65 +0,0 @@ - $this->resolveVariable($matches[1], $context), - $template, - ) ?? $template; - } - - /** - * Resolves `{{ ... }}` placeholders inside an already-decoded JSON structure - * (arrays/strings), resolving only string leaves. The caller json_encodes the - * result, so values are never string-interpolated into raw JSON — quotes, - * `&`, newlines etc. in the data can't corrupt the payload. - * - * @param array $context - */ - public function resolveStructured(mixed $value, array $context): mixed - { - if (is_string($value)) { - return $this->resolve($value, $context); - } - - if (is_array($value)) { - return array_map(fn ($item) => $this->resolveStructured($item, $context), $value); - } - - return $value; - } - - private function resolveVariable(string $path, array $context): string - { - if ($path === 'now') { - return Carbon::now()->toIso8601String(); - } - - if ($path === 'today') { - return Carbon::today()->toDateString(); - } - - $value = data_get($context, $path); - - if ($value === null) { - return ''; - } - - if (is_scalar($value)) { - return (string) $value; - } - - // json_encode returns false on malformed UTF-8 (plausible for scraped - // feed/HTTP payloads); the method must still return a string. - return json_encode($value, JSON_PARTIAL_OUTPUT_ON_ERROR) ?: ''; - } -} diff --git a/app/Services/Automation/FeedParser.php b/app/Services/Automation/FeedParser.php deleted file mode 100644 index 340c13ee..00000000 --- a/app/Services/Automation/FeedParser.php +++ /dev/null @@ -1,185 +0,0 @@ - }}`. - * - * Aliases win on key collisions. The HTTP fetch and SSRF guard stay in the caller; - * the body is handed to SimplePie via `set_raw_data()` so the SSRF guard isn't bypassed. - */ -class FeedParser -{ - /** - * Namespace URI → short prefix for the raw layer. - */ - private const NS_PREFIX = [ - 'http://search.yahoo.com/mrss/' => 'media', - 'http://www.youtube.com/xml/schemas/2015' => 'yt', - 'http://purl.org/dc/elements/1.1/' => 'dc', - 'http://purl.org/dc/terms/' => 'dc', - 'http://purl.org/rss/1.0/modules/content/' => 'content', - 'http://www.itunes.com/dtds/podcast-1.0.dtd' => 'itunes', - 'https://podcastindex.org/namespace/1.0' => 'podcast', - 'http://purl.org/rss/1.0/modules/slash/' => 'slash', - 'http://wellformedweb.org/CommentAPI/' => 'wfw', - 'http://www.georss.org/georss' => 'georss', - ]; - - /** - * Namespaces treated as the feed "core" — their tags get no prefix, so RSS and - * Atom land on the same raw key names (and are then overridden by aliases). - */ - private const NS_CORE = [ - '', - 'http://www.w3.org/2005/Atom', - 'http://purl.org/rss/1.0/', - 'http://backend.userland.com/rss2', - 'http://my.netscape.com/rdf/simple/0.9/', - ]; - - /** - * @return list>|null Null when the body is not a usable feed - * (invalid XML, or any parse failure). - */ - public function parse(string $body): ?array - { - try { - $feed = new SimplePie; - $feed->enable_cache(false); - $feed->set_raw_data($body); - - if (! @$feed->init()) { - return null; - } - - return array_map(fn (Item $item): array => $this->normalize($item), $feed->get_items()); - } catch (Throwable) { - return null; - } - } - - /** - * @return array - */ - private function normalize(Item $item): array - { - $enclosure = $item->get_enclosure(); - - $aliases = [ - 'key' => $item->get_id(false, false) ?: $item->get_permalink(), - 'title' => $item->get_title(), - 'link' => $item->get_permalink(), - 'date' => $item->get_date('c'), - 'pubDate' => $item->get_date('c'), - 'content' => $item->get_content(), - 'description' => $item->get_description(), - 'author' => $item->get_author()?->get_name(), - 'id' => $item->get_id(false, false), - 'image' => $enclosure?->get_thumbnail() ?: null, - 'categories' => array_values(array_filter(array_map( - fn ($category) => $category->get_label(), - $item->get_categories() ?? [], - ))), - 'enclosure' => $enclosure === null ? null : array_filter([ - 'url' => $enclosure->get_link(), - 'type' => $enclosure->get_type(), - 'length' => $enclosure->get_length(), - ], fn ($value) => $value !== null), - ]; - - // Aliases win on collision, so merge them over the raw layer. - return array_merge($this->rawFields($item), $aliases); - } - - /** - * @return array - */ - private function rawFields(Item $item): array - { - return $this->flattenChildren((array) data_get($item->data, 'child', [])); - } - - /** - * @param array $children Namespace-URI keyed tag map. - * @return array - */ - private function flattenChildren(array $children): array - { - $out = []; - - foreach ($children as $namespace => $tags) { - $prefix = $this->prefixFor((string) $namespace); - - foreach ($tags as $tag => $nodes) { - $key = $prefix === '' ? (string) $tag : "{$prefix}_{$tag}"; - $values = array_map(fn ($node) => $this->flattenNode((array) $node), $nodes); - $out[$key] = count($values) === 1 ? $values[0] : $values; - } - } - - return $out; - } - - /** - * @param array $node - */ - private function flattenNode(array $node): mixed - { - $attribs = $this->attributes($node); - $children = (array) data_get($node, 'child', []); - - if ($children !== []) { - return array_merge($this->flattenChildren($children), $attribs); - } - - $text = trim((string) data_get($node, 'data', '')); - - return match (true) { - $text !== '' && $attribs === [] => $text, - $text === '' && $attribs !== [] => $attribs, - $text !== '' && $attribs !== [] => array_merge(['_text' => $text], $attribs), - default => $text, - }; - } - - /** - * @param array $node - * @return array - */ - private function attributes(array $node): array - { - $out = []; - - foreach ((array) data_get($node, 'attribs', []) as $attrs) { - foreach ((array) $attrs as $name => $value) { - $out[(string) $name] = (string) $value; - } - } - - return $out; - } - - private function prefixFor(string $namespace): string - { - if (in_array($namespace, self::NS_CORE, true)) { - return ''; - } - - return self::NS_PREFIX[$namespace] ?? ''; - } -} diff --git a/app/Services/Automation/GenerateNodeValidator.php b/app/Services/Automation/GenerateNodeValidator.php deleted file mode 100644 index 0ca56ed3..00000000 --- a/app/Services/Automation/GenerateNodeValidator.php +++ /dev/null @@ -1,81 +0,0 @@ - $config - */ - public function issueFor(array $config): ?string - { - $accounts = data_get($config, 'accounts'); - - if (! is_array($accounts) || $accounts === []) { - return null; - } - - // Single source of truth: 0 = text-only, 1 = single image, 2+ = carousel. - $imageCount = (int) data_get($config, 'target_slide_count', 1); - - foreach ($accounts as $entry) { - $contentType = ContentType::tryFrom((string) data_get($entry, 'content_type')); - - if (! $contentType instanceof ContentType) { - continue; - } - - $issue = $this->issueForAccount($contentType, $imageCount); - - if ($issue !== null) { - return $issue; - } - } - - return null; - } - - private function issueForAccount(ContentType $contentType, int $imageCount): ?string - { - // Generate only produces images — video-only formats (Reel, Video Pin, …) - // can never be satisfied by this node. - if (! $contentType->supportsImage()) { - return __('automations.errors.generate_image_format_required'); - } - - $min = $contentType->minMediaCount(); - - if ($min > 0 && $imageCount < $min) { - return __('posts.edit.compliance.too_few_files', ['min' => (string) $min]); - } - - if ($contentType->requiresMedia() && $imageCount === 0) { - return __('posts.edit.compliance.requires_media'); - } - - $max = min(self::MAX_GENERATED_IMAGES, $contentType->maxMediaCount()); - - if ($imageCount > $max) { - return __('posts.edit.compliance.too_many_files', ['max' => (string) $max]); - } - - return null; - } -} diff --git a/composer.json b/composer.json index fd8778b7..e86d17de 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,6 @@ "posthog/posthog-php": "^4.1", "predis/predis": "^3.3", "sendkit/sendkit-laravel": "^1.1", - "simplepie/simplepie": "^1.9", "socialiteproviders/facebook": "^4.1", "socialiteproviders/instagram": "^5.1", "socialiteproviders/linkedin": "^5.0", diff --git a/composer.lock b/composer.lock index de9fa6eb..f3dea776 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0a41ea1ff205782a01973b2ad02fd76c", + "content-hash": "ef65b49279941f3458f444863f0ce6e4", "packages": [ { "name": "aws/aws-crt-php", @@ -7244,87 +7244,6 @@ }, "time": "2026-03-20T00:53:21+00:00" }, - { - "name": "simplepie/simplepie", - "version": "1.9.0", - "source": { - "type": "git", - "url": "https://github.com/simplepie/simplepie.git", - "reference": "76cccb1b2c5dcaf44f304c925ab30c0f48643992" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/simplepie/simplepie/zipball/76cccb1b2c5dcaf44f304c925ab30c0f48643992", - "reference": "76cccb1b2c5dcaf44f304c925ab30c0f48643992", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "ext-xml": "*", - "ext-xmlreader": "*", - "php": ">=7.2.0" - }, - "require-dev": { - "donatj/mock-webserver": "^2.7", - "friendsofphp/php-cs-fixer": "^2.19 || ^3.8", - "mf2/mf2": "^0.5.0", - "phpstan/phpstan": "~1.12.2", - "phpunit/phpunit": "^8 || ^9 || ^10", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", - "psr/simple-cache": "^1 || ^2 || ^3" - }, - "suggest": { - "ext-curl": "", - "ext-iconv": "", - "ext-intl": "", - "ext-mbstring": "", - "mf2/mf2": "Microformat module that allows for parsing HTML for microformats" - }, - "type": "library", - "autoload": { - "psr-0": { - "SimplePie": "library" - }, - "psr-4": { - "SimplePie\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Ryan Parman", - "homepage": "http://ryanparman.com/", - "role": "Creator, alumnus developer" - }, - { - "name": "Sam Sneddon", - "homepage": "https://gsnedders.com/", - "role": "Alumnus developer" - }, - { - "name": "Ryan McCue", - "email": "me@ryanmccue.info", - "homepage": "http://ryanmccue.info/", - "role": "Developer" - } - ], - "description": "A simple Atom/RSS parsing library for PHP", - "homepage": "http://simplepie.org/", - "keywords": [ - "atom", - "feeds", - "rss" - ], - "support": { - "issues": "https://github.com/simplepie/simplepie/issues", - "source": "https://github.com/simplepie/simplepie/tree/1.9.0" - }, - "time": "2025-09-12T06:34:27+00:00" - }, { "name": "socialiteproviders/facebook", "version": "4.1.0", diff --git a/config/horizon.php b/config/horizon.php index 45ec87b5..954deb54 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -256,21 +256,6 @@ 'nice' => 0, ], - 'automations' => [ - 'connection' => 'redis', - 'queue' => ['automations'], - 'balance' => 'auto', - 'autoScalingStrategy' => 'time', - 'minProcesses' => 1, - 'maxProcesses' => 3, - 'timeout' => 630, - 'maxTime' => 0, - 'maxJobs' => 0, - 'memory' => 256, - 'tries' => 1, - 'nice' => 0, - ], - 'webhooks' => [ 'connection' => 'redis', 'queue' => ['webhooks'], @@ -307,12 +292,6 @@ 'balanceCooldown' => 3, ], - 'automations' => [ - 'maxProcesses' => 5, - 'balanceMaxShift' => 1, - 'balanceCooldown' => 3, - ], - 'webhooks' => [ 'maxProcesses' => 3, 'balanceMaxShift' => 1, @@ -333,10 +312,6 @@ 'maxProcesses' => 2, ], - 'automations' => [ - 'maxProcesses' => 2, - ], - 'webhooks' => [ 'maxProcesses' => 1, ], diff --git a/config/trypost.php b/config/trypost.php index d0288b99..5eff1009 100644 --- a/config/trypost.php +++ b/config/trypost.php @@ -72,7 +72,7 @@ | | SafeHttpFetcher blocks requests to private/reserved IP ranges (SSRF | protection) by default. Self-hosted operators who need to fetch from - | their own internal network (e.g. an internal RSS feed or webhook) can + | their own internal network (e.g. an internal webhook endpoint) can | opt in here. Leave disabled unless you understand the SSRF risk. | */ @@ -145,9 +145,9 @@ | Outbound User-Agent |-------------------------------------------------------------------------- | - | Branded User-Agent applied to outbound HTTP from automation nodes - | (http_request) and workspace webhooks so recipients know the request came from - | TryPost.it. Self-hosters can override it. + | Branded User-Agent applied to outbound HTTP from workspace webhooks so + | recipients know the request came from TryPost.it. Self-hosters can + | override it. | */ diff --git a/database/factories/AutomationFactory.php b/database/factories/AutomationFactory.php deleted file mode 100644 index 1dcaed65..00000000 --- a/database/factories/AutomationFactory.php +++ /dev/null @@ -1,62 +0,0 @@ - Workspace::factory(), - 'user_id' => User::factory(), - 'name' => fake()->sentence(3), - 'status' => Status::Draft, - 'nodes' => [], - 'connections' => [], - ]; - } - - public function active(): static - { - return $this->state(fn () => [ - 'status' => Status::Active, - 'activated_at' => now(), - ]); - } - - public function paused(): static - { - return $this->state(fn () => [ - 'status' => Status::Paused, - 'paused_at' => now(), - ]); - } - - public function withScheduleTrigger(string $cron = '0 9 * * *'): static - { - return $this->state(fn () => [ - 'nodes' => [ - [ - 'id' => 'trigger_1', - 'type' => 'trigger', - 'position' => ['x' => 0, 'y' => 0], - 'data' => [ - 'trigger_type' => 'schedule', - 'cron' => $cron, - ], - ], - ], - 'connections' => [], - ]); - } -} diff --git a/database/factories/AutomationNodeRunFactory.php b/database/factories/AutomationNodeRunFactory.php deleted file mode 100644 index e68c6313..00000000 --- a/database/factories/AutomationNodeRunFactory.php +++ /dev/null @@ -1,28 +0,0 @@ - AutomationRun::factory(), - 'node_id' => 'node_'.fake()->randomNumber(6), - 'node_type' => NodeType::Generate, - 'status' => Status::Running, - 'input' => [], - 'started_at' => now(), - ]; - } -} diff --git a/database/factories/AutomationNodeStateFactory.php b/database/factories/AutomationNodeStateFactory.php deleted file mode 100644 index 92ba1fc5..00000000 --- a/database/factories/AutomationNodeStateFactory.php +++ /dev/null @@ -1,23 +0,0 @@ - Automation::factory(), - 'node_id' => 'node_'.fake()->randomNumber(6), - 'data' => [], - ]; - } -} diff --git a/database/factories/AutomationRunFactory.php b/database/factories/AutomationRunFactory.php deleted file mode 100644 index f6bfbec4..00000000 --- a/database/factories/AutomationRunFactory.php +++ /dev/null @@ -1,51 +0,0 @@ - Automation::factory(), - 'status' => Status::Pending, - 'is_manual' => false, - 'is_dry_run' => false, - 'context' => [], - ]; - } - - public function running(string $nodeId = 'node_1'): static - { - return $this->state(fn () => [ - 'status' => Status::Running, - 'current_node_id' => $nodeId, - 'started_at' => now(), - ]); - } - - public function waiting(\DateTimeInterface $until): static - { - return $this->state(fn () => [ - 'status' => Status::Waiting, - 'next_action_at' => $until, - ]); - } - - public function completed(): static - { - return $this->state(fn () => [ - 'status' => Status::Completed, - 'finished_at' => now(), - ]); - } -} diff --git a/database/factories/AutomationTriggerItemFactory.php b/database/factories/AutomationTriggerItemFactory.php deleted file mode 100644 index ca370207..00000000 --- a/database/factories/AutomationTriggerItemFactory.php +++ /dev/null @@ -1,27 +0,0 @@ - Automation::factory(), - 'item_key' => fake()->uuid(), - 'payload' => [ - 'title' => fake()->sentence(), - 'url' => fake()->url(), - ], - 'first_seen_at' => now(), - ]; - } -} diff --git a/database/migrations/2026_09_05_124637_drop_automation_tables.php b/database/migrations/2026_09_05_124637_drop_automation_tables.php new file mode 100644 index 00000000..a1b72910 --- /dev/null +++ b/database/migrations/2026_09_05_124637_drop_automation_tables.php @@ -0,0 +1,26 @@ +where('created_via', 'automation')->update(['created_via' => 'web']); + + Schema::dropIfExists('automation_node_states'); + Schema::dropIfExists('automation_node_runs'); + Schema::dropIfExists('automation_runs'); + Schema::dropIfExists('automation_trigger_items'); + Schema::dropIfExists('automations'); + } + + public function down(): void + { + // Irreversible: the automations module and its data are gone for good. + } +}; diff --git a/lang/ar/automations.php b/lang/ar/automations.php deleted file mode 100644 index 014134ff..00000000 --- a/lang/ar/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'يعمل محرر الأتمتة بشكل أفضل على شاشة أكبر. افتحه على جهاز كمبيوتر مكتبي لإنشاء سير عملك.', - 'title' => 'الأتمتة', - 'default_name' => 'أتمتة جديدة', - - 'actions' => [ - 'new' => 'أتمتة جديدة', - 'edit' => 'تعديل', - 'save' => 'حفظ', - 'activate' => 'تفعيل', - 'pause' => 'إيقاف مؤقت', - 'delete' => 'حذف', - 'retry' => 'إعادة المحاولة', - 'guide' => 'تعرّف على آلية العمل', - ], - - 'tabs' => [ - 'build' => 'البناء', - 'variables' => 'المتغيرات', - 'test' => 'اختبار', - ], - - 'nav' => [ - 'workflow' => 'سير العمل', - 'invocations' => 'عمليات التشغيل', - 'metrics' => 'المقاييس', - 'settings' => 'الإعدادات', - ], - - 'settings' => [ - 'general' => 'عام', - 'general_description' => 'إعادة تسمية هذه الأتمتة.', - 'name_label' => 'الاسم', - 'name_saved' => 'تمت إعادة تسمية الأتمتة.', - 'status_title' => 'الحالة', - 'status_description' => 'فعّلها لبدء تشغيلها، أو أوقفها مؤقتًا للتوقف.', - 'activated_at' => 'تم التفعيل :date', - 'paused_at' => 'تم الإيقاف المؤقت :date', - 'created_at' => 'تم الإنشاء :date', - 'danger_title' => 'منطقة الخطر', - 'danger_description' => 'إجراءات لا يمكن التراجع عنها.', - 'delete_title' => 'حذف هذه الأتمتة', - 'delete_description' => 'يزيل الأتمتة وسجل تشغيلها نهائيًا.', - ], - - 'status_run' => [ - 'pending' => 'قيد الانتظار', - 'running' => 'قيد التشغيل', - 'waiting' => 'في الانتظار', - 'completed' => 'مكتمل', - 'failed' => 'فشل', - 'cancelled' => 'مُلغى', - ], - - 'node_type' => [ - 'trigger' => 'مُشغّل', - 'generate' => 'إنشاء محتوى', - 'delay' => 'تأخير', - 'condition' => 'شرط', - 'publish' => 'نشر', - 'end' => 'إنهاء', - 'fetch_rss' => 'جلب RSS', - 'http_request' => 'طلب HTTP', - ], - - 'invocations' => [ - 'empty' => 'لا توجد عمليات تشغيل بعد.', - 'refresh' => 'تحديث', - 'search_placeholder' => 'البحث عبر معرّف التشغيل…', - 'copied' => 'تم نسخ معرّف التشغيل.', - 'loading' => 'جارٍ تحميل الخطوات…', - 'no_steps' => 'لم يتم تسجيل أي خطوات.', - 'load_error' => 'تعذر تحميل الخطوات.', - 'steps' => '{0}لا خطوات|{1}خطوة واحدة|{2}خطوتان|[3,10]:count خطوات|[11,*]:count خطوة', - 'filter' => [ - 'all' => 'جميع الحالات', - ], - 'columns' => [ - 'timestamp' => 'الطابع الزمني', - 'run' => 'التشغيل', - 'status' => 'الحالة', - 'message' => 'آخر رسالة', - 'duration' => 'المدة', - ], - 'summary' => [ - 'completed' => 'اكتمل سير العمل', - 'failed' => 'فشل سير العمل', - 'running' => 'سير العمل قيد التشغيل', - 'cancelled' => 'تم إلغاء سير العمل', - 'pending' => 'سير العمل قيد الانتظار', - ], - ], - - 'metrics' => [ - 'overview' => 'نظرة عامة', - 'runs_over_time' => 'عمليات التشغيل عبر الزمن', - 'posts_by_platform' => 'المنشورات حسب المنصة', - 'no_posts' => 'لم يتم نشر أي منشورات في هذه الفترة.', - 'cards' => [ - 'runs' => 'إجمالي عمليات التشغيل', - 'completed' => 'مكتملة', - 'failed' => 'فاشلة', - 'in_progress' => 'قيد التنفيذ', - 'success_rate' => 'معدل النجاح', - 'avg_duration' => 'متوسط المدة', - 'posts_created' => 'المنشورات المُنشأة', - ], - 'legend' => [ - 'started' => 'بدأت', - 'completed' => 'اكتملت', - 'failed' => 'فشلت', - ], - ], - - 'categories' => [ - 'sources' => 'المصادر', - 'content' => 'المحتوى', - 'flow' => 'التدفق', - 'output' => 'الإخراج', - ], - - 'variables' => [ - 'title' => 'متغيرات سير العمل', - 'hint' => 'قيم قابلة لإعادة الاستخدام يُشار إليها في أي مكان عبر {{ variables.KEY }}. تُخزَّن مشفّرة.', - 'empty' => 'لا توجد متغيرات بعد.', - 'key' => 'المفتاح', - 'value' => 'القيمة', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'القيمة', - 'add' => 'متغير جديد', - ], - - 'expr' => [ - 'trigger_event' => 'اسم حدث المُشغّل', - 'trigger_fired_at' => 'وقت إطلاق المُشغّل', - 'trigger_post_id' => 'معرّف المنشور المُشغِّل', - 'trigger_post_content' => 'محتوى المنشور المُشغِّل', - 'trigger_post_status' => 'حالة المنشور المُشغِّل', - 'trigger_post_scheduled_at' => 'وقت جدولة المنشور', - 'trigger_post_published_at' => 'وقت نشر المنشور', - 'fetched_title' => 'عنوان العنصر المجلوب', - 'fetched_link' => 'رابط العنصر المجلوب', - 'fetched_date' => 'تاريخ نشر العنصر المجلوب', - 'fetched_content' => 'المحتوى الكامل للعنصر المجلوب', - 'fetched_description' => 'ملخص العنصر المجلوب', - 'fetched_author' => 'كاتب العنصر المجلوب', - 'fetched_image' => 'رابط صورة العنصر المجلوب', - 'fetched_categories' => 'فئات العنصر المجلوب', - 'fetched_enclosure' => 'وسائط العنصر المجلوب (صوت/فيديو/ملف)', - 'fetched_pubdate' => 'تاريخ نشر العنصر المجلوب', - 'fetched_http' => 'عنصر HTTP المجلوب (أضِف حقلًا)', - 'generated_content' => 'محتوى المنشور المُنشأ بالذكاء الاصطناعي', - 'generated_post_url' => 'رابط المنشور المُنشأ بالذكاء الاصطناعي', - 'variable' => 'متغير سير العمل', - 'now' => 'التاريخ والوقت الحالي', - ], - - 'test' => [ - 'description' => 'يشغّل الأتمتة من البداية إلى النهاية باستخدام حمولة مُشغِّل مُصطنعة. مفيد للتحقق من كل عقدة دون انتظار الجدول أو الخلاصة الحقيقية.', - 'starting' => 'جارٍ بدء تشغيل الاختبار…', - 'in_progress' => 'قيد التنفيذ', - 'completed' => 'مكتمل', - 'failed' => 'فشل', - 'waiting' => 'في الانتظار', - 'close' => 'إغلاق', - 'no_node_runs' => 'في انتظار بدء العقدة الأولى…', - 'node_input' => 'المدخلات', - 'node_output' => 'المخرجات', - 'node_error' => 'خطأ', - 'no_new_items' => 'لا توجد عناصر جديدة — لم يُشغَّل أي شيء لاحق.', - 'error_starting' => 'تعذر بدء تشغيل الاختبار.', - 'with_real_data' => 'ببيانات حقيقية', - 'run' => 'تشغيل الاختبار', - 'idle_hint' => 'اضغط على "تشغيل الاختبار" لتنفيذ الأتمتة من البداية إلى النهاية.', - 'real_data_hint' => 'سيقوم هذا الاختبار بنشر المنشورات، وتقديم علامات الاستطلاع، وإطلاق تأثيرات جانبية خارجية.', - 'dry_badge' => 'تشغيل تجريبي', - ], - - 'status' => [ - 'draft' => 'مسودة', - 'active' => 'نشط', - 'paused' => 'متوقف مؤقتًا', - ], - - 'index' => [ - 'empty_title' => 'لا توجد عمليات أتمتة بعد', - 'empty_description' => 'أنشئ أول أتمتة لك لبدء النشر تلقائيًا.', - 'columns' => [ - 'name' => 'الاسم', - 'status' => 'الحالة', - 'created' => 'تاريخ الإنشاء', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'تعذر تفعيل الأتمتة.', - 'pause_error_fallback' => 'تعذر إيقاف الأتمتة مؤقتًا.', - 'save_error_fallback' => 'تعذر حفظ الأتمتة.', - 'save_success' => 'تم حفظ الأتمتة.', - 'empty_canvas_title' => 'ابدأ ببناء أتمتتك', - 'empty_canvas_description' => 'اسحب عقدة من اللوحة الجانبية للبدء.', - 'name_placeholder' => 'أتمتة بلا عنوان', - ], - - 'nodes' => [ - 'trigger' => 'مُشغّل', - 'generate' => 'إنشاء', - 'delay' => 'تأخير', - 'condition' => 'شرط', - 'publish' => 'نشر', - 'end' => 'إنهاء', - 'end_summary' => 'يوقف الأتمتة هنا', - 'fetch_rss' => 'جلب RSS', - 'http_request' => 'طلب HTTP', - 'handles' => [ - 'items' => 'يحتوي على عناصر', - 'no_items' => 'لا عناصر', - ], - ], - - 'config' => [ - 'select_placeholder' => 'اختر…', - 'invalid_json' => 'هذا ليس JSON صالحًا بعد.', - 'expand_editor' => 'توسيع المحرر', - 'minimize_editor' => 'تصغير', - - 'trigger' => [ - 'type' => 'نوع المُشغّل', - 'types' => [ - 'schedule' => 'جدولة', - 'post_published' => 'عند نشر منشور', - 'post_scheduled' => 'عند جدولة منشور', - ], - 'post_published_hint' => 'يعمل كلما تم نشر أي منشور في مساحة العمل هذه. يصبح المنشور المنشور متاحًا في {{ trigger.post }} للعقد اللاحقة.', - 'post_scheduled_hint' => 'يعمل كلما تمت جدولة أي منشور في مساحة العمل هذه. يكون المنشور المجدول متاحًا في {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'فاصل المُشغّل', - 'fields' => [ - 'minutes' => 'دقائق', - 'hours' => 'ساعات', - 'days' => 'أيام', - 'weeks' => 'أسابيع', - 'months' => 'أشهر', - ], - 'minutes_interval' => 'الدقائق بين عمليات التشغيل', - 'hours_interval' => 'الساعات بين عمليات التشغيل', - 'days_interval' => 'الأيام بين عمليات التشغيل', - 'hour' => 'التشغيل عند الساعة', - 'minute' => 'التشغيل عند الدقيقة', - 'weekdays' => 'التشغيل في أيام الأسبوع', - 'day_of_month' => 'يوم من الشهر', - 'weekday_names' => [ - 'sun' => 'أحد', - 'mon' => 'اثنين', - 'tue' => 'ثلاثاء', - 'wed' => 'أربعاء', - 'thu' => 'خميس', - 'fri' => 'جمعة', - 'sat' => 'سبت', - ], - 'summary' => [ - 'every_n_minutes' => '{1}يعمل كل دقيقة|{2}يعمل كل دقيقتين|[3,10]يعمل كل :count دقائق|[11,*]يعمل كل :count دقيقة', - 'every_n_hours' => '{1}يعمل كل ساعة عند الدقيقة :minute|{2}يعمل كل ساعتين عند الدقيقة :minute|[3,10]يعمل كل :count ساعات عند الدقيقة :minute|[11,*]يعمل كل :count ساعة عند الدقيقة :minute', - 'every_n_days' => '{1}يعمل كل يوم عند :time|{2}يعمل كل يومين عند :time|[3,10]يعمل كل :count أيام عند :time|[11,*]يعمل كل :count يوم عند :time', - 'weekly' => 'يعمل كل :days عند :time', - 'monthly' => 'يعمل في اليوم :day من كل شهر عند :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'الحسابات الاجتماعية', - 'social_accounts_empty' => 'لا توجد حسابات اجتماعية متصلة. اربط واحدًا أولًا.', - 'target_slide_count' => 'الشرائح المراد إنشاؤها', - 'prompt_template' => 'قالب الموجّه', - 'prompt_template_hint' => 'اكتب {{ لإدراج بيانات من الخطوات السابقة.', - 'image_count' => 'الصور المراد إنشاؤها', - 'image_count_hint' => '0 = منشور نصي فقط (بلا صورة). 1 = صورة واحدة. 2 أو أكثر = عرض دائري.', - 'use_brand_voice' => 'استخدام صوت العلامة التجارية', - 'use_brand_voice_hint' => 'طبّق وصف علامتك التجارية وصوتها. أوقفه للتنسيق الأمين لمصادر الجهات الخارجية (الأخبار، RSS).', - 'use_brand_visuals' => 'استخدام العناصر المرئية للعلامة التجارية', - 'use_brand_visuals_hint' => 'وجّه صور الذكاء الاصطناعي بألوان علامتك التجارية وهويتها. أوقفه للصور المحايدة المدفوعة بموضوع المنشور فقط.', - 'style' => 'النمط', - 'account_summary' => '{1}حساب واحد · :format|{2}حسابان · :format|[3,10]:count حسابات · :format|[11,*]:count حساب · :format', - 'formats' => [ - 'single' => 'مفرد', - 'carousel' => 'عرض دائري', - ], - ], - 'delay' => [ - 'duration' => 'المدة', - 'unit' => 'الوحدة', - 'units' => [ - 'minutes' => 'دقائق', - 'hours' => 'ساعات', - 'days' => 'أيام', - ], - ], - 'condition' => [ - 'field' => 'الحقل', - 'operator' => 'العامل', - 'operators' => [ - 'contains' => 'يحتوي على', - 'not_contains' => 'لا يحتوي على', - 'equals' => 'يساوي', - 'not_equals' => 'لا يساوي', - 'matches' => 'يطابق (تعبير نمطي)', - 'greater_than' => 'أكبر من', - 'less_than' => 'أصغر من', - ], - 'value' => 'القيمة', - ], - 'publish' => [ - 'mode' => 'الوضع', - 'modes' => [ - 'now' => 'نشر الآن', - 'scheduled' => 'جدولة', - 'draft' => 'حفظ كمسودة', - ], - 'scheduled_offset' => 'الإزاحة عن المُشغّل (بالدقائق)', - 'offset_summary' => ':mode · +:offset د', - ], - 'end' => [ - 'reason' => 'السبب (اختياري)', - 'reason_placeholder' => 'مثال: تمت تصفيته بواسطة شرط', - ], - 'fetch_rss' => [ - 'feed_url' => 'رابط الخلاصة', - 'feed_url_hint' => 'في التشغيل الأول، تُضبط العلامة على "الآن" حتى لا تُغرِق العناصر التاريخية العقد اللاحقة. تشاهد عمليات التشغيل اللاحقة فقط العناصر الأحدث من الاستطلاع السابق.', - 'inspect' => 'فحص الخلاصة', - 'inspecting' => 'جارٍ الفحص…', - 'inspect_hint' => 'اجلب عينة لاكتشاف الحقول المتاحة للاستخدام في العقد اللاحقة.', - 'inspect_error' => 'تعذرت قراءة هذه الخلاصة. تحقق من الرابط وحاول مرة أخرى.', - 'discovered_fields' => 'الحقول المتاحة', - 'discovered_empty' => 'لم يتم العثور على حقول في أحدث عنصر.', - ], - 'http_request' => [ - 'url' => 'الرابط', - 'method' => 'الطريقة', - 'auth_type' => 'المصادقة', - 'auth' => [ - 'none' => 'بلا (عام)', - 'bearer' => 'رمز Bearer', - 'basic' => 'مصادقة أساسية', - 'api_key' => 'ترويسة مفتاح API', - ], - 'bearer_token' => 'رمز Bearer', - 'basic_username' => 'اسم المستخدم', - 'basic_password' => 'كلمة المرور', - 'api_key_header' => 'اسم الترويسة', - 'api_key_value' => 'مفتاح API', - 'body_template' => 'قالب النص (JSON)', - 'headers' => 'الترويسات', - 'header_name' => 'اسم الترويسة', - 'header_value' => 'القيمة', - 'add_header' => 'إضافة ترويسة', - 'polling_section' => 'القائمة وإزالة التكرار (اختياري)', - 'polling_hint' => 'عندما تكون الاستجابة قائمة، يشغّل كل عنصر سير العمل بشكل منفصل. يشغّل الكائن الواحد مرة واحدة.', - 'items_path' => 'مسار العناصر', - 'items_path_hint' => 'اتركه فارغًا إذا كانت الاستجابة مصفوفة بالفعل. استخدم مسارًا منقّطًا (مثل data.items) لمصفوفة متداخلة، أو * لكائن مفهرس بالمعرّف.', - 'item_key_path' => 'مسار مفتاح العنصر', - 'item_key_path_hint' => 'مسار JSON لمعرّف فريد (مثل id). تُتخطى العناصر التي سبقت رؤيتها، لذا تظل الخلاصة بلا تواريخ تُمرّر الإدخالات الجديدة فقط.', - 'item_date_path' => 'مسار تاريخ العنصر', - 'item_date_path_hint' => 'مسار JSON للطابع الزمني للعنصر (مثل published_at). يُفضَّل على مسار المفتاح عند توفره. يسجّل الاستطلاع الأول الأساس ولا يمرّر شيئًا، لذا لا تُغرِق الخلاصة الموجودة في اليوم الأول.', - ], - ], - - 'delete' => [ - 'title' => 'حذف الأتمتة', - 'description' => 'هل أنت متأكد من رغبتك في حذف هذه الأتمتة؟ ستتم إزالة جميع عمليات التشغيل وعناصر التشغيل أيضًا. لا يمكن التراجع عن هذا الإجراء.', - 'confirm' => 'حذف', - 'cancel' => 'إلغاء', - ], - - 'flash' => [ - 'deleted' => 'تم حذف الأتمتة بنجاح!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'لا توجد حسابات اجتماعية نشطة مُهيّأة لهذه الأتمتة.', - 'must_have_one_trigger' => 'يجب أن تحتوي الأتمتة على عقدة مُشغّل واحدة بالضبط.', - 'trigger_must_be_connected' => 'يجب أن تكون عقدة المُشغّل متصلة بعقدة واحدة على الأقل.', - 'graph_contains_cycle' => 'يحتوي مخطط الأتمتة على حلقة.', - 'only_failed_can_retry' => 'يمكن إعادة محاولة عمليات التشغيل الفاشلة فقط.', - 'no_generated_post' => 'لم يتم العثور على منشور مُنشأ في التشغيل.', - 'url_not_allowed' => 'رابط الطلب يشير إلى عنوان خاص أو غير قابل للوصول وتم حظره.', - 'node_no_longer_exists' => 'العقدة :node_id لم تعد موجودة في الأتمتة.', - 'no_trigger_connection' => 'لا توجد عقدة متصلة بعقدة المُشغّل.', - 'fetch_rss_missing_url' => 'عقدة جلب RSS تفتقد إلى رابط خلاصة.', - 'fetch_rss_request_failed' => 'فشل طلب خلاصة RSS.', - 'fetch_rss_malformed' => 'خلاصة RSS مشوّهة.', - 'http_missing_url' => 'عقدة طلب HTTP تفتقد إلى رابط.', - 'http_request_exception' => 'أطلق طلب HTTP استثناءً.', - 'http_request_failed' => 'فشل طلب HTTP.', - 'http_items_path_not_array' => 'لم يُفضِ مسار العناصر إلى قائمة.', - 'generate_image_format_required' => 'توليد الذكاء الاصطناعي ينشئ صورًا فقط. اختر تنسيق صورة (وليس فيديو).', - ], -]; diff --git a/lang/ar/common.php b/lang/ar/common.php index 51f6e74d..0061b087 100644 --- a/lang/ar/common.php +++ b/lang/ar/common.php @@ -6,8 +6,6 @@ 'back' => 'رجوع', - 'beta' => 'تجريبي', - 'confirm_modal' => [ 'cannot_be_undone' => 'لا يمكن التراجع عن هذا الإجراء.', 'type' => 'اكتب', diff --git a/lang/ar/sidebar.php b/lang/ar/sidebar.php index 2140c498..75f34366 100644 --- a/lang/ar/sidebar.php +++ b/lang/ar/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'أخرى', ], 'analytics' => 'التحليلات', - 'automations' => 'الأتمتة', 'onboarding' => 'البدء', 'onboarding_hint' => 'أكمل الإعداد', 'posts' => [ diff --git a/lang/de/automations.php b/lang/de/automations.php deleted file mode 100644 index 67c28cc0..00000000 --- a/lang/de/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Der Automatisierungs-Editor funktioniert am besten auf einem größeren Bildschirm. Öffne ihn auf einem Desktop, um deinen Workflow zu erstellen.', - 'title' => 'Automatisierungen', - 'default_name' => 'Neue Automatisierung', - - 'actions' => [ - 'new' => 'Neue Automatisierung', - 'edit' => 'Bearbeiten', - 'save' => 'Speichern', - 'activate' => 'Aktivieren', - 'pause' => 'Pausieren', - 'delete' => 'Löschen', - 'retry' => 'Erneut versuchen', - 'guide' => 'So funktioniert es', - ], - - 'tabs' => [ - 'build' => 'Erstellen', - 'variables' => 'Variablen', - 'test' => 'Test', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Ausführungen', - 'metrics' => 'Kennzahlen', - 'settings' => 'Einstellungen', - ], - - 'settings' => [ - 'general' => 'Allgemein', - 'general_description' => 'Benenne diese Automatisierung um.', - 'name_label' => 'Name', - 'name_saved' => 'Automatisierung umbenannt.', - 'status_title' => 'Status', - 'status_description' => 'Aktiviere sie, um sie auszuführen, oder pausiere sie, um sie zu stoppen.', - 'activated_at' => 'Aktiviert :date', - 'paused_at' => 'Pausiert :date', - 'created_at' => 'Erstellt :date', - 'danger_title' => 'Gefahrenzone', - 'danger_description' => 'Unumkehrbare Aktionen.', - 'delete_title' => 'Diese Automatisierung löschen', - 'delete_description' => 'Entfernt die Automatisierung und ihren Ausführungsverlauf dauerhaft.', - ], - - 'status_run' => [ - 'pending' => 'Ausstehend', - 'running' => 'Läuft', - 'waiting' => 'Wartet', - 'completed' => 'Abgeschlossen', - 'failed' => 'Fehlgeschlagen', - 'cancelled' => 'Abgebrochen', - ], - - 'node_type' => [ - 'trigger' => 'Trigger', - 'generate' => 'Inhalt generieren', - 'delay' => 'Verzögerung', - 'condition' => 'Bedingung', - 'publish' => 'Veröffentlichen', - 'end' => 'Ende', - 'fetch_rss' => 'RSS abrufen', - 'http_request' => 'HTTP-Anfrage', - ], - - 'invocations' => [ - 'empty' => 'Noch keine Ausführungen.', - 'refresh' => 'Aktualisieren', - 'search_placeholder' => 'Nach Ausführungs-ID suchen…', - 'copied' => 'Ausführungs-ID kopiert.', - 'loading' => 'Schritte werden geladen…', - 'no_steps' => 'Keine Schritte aufgezeichnet.', - 'load_error' => 'Schritte konnten nicht geladen werden.', - 'steps' => '{0}Keine Schritte|{1}:count Schritt|[2,*]:count Schritte', - 'filter' => [ - 'all' => 'Alle Status', - ], - 'columns' => [ - 'timestamp' => 'Zeitstempel', - 'run' => 'Ausführung', - 'status' => 'Status', - 'message' => 'Letzte Meldung', - 'duration' => 'Dauer', - ], - 'summary' => [ - 'completed' => 'Workflow abgeschlossen', - 'failed' => 'Workflow fehlgeschlagen', - 'running' => 'Workflow läuft', - 'cancelled' => 'Workflow abgebrochen', - 'pending' => 'Workflow ausstehend', - ], - ], - - 'metrics' => [ - 'overview' => 'Übersicht', - 'runs_over_time' => 'Ausführungen im Zeitverlauf', - 'posts_by_platform' => 'Beiträge nach Plattform', - 'no_posts' => 'In diesem Zeitraum wurden keine Beiträge veröffentlicht.', - 'cards' => [ - 'runs' => 'Ausführungen gesamt', - 'completed' => 'Abgeschlossen', - 'failed' => 'Fehlgeschlagen', - 'in_progress' => 'In Bearbeitung', - 'success_rate' => 'Erfolgsquote', - 'avg_duration' => 'Durchschn. Dauer', - 'posts_created' => 'Erstellte Beiträge', - ], - 'legend' => [ - 'started' => 'Gestartet', - 'completed' => 'Abgeschlossen', - 'failed' => 'Fehlgeschlagen', - ], - ], - - 'categories' => [ - 'sources' => 'Quellen', - 'content' => 'Inhalt', - 'flow' => 'Ablauf', - 'output' => 'Ausgabe', - ], - - 'variables' => [ - 'title' => 'Workflow-Variablen', - 'hint' => 'Wiederverwendbare Werte, die überall mit {{ variables.KEY }} referenziert werden. Verschlüsselt gespeichert.', - 'empty' => 'Noch keine Variablen.', - 'key' => 'Schlüssel', - 'value' => 'Wert', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Wert', - 'add' => 'Neue Variable', - ], - - 'expr' => [ - 'trigger_event' => 'Name des Trigger-Events', - 'trigger_fired_at' => 'Wann der Trigger ausgelöst wurde', - 'trigger_post_id' => 'ID des auslösenden Beitrags', - 'trigger_post_content' => 'Inhalt des auslösenden Beitrags', - 'trigger_post_status' => 'Status des auslösenden Beitrags', - 'trigger_post_scheduled_at' => 'Wann der Beitrag geplant ist', - 'trigger_post_published_at' => 'Wann der Beitrag veröffentlicht wurde', - 'fetched_title' => 'Titel des abgerufenen Eintrags', - 'fetched_link' => 'Link des abgerufenen Eintrags', - 'fetched_date' => 'Veröffentlichungsdatum des abgerufenen Eintrags', - 'fetched_content' => 'Vollständiger Inhalt des abgerufenen Eintrags', - 'fetched_description' => 'Zusammenfassung des abgerufenen Eintrags', - 'fetched_author' => 'Autor des abgerufenen Eintrags', - 'fetched_image' => 'Bild-URL des abgerufenen Eintrags', - 'fetched_categories' => 'Kategorien des abgerufenen Eintrags', - 'fetched_enclosure' => 'Medien des abgerufenen Eintrags (Audio/Video/Datei)', - 'fetched_pubdate' => 'Veröffentlichungsdatum des abgerufenen Eintrags', - 'fetched_http' => 'Abgerufener HTTP-Eintrag (Feld anhängen)', - 'generated_content' => 'KI-generierter Beitragsinhalt', - 'generated_post_url' => 'URL des KI-generierten Beitrags', - 'variable' => 'Workflow-Variable', - 'now' => 'Aktuelles Datum & Uhrzeit', - ], - - 'test' => [ - 'description' => 'Führt die Automatisierung durchgängig mit einer synthetisierten Trigger-Nutzlast aus. Nützlich, um jeden Node zu validieren, ohne auf den echten Zeitplan oder Feed zu warten.', - 'starting' => 'Testlauf wird gestartet…', - 'in_progress' => 'In Bearbeitung', - 'completed' => 'Abgeschlossen', - 'failed' => 'Fehlgeschlagen', - 'waiting' => 'Wartet', - 'close' => 'Schließen', - 'no_node_runs' => 'Warten auf den Start des ersten Nodes…', - 'node_input' => 'Eingabe', - 'node_output' => 'Ausgabe', - 'node_error' => 'Fehler', - 'no_new_items' => 'Keine neuen Einträge – nichts Nachgelagertes wurde ausgeführt.', - 'error_starting' => 'Der Testlauf konnte nicht gestartet werden.', - 'with_real_data' => 'Mit echten Daten', - 'run' => 'Test ausführen', - 'idle_hint' => 'Klicke auf „Test ausführen", um die Automatisierung durchgängig auszuführen.', - 'real_data_hint' => 'Dieser Test veröffentlicht Beiträge, setzt Polling-Markierungen fort und löst externe Seiteneffekte aus.', - 'dry_badge' => 'Probelauf', - ], - - 'status' => [ - 'draft' => 'Entwurf', - 'active' => 'Aktiv', - 'paused' => 'Pausiert', - ], - - 'index' => [ - 'empty_title' => 'Noch keine Automatisierungen', - 'empty_description' => 'Erstelle deine erste Automatisierung, um auf Autopilot zu veröffentlichen.', - 'columns' => [ - 'name' => 'Name', - 'status' => 'Status', - 'created' => 'Erstellt', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Automatisierung konnte nicht aktiviert werden.', - 'pause_error_fallback' => 'Automatisierung konnte nicht pausiert werden.', - 'save_error_fallback' => 'Automatisierung konnte nicht gespeichert werden.', - 'save_success' => 'Automatisierung gespeichert.', - 'empty_canvas_title' => 'Beginne mit dem Aufbau deiner Automatisierung', - 'empty_canvas_description' => 'Ziehe einen Node aus dem linken Bereich, um zu starten.', - 'name_placeholder' => 'Unbenannte Automatisierung', - ], - - 'nodes' => [ - 'trigger' => 'Trigger', - 'generate' => 'Generieren', - 'delay' => 'Verzögerung', - 'condition' => 'Bedingung', - 'publish' => 'Veröffentlichen', - 'end' => 'Ende', - 'end_summary' => 'Stoppt die Automatisierung hier', - 'fetch_rss' => 'RSS abrufen', - 'http_request' => 'HTTP-Anfrage', - 'handles' => [ - 'items' => 'hat Einträge', - 'no_items' => 'keine Einträge', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Auswählen…', - 'invalid_json' => 'Das ist noch kein gültiges JSON.', - 'expand_editor' => 'Editor vergrößern', - 'minimize_editor' => 'Verkleinern', - - 'trigger' => [ - 'type' => 'Trigger-Typ', - 'types' => [ - 'schedule' => 'Zeitplan', - 'post_published' => 'Wenn ein Beitrag veröffentlicht wird', - 'post_scheduled' => 'Wenn ein Beitrag geplant wird', - ], - 'post_published_hint' => 'Läuft, wann immer ein Beitrag in diesem Workspace veröffentlicht wird. Der veröffentlichte Beitrag steht unter {{ trigger.post }} für nachgelagerte Nodes zur Verfügung.', - 'post_scheduled_hint' => 'Läuft, wann immer ein Beitrag in diesem Workspace geplant wird. Der geplante Beitrag steht unter {{ trigger.post }} zur Verfügung.', - - 'schedule' => [ - 'field' => 'Trigger-Intervall', - 'fields' => [ - 'minutes' => 'Minuten', - 'hours' => 'Stunden', - 'days' => 'Tage', - 'weeks' => 'Wochen', - 'months' => 'Monate', - ], - 'minutes_interval' => 'Minuten zwischen den Triggern', - 'hours_interval' => 'Stunden zwischen den Triggern', - 'days_interval' => 'Tage zwischen den Triggern', - 'hour' => 'Auslösen zur Stunde', - 'minute' => 'Auslösen zur Minute', - 'weekdays' => 'An Wochentagen auslösen', - 'day_of_month' => 'Tag des Monats', - 'weekday_names' => [ - 'sun' => 'So', - 'mon' => 'Mo', - 'tue' => 'Di', - 'wed' => 'Mi', - 'thu' => 'Do', - 'fri' => 'Fr', - 'sat' => 'Sa', - ], - 'summary' => [ - 'every_n_minutes' => 'Läuft jede Minute|Läuft alle :count Minuten', - 'every_n_hours' => 'Läuft jede Stunde zur Minute :minute|Läuft alle :count Stunden zur Minute :minute', - 'every_n_days' => 'Läuft täglich um :time|Läuft alle :count Tage um :time', - 'weekly' => 'Läuft :days um :time', - 'monthly' => 'Läuft an Tag :day jedes Monats um :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Social-Media-Konten', - 'social_accounts_empty' => 'Keine verbundenen Social-Media-Konten. Verbinde zuerst eines.', - 'target_slide_count' => 'Zu generierende Slides', - 'prompt_template' => 'Prompt-Vorlage', - 'prompt_template_hint' => 'Tippe {{, um Daten aus vorherigen Schritten einzufügen.', - 'image_count' => 'Zu generierende Bilder', - 'image_count_hint' => '0 = reiner Textbeitrag (kein Bild). 1 = einzelnes Bild. 2+ = Karussell.', - 'use_brand_voice' => 'Markenton verwenden', - 'use_brand_voice_hint' => 'Wende deine Markenbeschreibung und deinen Markenton an. Deaktiviere dies für die originalgetreue Kuratierung von Drittquellen (News, RSS).', - 'use_brand_visuals' => 'Marken-Visuals verwenden', - 'use_brand_visuals_hint' => 'Steuere KI-Bilder mit deinen Markenfarben und deiner Markenidentität. Deaktiviere dies für neutrale Bilder, die nur vom Beitragsthema bestimmt werden.', - 'style' => 'Stil', - 'account_summary' => ':count Konto · :format|:count Konten · :format', - 'formats' => [ - 'single' => 'Einzeln', - 'carousel' => 'Karussell', - ], - ], - 'delay' => [ - 'duration' => 'Dauer', - 'unit' => 'Einheit', - 'units' => [ - 'minutes' => 'Minuten', - 'hours' => 'Stunden', - 'days' => 'Tage', - ], - ], - 'condition' => [ - 'field' => 'Feld', - 'operator' => 'Operator', - 'operators' => [ - 'contains' => 'enthält', - 'not_contains' => 'enthält nicht', - 'equals' => 'ist gleich', - 'not_equals' => 'ist ungleich', - 'matches' => 'entspricht (Regex)', - 'greater_than' => 'größer als', - 'less_than' => 'kleiner als', - ], - 'value' => 'Wert', - ], - 'publish' => [ - 'mode' => 'Modus', - 'modes' => [ - 'now' => 'Jetzt veröffentlichen', - 'scheduled' => 'Planen', - 'draft' => 'Als Entwurf speichern', - ], - 'scheduled_offset' => 'Versatz zum Trigger (Minuten)', - 'offset_summary' => ':mode · +:offset Min.', - ], - 'end' => [ - 'reason' => 'Grund (optional)', - 'reason_placeholder' => 'z. B. Durch Bedingung herausgefiltert', - ], - 'fetch_rss' => [ - 'feed_url' => 'Feed-URL', - 'feed_url_hint' => 'Beim ersten Durchlauf wird die Markierung auf "now" gesetzt, damit historische Einträge die nachgelagerten Nodes nicht überfluten. Nachfolgende Durchläufe sehen nur Einträge, die neuer sind als die vorherige Abfrage.', - 'inspect' => 'Feed prüfen', - 'inspecting' => 'Wird geprüft…', - 'inspect_hint' => 'Rufe ein Beispiel ab, um die verfügbaren Felder für nachgelagerte Nodes zu ermitteln.', - 'inspect_error' => 'Dieser Feed konnte nicht gelesen werden. Prüfe die URL und versuche es erneut.', - 'discovered_fields' => 'Verfügbare Felder', - 'discovered_empty' => 'Keine Felder im neuesten Eintrag gefunden.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Methode', - 'auth_type' => 'Authentifizierung', - 'auth' => [ - 'none' => 'Keine (öffentlich)', - 'bearer' => 'Bearer-Token', - 'basic' => 'Basic Auth', - 'api_key' => 'API-Key-Header', - ], - 'bearer_token' => 'Bearer-Token', - 'basic_username' => 'Benutzername', - 'basic_password' => 'Passwort', - 'api_key_header' => 'Header-Name', - 'api_key_value' => 'API-Key', - 'body_template' => 'Body-Vorlage (JSON)', - 'headers' => 'Header', - 'header_name' => 'Header-Name', - 'header_value' => 'Wert', - 'add_header' => 'Header hinzufügen', - 'polling_section' => 'Liste & Deduplizierung (optional)', - 'polling_hint' => 'Wenn die Antwort eine Liste ist, durchläuft jeder Eintrag den Workflow separat. Ein einzelnes Objekt wird einmal ausgeführt.', - 'items_path' => 'Pfad zu den Einträgen', - 'items_path_hint' => 'Leer lassen, wenn die Antwort bereits ein Array ist. Verwende einen Punkt-Pfad (z. B. data.items) für ein verschachteltes Array oder * für ein nach ID indiziertes Objekt.', - 'item_key_path' => 'Pfad zum Eintragsschlüssel', - 'item_key_path_hint' => 'JSON-Pfad zu einer eindeutigen ID (z. B. id). Bereits gesehene Einträge werden übersprungen, sodass ein Feed ohne Datumsangaben trotzdem nur neue Einträge weiterleitet.', - 'item_date_path' => 'Pfad zum Eintragsdatum', - 'item_date_path_hint' => 'JSON-Pfad zum Zeitstempel des Eintrags (z. B. published_at). Wird, sofern verfügbar, dem Schlüssel-Pfad vorgezogen. Die erste Abfrage erfasst den Ausgangswert und leitet nichts weiter, sodass ein bestehender Feed am ersten Tag niemals überflutet.', - ], - ], - - 'delete' => [ - 'title' => 'Automatisierung löschen', - 'description' => 'Möchtest du diese Automatisierung wirklich löschen? Alle Ausführungen und Trigger-Einträge werden ebenfalls entfernt. Diese Aktion kann nicht rückgängig gemacht werden.', - 'confirm' => 'Löschen', - 'cancel' => 'Abbrechen', - ], - - 'flash' => [ - 'deleted' => 'Automatisierung erfolgreich gelöscht!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Für diese Automatisierung sind keine aktiven Social-Media-Konten konfiguriert.', - 'must_have_one_trigger' => 'Eine Automatisierung muss genau einen Trigger-Node haben.', - 'trigger_must_be_connected' => 'Der Trigger-Node muss mit mindestens einem Node verbunden sein.', - 'graph_contains_cycle' => 'Der Automatisierungsgraph enthält einen Zyklus.', - 'only_failed_can_retry' => 'Nur fehlgeschlagene Ausführungen können wiederholt werden.', - 'no_generated_post' => 'Bei der Ausführung wurde kein generierter Beitrag gefunden.', - 'url_not_allowed' => 'Die Anfrage-URL verweist auf eine private oder nicht erreichbare Adresse und wurde blockiert.', - 'node_no_longer_exists' => 'Node :node_id existiert in der Automatisierung nicht mehr.', - 'no_trigger_connection' => 'Kein Node mit dem Trigger-Node verbunden.', - 'fetch_rss_missing_url' => 'Dem Node „RSS abrufen" fehlt eine Feed-URL.', - 'fetch_rss_request_failed' => 'Die Anfrage an den RSS-Feed ist fehlgeschlagen.', - 'fetch_rss_malformed' => 'Der RSS-Feed ist fehlerhaft.', - 'http_missing_url' => 'Dem HTTP-Anfrage-Node fehlt eine URL.', - 'http_request_exception' => 'Die HTTP-Anfrage hat eine Ausnahme ausgelöst.', - 'http_request_failed' => 'Die HTTP-Anfrage ist fehlgeschlagen.', - 'http_items_path_not_array' => 'Der Pfad zu den Einträgen ergab keine Liste.', - 'generate_image_format_required' => 'KI-Generierung erstellt nur Bilder. Wähle ein Bildformat (kein Video).', - ], -]; diff --git a/lang/de/common.php b/lang/de/common.php index 0d0db425..e62b1598 100644 --- a/lang/de/common.php +++ b/lang/de/common.php @@ -6,8 +6,6 @@ 'back' => 'Zurück', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Dies kann nicht rückgängig gemacht werden.', 'type' => 'Gib', diff --git a/lang/de/sidebar.php b/lang/de/sidebar.php index 338d3361..73160a27 100644 --- a/lang/de/sidebar.php +++ b/lang/de/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Sonstiges', ], 'analytics' => 'Analytics', - 'automations' => 'Automatisierungen', 'onboarding' => 'Erste Schritte', 'onboarding_hint' => 'Einrichtung abschließen', 'posts' => [ diff --git a/lang/el/automations.php b/lang/el/automations.php deleted file mode 100644 index f4aa2d8e..00000000 --- a/lang/el/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Ο επεξεργαστής αυτοματισμών λειτουργεί καλύτερα σε μεγαλύτερη οθόνη. Άνοιξέ τον σε υπολογιστή για να δημιουργήσεις τη ροή εργασίας σου.', - 'title' => 'Αυτοματισμοί', - 'default_name' => 'Νέος αυτοματισμός', - - 'actions' => [ - 'new' => 'Νέος αυτοματισμός', - 'edit' => 'Επεξεργασία', - 'save' => 'Αποθήκευση', - 'activate' => 'Ενεργοποίηση', - 'pause' => 'Παύση', - 'delete' => 'Διαγραφή', - 'retry' => 'Επανάληψη', - 'guide' => 'Μάθετε πώς λειτουργεί', - ], - - 'tabs' => [ - 'build' => 'Δημιουργία', - 'variables' => 'Μεταβλητές', - 'test' => 'Δοκιμή', - ], - - 'nav' => [ - 'workflow' => 'Ροή εργασίας', - 'invocations' => 'Εκτελέσεις', - 'metrics' => 'Μετρήσεις', - 'settings' => 'Ρυθμίσεις', - ], - - 'settings' => [ - 'general' => 'Γενικά', - 'general_description' => 'Μετονομάστε αυτόν τον αυτοματισμό.', - 'name_label' => 'Όνομα', - 'name_saved' => 'Ο αυτοματισμός μετονομάστηκε.', - 'status_title' => 'Κατάσταση', - 'status_description' => 'Ενεργοποιήστε για να ξεκινήσει η εκτέλεση ή κάντε παύση για να σταματήσει.', - 'activated_at' => 'Ενεργοποιήθηκε :date', - 'paused_at' => 'Σε παύση :date', - 'created_at' => 'Δημιουργήθηκε :date', - 'danger_title' => 'Ζώνη κινδύνου', - 'danger_description' => 'Μη αναστρέψιμες ενέργειες.', - 'delete_title' => 'Διαγραφή αυτού του αυτοματισμού', - 'delete_description' => 'Αφαιρεί οριστικά τον αυτοματισμό και το ιστορικό εκτελέσεών του.', - ], - - 'status_run' => [ - 'pending' => 'Σε εκκρεμότητα', - 'running' => 'Σε εξέλιξη', - 'waiting' => 'Σε αναμονή', - 'completed' => 'Ολοκληρώθηκε', - 'failed' => 'Απέτυχε', - 'cancelled' => 'Ακυρώθηκε', - ], - - 'node_type' => [ - 'trigger' => 'Έναυσμα', - 'generate' => 'Δημιουργία περιεχομένου', - 'delay' => 'Καθυστέρηση', - 'condition' => 'Συνθήκη', - 'publish' => 'Δημοσίευση', - 'end' => 'Τέλος', - 'fetch_rss' => 'Ανάκτηση RSS', - 'http_request' => 'Αίτημα HTTP', - ], - - 'invocations' => [ - 'empty' => 'Δεν υπάρχουν εκτελέσεις ακόμη.', - 'refresh' => 'Ανανέωση', - 'search_placeholder' => 'Αναζήτηση με ID εκτέλεσης…', - 'copied' => 'Το ID εκτέλεσης αντιγράφηκε.', - 'loading' => 'Φόρτωση βημάτων…', - 'no_steps' => 'Δεν καταγράφηκαν βήματα.', - 'load_error' => 'Δεν ήταν δυνατή η φόρτωση των βημάτων.', - 'steps' => '{0}Κανένα βήμα|{1}:count βήμα|[2,*]:count βήματα', - 'filter' => [ - 'all' => 'Όλες οι καταστάσεις', - ], - 'columns' => [ - 'timestamp' => 'Χρονική σήμανση', - 'run' => 'Εκτέλεση', - 'status' => 'Κατάσταση', - 'message' => 'Τελευταίο μήνυμα', - 'duration' => 'Διάρκεια', - ], - 'summary' => [ - 'completed' => 'Η ροή εργασίας ολοκληρώθηκε', - 'failed' => 'Η ροή εργασίας απέτυχε', - 'running' => 'Η ροή εργασίας εκτελείται', - 'cancelled' => 'Η ροή εργασίας ακυρώθηκε', - 'pending' => 'Η ροή εργασίας εκκρεμεί', - ], - ], - - 'metrics' => [ - 'overview' => 'Επισκόπηση', - 'runs_over_time' => 'Εκτελέσεις με την πάροδο του χρόνου', - 'posts_by_platform' => 'Δημοσιεύσεις ανά πλατφόρμα', - 'no_posts' => 'Δεν δημοσιεύτηκαν δημοσιεύσεις σε αυτή την περίοδο.', - 'cards' => [ - 'runs' => 'Συνολικές εκτελέσεις', - 'completed' => 'Ολοκληρώθηκαν', - 'failed' => 'Απέτυχαν', - 'in_progress' => 'Σε εξέλιξη', - 'success_rate' => 'Ποσοστό επιτυχίας', - 'avg_duration' => 'Μέση διάρκεια', - 'posts_created' => 'Δημοσιεύσεις που δημιουργήθηκαν', - ], - 'legend' => [ - 'started' => 'Ξεκίνησαν', - 'completed' => 'Ολοκληρώθηκαν', - 'failed' => 'Απέτυχαν', - ], - ], - - 'categories' => [ - 'sources' => 'Πηγές', - 'content' => 'Περιεχόμενο', - 'flow' => 'Ροή', - 'output' => 'Έξοδος', - ], - - 'variables' => [ - 'title' => 'Μεταβλητές ροής εργασίας', - 'hint' => 'Επαναχρησιμοποιήσιμες τιμές που αναφέρονται οπουδήποτε με {{ variables.KEY }}. Αποθηκεύονται κρυπτογραφημένες.', - 'empty' => 'Δεν υπάρχουν μεταβλητές ακόμη.', - 'key' => 'Κλειδί', - 'value' => 'Τιμή', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Τιμή', - 'add' => 'Νέα μεταβλητή', - ], - - 'expr' => [ - 'trigger_event' => 'Όνομα συμβάντος εναύσματος', - 'trigger_fired_at' => 'Πότε ενεργοποιήθηκε το έναυσμα', - 'trigger_post_id' => 'ID δημοσίευσης εναύσματος', - 'trigger_post_content' => 'Περιεχόμενο δημοσίευσης εναύσματος', - 'trigger_post_status' => 'Κατάσταση δημοσίευσης εναύσματος', - 'trigger_post_scheduled_at' => 'Πότε είναι προγραμματισμένη η δημοσίευση', - 'trigger_post_published_at' => 'Πότε δημοσιεύτηκε η δημοσίευση', - 'fetched_title' => 'Τίτλος ανακτηθέντος στοιχείου', - 'fetched_link' => 'Σύνδεσμος ανακτηθέντος στοιχείου', - 'fetched_date' => 'Ημερομηνία δημοσίευσης ανακτηθέντος στοιχείου', - 'fetched_content' => 'Πλήρες περιεχόμενο ανακτηθέντος στοιχείου', - 'fetched_description' => 'Σύνοψη ανακτηθέντος στοιχείου', - 'fetched_author' => 'Συντάκτης ανακτηθέντος στοιχείου', - 'fetched_image' => 'URL εικόνας ανακτηθέντος στοιχείου', - 'fetched_categories' => 'Κατηγορίες ανακτηθέντος στοιχείου', - 'fetched_enclosure' => 'Πολυμέσα ανακτηθέντος στοιχείου (ήχος/βίντεο/αρχείο)', - 'fetched_pubdate' => 'Ημερομηνία δημοσίευσης ανακτηθέντος στοιχείου', - 'fetched_http' => 'Ανακτηθέν στοιχείο HTTP (προσθέστε ένα πεδίο)', - 'generated_content' => 'Περιεχόμενο δημοσίευσης που δημιούργησε το AI', - 'generated_post_url' => 'URL δημοσίευσης που δημιούργησε το AI', - 'variable' => 'Μεταβλητή ροής εργασίας', - 'now' => 'Τρέχουσα ημερομηνία και ώρα', - ], - - 'test' => [ - 'description' => 'Εκτελεί τον αυτοματισμό από άκρη σε άκρη χρησιμοποιώντας ένα συνθετικό payload εναύσματος. Χρήσιμο για την επικύρωση κάθε κόμβου χωρίς αναμονή για το πραγματικό χρονοδιάγραμμα ή τη ροή.', - 'starting' => 'Έναρξη δοκιμαστικής εκτέλεσης…', - 'in_progress' => 'Σε εξέλιξη', - 'completed' => 'Ολοκληρώθηκε', - 'failed' => 'Απέτυχε', - 'waiting' => 'Σε αναμονή', - 'close' => 'Κλείσιμο', - 'no_node_runs' => 'Αναμονή για την έναρξη του πρώτου κόμβου…', - 'node_input' => 'Είσοδος', - 'node_output' => 'Έξοδος', - 'node_error' => 'Σφάλμα', - 'no_new_items' => 'Δεν υπάρχουν νέα στοιχεία — τίποτα δεν εκτελέστηκε παρακάτω.', - 'error_starting' => 'Δεν ήταν δυνατή η έναρξη της δοκιμαστικής εκτέλεσης.', - 'with_real_data' => 'Με πραγματικά δεδομένα', - 'run' => 'Εκτέλεση δοκιμής', - 'idle_hint' => 'Πατήστε Εκτέλεση δοκιμής για να εκτελέσετε τον αυτοματισμό από άκρη σε άκρη.', - 'real_data_hint' => 'Αυτή η δοκιμή θα δημοσιεύσει δημοσιεύσεις, θα προωθήσει τα σημεία ελέγχου polling και θα ενεργοποιήσει εξωτερικές παρενέργειες.', - 'dry_badge' => 'Δοκιμαστική εκτέλεση', - ], - - 'status' => [ - 'draft' => 'Πρόχειρο', - 'active' => 'Ενεργός', - 'paused' => 'Σε παύση', - ], - - 'index' => [ - 'empty_title' => 'Δεν υπάρχουν αυτοματισμοί ακόμη', - 'empty_description' => 'Δημιουργήστε τον πρώτο σας αυτοματισμό για να ξεκινήσετε να δημοσιεύετε αυτόματα.', - 'columns' => [ - 'name' => 'Όνομα', - 'status' => 'Κατάσταση', - 'created' => 'Δημιουργήθηκε', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Δεν ήταν δυνατή η ενεργοποίηση του αυτοματισμού.', - 'pause_error_fallback' => 'Δεν ήταν δυνατή η παύση του αυτοματισμού.', - 'save_error_fallback' => 'Δεν ήταν δυνατή η αποθήκευση του αυτοματισμού.', - 'save_success' => 'Ο αυτοματισμός αποθηκεύτηκε.', - 'empty_canvas_title' => 'Ξεκινήστε να δημιουργείτε τον αυτοματισμό σας', - 'empty_canvas_description' => 'Σύρετε έναν κόμβο από τον αριστερό πίνακα για να ξεκινήσετε.', - 'name_placeholder' => 'Αυτοματισμός χωρίς τίτλο', - ], - - 'nodes' => [ - 'trigger' => 'Έναυσμα', - 'generate' => 'Δημιουργία', - 'delay' => 'Καθυστέρηση', - 'condition' => 'Συνθήκη', - 'publish' => 'Δημοσίευση', - 'end' => 'Τέλος', - 'end_summary' => 'Σταματά τον αυτοματισμό εδώ', - 'fetch_rss' => 'Ανάκτηση RSS', - 'http_request' => 'Αίτημα HTTP', - 'handles' => [ - 'items' => 'έχει στοιχεία', - 'no_items' => 'κανένα στοιχείο', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Επιλέξτε…', - 'invalid_json' => 'Αυτό δεν είναι ακόμη έγκυρο JSON.', - 'expand_editor' => 'Ανάπτυξη επεξεργαστή', - 'minimize_editor' => 'Ελαχιστοποίηση', - - 'trigger' => [ - 'type' => 'Τύπος εναύσματος', - 'types' => [ - 'schedule' => 'Χρονοδιάγραμμα', - 'post_published' => 'Όταν δημοσιεύεται μια δημοσίευση', - 'post_scheduled' => 'Όταν προγραμματίζεται μια δημοσίευση', - ], - 'post_published_hint' => 'Εκτελείται κάθε φορά που δημοσιεύεται οποιαδήποτε δημοσίευση σε αυτό το workspace. Η δημοσιευμένη δημοσίευση γίνεται διαθέσιμη στο {{ trigger.post }} για τους επόμενους κόμβους.', - 'post_scheduled_hint' => 'Εκτελείται κάθε φορά που προγραμματίζεται οποιαδήποτε δημοσίευση σε αυτό το workspace. Η προγραμματισμένη δημοσίευση είναι διαθέσιμη στο {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Διάστημα εναύσματος', - 'fields' => [ - 'minutes' => 'Λεπτά', - 'hours' => 'Ώρες', - 'days' => 'Ημέρες', - 'weeks' => 'Εβδομάδες', - 'months' => 'Μήνες', - ], - 'minutes_interval' => 'Λεπτά μεταξύ εναυσμάτων', - 'hours_interval' => 'Ώρες μεταξύ εναυσμάτων', - 'days_interval' => 'Ημέρες μεταξύ εναυσμάτων', - 'hour' => 'Έναυσμα στην ώρα', - 'minute' => 'Έναυσμα στο λεπτό', - 'weekdays' => 'Έναυσμα σε ημέρες της εβδομάδας', - 'day_of_month' => 'Ημέρα του μήνα', - 'weekday_names' => [ - 'sun' => 'Κυρ', - 'mon' => 'Δευ', - 'tue' => 'Τρί', - 'wed' => 'Τετ', - 'thu' => 'Πέμ', - 'fri' => 'Παρ', - 'sat' => 'Σάβ', - ], - 'summary' => [ - 'every_n_minutes' => 'Εκτελείται κάθε λεπτό|Εκτελείται κάθε :count λεπτά', - 'every_n_hours' => 'Εκτελείται κάθε ώρα στο λεπτό :minute|Εκτελείται κάθε :count ώρες στο λεπτό :minute', - 'every_n_days' => 'Εκτελείται κάθε ημέρα στις :time|Εκτελείται κάθε :count ημέρες στις :time', - 'weekly' => 'Εκτελείται κάθε :days στις :time', - 'monthly' => 'Εκτελείται την ημέρα :day κάθε μήνα στις :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Λογαριασμοί κοινωνικών δικτύων', - 'social_accounts_empty' => 'Δεν υπάρχουν συνδεδεμένοι λογαριασμοί κοινωνικών δικτύων. Συνδέστε πρώτα έναν.', - 'target_slide_count' => 'Slides προς δημιουργία', - 'prompt_template' => 'Πρότυπο prompt', - 'prompt_template_hint' => 'Πληκτρολογήστε {{ για εισαγωγή δεδομένων από προηγούμενα βήματα.', - 'image_count' => 'Εικόνες προς δημιουργία', - 'image_count_hint' => '0 = δημοσίευση μόνο με κείμενο (χωρίς εικόνα). 1 = μία εικόνα. 2+ = carousel.', - 'use_brand_voice' => 'Χρήση φωνής μάρκας', - 'use_brand_voice_hint' => 'Εφαρμόστε την περιγραφή και τη φωνή της μάρκας σας. Απενεργοποιήστε το για πιστή επιμέλεια πηγών τρίτων (ειδήσεις, RSS).', - 'use_brand_visuals' => 'Χρήση οπτικών στοιχείων μάρκας', - 'use_brand_visuals_hint' => 'Κατευθύνετε τις εικόνες AI με τα χρώματα και την ταυτότητα της μάρκας σας. Απενεργοποιήστε το για ουδέτερες εικόνες που καθορίζονται μόνο από το θέμα της δημοσίευσης.', - 'style' => 'Ύφος', - 'account_summary' => ':count λογαριασμός · :format|:count λογαριασμοί · :format', - 'formats' => [ - 'single' => 'μεμονωμένο', - 'carousel' => 'carousel', - ], - ], - 'delay' => [ - 'duration' => 'Διάρκεια', - 'unit' => 'Μονάδα', - 'units' => [ - 'minutes' => 'Λεπτά', - 'hours' => 'Ώρες', - 'days' => 'Ημέρες', - ], - ], - 'condition' => [ - 'field' => 'Πεδίο', - 'operator' => 'Τελεστής', - 'operators' => [ - 'contains' => 'περιέχει', - 'not_contains' => 'δεν περιέχει', - 'equals' => 'ισούται με', - 'not_equals' => 'δεν ισούται με', - 'matches' => 'ταιριάζει (regex)', - 'greater_than' => 'μεγαλύτερο από', - 'less_than' => 'μικρότερο από', - ], - 'value' => 'Τιμή', - ], - 'publish' => [ - 'mode' => 'Λειτουργία', - 'modes' => [ - 'now' => 'Δημοσίευση τώρα', - 'scheduled' => 'Χρονοδιάγραμμα', - 'draft' => 'Αποθήκευση ως πρόχειρο', - ], - 'scheduled_offset' => 'Μετατόπιση από το έναυσμα (λεπτά)', - 'offset_summary' => ':mode · +:offset λεπτά', - ], - 'end' => [ - 'reason' => 'Αιτία (προαιρετικό)', - 'reason_placeholder' => 'π.χ. Φιλτραρίστηκε από τη συνθήκη', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL ροής', - 'feed_url_hint' => 'Στην πρώτη εκτέλεση, το σημείο ελέγχου ορίζεται στο «τώρα» ώστε τα παλαιότερα στοιχεία να μην πλημμυρίσουν τους επόμενους κόμβους. Οι επόμενες εκτελέσεις βλέπουν μόνο στοιχεία νεότερα από το προηγούμενο poll.', - 'inspect' => 'Επιθεώρηση ροής', - 'inspecting' => 'Επιθεώρηση…', - 'inspect_hint' => 'Ανακτήστε ένα δείγμα για να ανακαλύψετε τα διαθέσιμα πεδία προς χρήση στους επόμενους κόμβους.', - 'inspect_error' => 'Δεν ήταν δυνατή η ανάγνωση αυτής της ροής. Ελέγξτε τη διεύθυνση URL και δοκιμάστε ξανά.', - 'discovered_fields' => 'Διαθέσιμα πεδία', - 'discovered_empty' => 'Δεν βρέθηκαν πεδία στο πιο πρόσφατο στοιχείο.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Μέθοδος', - 'auth_type' => 'Ταυτοποίηση', - 'auth' => [ - 'none' => 'Καμία (δημόσιο)', - 'bearer' => 'Bearer token', - 'basic' => 'Basic auth', - 'api_key' => 'Κεφαλίδα κλειδιού API', - ], - 'bearer_token' => 'Bearer token', - 'basic_username' => 'Όνομα χρήστη', - 'basic_password' => 'Κωδικός πρόσβασης', - 'api_key_header' => 'Όνομα κεφαλίδας', - 'api_key_value' => 'Κλειδί API', - 'body_template' => 'Πρότυπο σώματος (JSON)', - 'headers' => 'Κεφαλίδες', - 'header_name' => 'Όνομα κεφαλίδας', - 'header_value' => 'Τιμή', - 'add_header' => 'Προσθήκη κεφαλίδας', - 'polling_section' => 'Λίστα και αφαίρεση διπλότυπων (προαιρετικό)', - 'polling_hint' => 'Όταν η απόκριση είναι λίστα, κάθε στοιχείο εκτελεί τη ροή εργασίας ξεχωριστά. Ένα μεμονωμένο αντικείμενο εκτελείται μία φορά.', - 'items_path' => 'Διαδρομή στοιχείων', - 'items_path_hint' => 'Αφήστε το κενό αν η απόκριση είναι ήδη πίνακας. Χρησιμοποιήστε διαδρομή με τελείες (π.χ. data.items) για εμφωλευμένο πίνακα ή * για αντικείμενο με κλειδί το id.', - 'item_key_path' => 'Διαδρομή κλειδιού στοιχείου', - 'item_key_path_hint' => 'Διαδρομή JSON προς ένα μοναδικό id (π.χ. id). Τα στοιχεία που έχουν ήδη εμφανιστεί παραλείπονται, ώστε μια ροή χωρίς ημερομηνίες να προωθεί μόνο νέες καταχωρίσεις.', - 'item_date_path' => 'Διαδρομή ημερομηνίας στοιχείου', - 'item_date_path_hint' => 'Διαδρομή JSON προς τη χρονική σήμανση του στοιχείου (π.χ. published_at). Προτιμάται έναντι της διαδρομής κλειδιού όταν είναι διαθέσιμη. Το πρώτο poll καταγράφει τη γραμμή βάσης και δεν προωθεί τίποτα, ώστε μια υπάρχουσα ροή να μην πλημμυρίζει την πρώτη ημέρα.', - ], - ], - - 'delete' => [ - 'title' => 'Διαγραφή αυτοματισμού', - 'description' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον αυτοματισμό; Όλες οι εκτελέσεις και τα στοιχεία εναύσματος θα αφαιρεθούν επίσης. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.', - 'confirm' => 'Διαγραφή', - 'cancel' => 'Ακύρωση', - ], - - 'flash' => [ - 'deleted' => 'Ο αυτοματισμός διαγράφηκε με επιτυχία!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Δεν έχουν ρυθμιστεί ενεργοί λογαριασμοί κοινωνικών δικτύων για αυτόν τον αυτοματισμό.', - 'must_have_one_trigger' => 'Ο αυτοματισμός πρέπει να έχει ακριβώς έναν κόμβο εναύσματος.', - 'trigger_must_be_connected' => 'Ο κόμβος εναύσματος πρέπει να είναι συνδεδεμένος με τουλάχιστον έναν κόμβο.', - 'graph_contains_cycle' => 'Το γράφημα του αυτοματισμού περιέχει κύκλο.', - 'only_failed_can_retry' => 'Μόνο οι αποτυχημένες εκτελέσεις μπορούν να επαναληφθούν.', - 'no_generated_post' => 'Δεν βρέθηκε δημιουργημένη δημοσίευση στην εκτέλεση.', - 'url_not_allowed' => 'Η διεύθυνση URL του αιτήματος δείχνει σε ιδιωτική ή μη προσβάσιμη διεύθυνση και αποκλείστηκε.', - 'node_no_longer_exists' => 'Ο κόμβος :node_id δεν υπάρχει πλέον στον αυτοματισμό.', - 'no_trigger_connection' => 'Κανένας κόμβος δεν είναι συνδεδεμένος με τον κόμβο εναύσματος.', - 'fetch_rss_missing_url' => 'Από τον κόμβο Ανάκτησης RSS λείπει μια διεύθυνση URL ροής.', - 'fetch_rss_request_failed' => 'Το αίτημα της ροής RSS απέτυχε.', - 'fetch_rss_malformed' => 'Η ροή RSS είναι δυσμορφική.', - 'http_missing_url' => 'Από τον κόμβο αιτήματος HTTP λείπει μια διεύθυνση URL.', - 'http_request_exception' => 'Το αίτημα HTTP προκάλεσε εξαίρεση.', - 'http_request_failed' => 'Το αίτημα HTTP απέτυχε.', - 'http_items_path_not_array' => 'Η διαδρομή στοιχείων δεν αντιστοιχήθηκε σε λίστα.', - 'generate_image_format_required' => 'Η δημιουργία AI παράγει μόνο εικόνες. Επίλεξε μορφή εικόνας (όχι βίντεο).', - ], -]; diff --git a/lang/el/common.php b/lang/el/common.php index 4155a6ad..ca899b05 100644 --- a/lang/el/common.php +++ b/lang/el/common.php @@ -6,8 +6,6 @@ 'back' => 'Πίσω', - 'beta' => 'Βήτα', - 'confirm_modal' => [ 'cannot_be_undone' => 'Αυτό δεν μπορεί να αναιρεθεί.', 'type' => 'Πληκτρολογήστε', diff --git a/lang/el/sidebar.php b/lang/el/sidebar.php index ae1facb0..dcb51f2f 100644 --- a/lang/el/sidebar.php +++ b/lang/el/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Άλλα', ], 'analytics' => 'Στατιστικά', - 'automations' => 'Αυτοματισμοί', 'onboarding' => 'Ξεκινώντας', 'onboarding_hint' => 'Ολοκλήρωση ρύθμισης', 'posts' => [ diff --git a/lang/en/automations.php b/lang/en/automations.php deleted file mode 100644 index 7ef5001e..00000000 --- a/lang/en/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'The automation editor works best on a larger screen. Open it on a desktop to build your workflow.', - 'title' => 'Automations', - 'default_name' => 'New automation', - - 'actions' => [ - 'new' => 'New automation', - 'edit' => 'Edit', - 'save' => 'Save', - 'activate' => 'Activate', - 'pause' => 'Pause', - 'delete' => 'Delete', - 'retry' => 'Retry', - 'guide' => 'Learn how it works', - ], - - 'tabs' => [ - 'build' => 'Build', - 'variables' => 'Variables', - 'test' => 'Test', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Invocations', - 'metrics' => 'Metrics', - 'settings' => 'Settings', - ], - - 'settings' => [ - 'general' => 'General', - 'general_description' => 'Rename this automation.', - 'name_label' => 'Name', - 'name_saved' => 'Automation renamed.', - 'status_title' => 'Status', - 'status_description' => 'Activate to start running it, or pause to stop.', - 'activated_at' => 'Activated :date', - 'paused_at' => 'Paused :date', - 'created_at' => 'Created :date', - 'danger_title' => 'Danger zone', - 'danger_description' => 'Irreversible actions.', - 'delete_title' => 'Delete this automation', - 'delete_description' => 'Permanently removes the automation and its run history.', - ], - - 'status_run' => [ - 'pending' => 'Pending', - 'running' => 'Running', - 'waiting' => 'Waiting', - 'completed' => 'Completed', - 'failed' => 'Failed', - 'cancelled' => 'Cancelled', - ], - - 'node_type' => [ - 'trigger' => 'Trigger', - 'generate' => 'Generate content', - 'delay' => 'Delay', - 'condition' => 'Condition', - 'publish' => 'Publish', - 'end' => 'End', - 'fetch_rss' => 'Fetch RSS', - 'http_request' => 'HTTP request', - ], - - 'invocations' => [ - 'empty' => 'No invocations yet.', - 'refresh' => 'Refresh', - 'search_placeholder' => 'Search by run ID…', - 'copied' => 'Run ID copied.', - 'loading' => 'Loading steps…', - 'no_steps' => 'No steps recorded.', - 'load_error' => 'Could not load steps.', - 'steps' => '{0}No steps|{1}:count step|[2,*]:count steps', - 'filter' => [ - 'all' => 'All statuses', - ], - 'columns' => [ - 'timestamp' => 'Timestamp', - 'run' => 'Run', - 'status' => 'Status', - 'message' => 'Last message', - 'duration' => 'Duration', - ], - 'summary' => [ - 'completed' => 'Workflow completed', - 'failed' => 'Workflow failed', - 'running' => 'Workflow running', - 'cancelled' => 'Workflow cancelled', - 'pending' => 'Workflow pending', - ], - ], - - 'metrics' => [ - 'overview' => 'Overview', - 'runs_over_time' => 'Runs over time', - 'posts_by_platform' => 'Posts by platform', - 'no_posts' => 'No posts published in this period.', - 'cards' => [ - 'runs' => 'Total runs', - 'completed' => 'Completed', - 'failed' => 'Failed', - 'in_progress' => 'In progress', - 'success_rate' => 'Success rate', - 'avg_duration' => 'Avg duration', - 'posts_created' => 'Posts created', - ], - 'legend' => [ - 'started' => 'Started', - 'completed' => 'Completed', - 'failed' => 'Failed', - ], - ], - - 'categories' => [ - 'sources' => 'Sources', - 'content' => 'Content', - 'flow' => 'Flow', - 'output' => 'Output', - ], - - 'variables' => [ - 'title' => 'Workflow variables', - 'hint' => 'Reusable values referenced anywhere with {{ variables.KEY }}. Stored encrypted.', - 'empty' => 'No variables yet.', - 'key' => 'Key', - 'value' => 'Value', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Value', - 'add' => 'New variable', - ], - - 'expr' => [ - 'trigger_event' => 'Trigger event name', - 'trigger_fired_at' => 'When the trigger fired', - 'trigger_post_id' => 'Triggering post ID', - 'trigger_post_content' => 'Triggering post content', - 'trigger_post_status' => 'Triggering post status', - 'trigger_post_scheduled_at' => 'When the post is scheduled', - 'trigger_post_published_at' => 'When the post was published', - 'fetched_title' => 'Fetched item title', - 'fetched_link' => 'Fetched item link', - 'fetched_date' => 'Fetched item publish date', - 'fetched_content' => 'Fetched item full content', - 'fetched_description' => 'Fetched item summary', - 'fetched_author' => 'Fetched item author', - 'fetched_image' => 'Fetched item image URL', - 'fetched_categories' => 'Fetched item categories', - 'fetched_enclosure' => 'Fetched item media (audio/video/file)', - 'fetched_pubdate' => 'Fetched item publish date', - 'fetched_http' => 'Fetched HTTP item (append a field)', - 'generated_content' => 'AI-generated post content', - 'generated_post_url' => 'AI-generated post URL', - 'variable' => 'Workflow variable', - 'now' => 'Current date & time', - ], - - 'test' => [ - 'description' => 'Runs the automation end-to-end using a synthesized trigger payload. Useful for validating each node without waiting for the real schedule or feed.', - 'starting' => 'Starting test run…', - 'in_progress' => 'In progress', - 'completed' => 'Completed', - 'failed' => 'Failed', - 'waiting' => 'Waiting', - 'close' => 'Close', - 'no_node_runs' => 'Waiting for the first node to start…', - 'node_input' => 'Input', - 'node_output' => 'Output', - 'node_error' => 'Error', - 'no_new_items' => 'No new items — nothing downstream ran.', - 'error_starting' => 'Could not start the test run.', - 'with_real_data' => 'With real data', - 'run' => 'Run test', - 'idle_hint' => 'Hit Run test to execute the automation end-to-end.', - 'real_data_hint' => 'This test will publish posts, advance polling watermarks, and trigger external side effects.', - 'dry_badge' => 'Dry run', - ], - - 'status' => [ - 'draft' => 'Draft', - 'active' => 'Active', - 'paused' => 'Paused', - ], - - 'index' => [ - 'empty_title' => 'No automations yet', - 'empty_description' => 'Create your first automation to start publishing on autopilot.', - 'columns' => [ - 'name' => 'Name', - 'status' => 'Status', - 'created' => 'Created', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Could not activate automation.', - 'pause_error_fallback' => 'Could not pause automation.', - 'save_error_fallback' => 'Could not save automation.', - 'save_success' => 'Automation saved.', - 'empty_canvas_title' => 'Start building your automation', - 'empty_canvas_description' => 'Drag a node from the left panel to get started.', - 'name_placeholder' => 'Untitled automation', - ], - - 'nodes' => [ - 'trigger' => 'Trigger', - 'generate' => 'Generate', - 'delay' => 'Delay', - 'condition' => 'Condition', - 'publish' => 'Publish', - 'end' => 'End', - 'end_summary' => 'Stops the automation here', - 'fetch_rss' => 'Fetch RSS', - 'http_request' => 'HTTP Request', - 'handles' => [ - 'items' => 'has items', - 'no_items' => 'no items', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Select…', - 'invalid_json' => 'This isn’t valid JSON yet.', - 'expand_editor' => 'Expand editor', - 'minimize_editor' => 'Minimize', - - 'trigger' => [ - 'type' => 'Trigger type', - 'types' => [ - 'schedule' => 'Schedule', - 'post_published' => 'When a post is published', - 'post_scheduled' => 'When a post is scheduled', - ], - 'post_published_hint' => 'Runs whenever any post in this workspace is published. The published post becomes available at {{ trigger.post }} for downstream nodes.', - 'post_scheduled_hint' => 'Runs whenever any post in this workspace is scheduled. The scheduled post is available at {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Trigger interval', - 'fields' => [ - 'minutes' => 'Minutes', - 'hours' => 'Hours', - 'days' => 'Days', - 'weeks' => 'Weeks', - 'months' => 'Months', - ], - 'minutes_interval' => 'Minutes between triggers', - 'hours_interval' => 'Hours between triggers', - 'days_interval' => 'Days between triggers', - 'hour' => 'Trigger at hour', - 'minute' => 'Trigger at minute', - 'weekdays' => 'Trigger on weekdays', - 'day_of_month' => 'Day of month', - 'weekday_names' => [ - 'sun' => 'Sun', - 'mon' => 'Mon', - 'tue' => 'Tue', - 'wed' => 'Wed', - 'thu' => 'Thu', - 'fri' => 'Fri', - 'sat' => 'Sat', - ], - 'summary' => [ - 'every_n_minutes' => 'Runs every minute|Runs every :count minutes', - 'every_n_hours' => 'Runs every hour at minute :minute|Runs every :count hours at minute :minute', - 'every_n_days' => 'Runs every day at :time|Runs every :count days at :time', - 'weekly' => 'Runs every :days at :time', - 'monthly' => 'Runs on day :day of every month at :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Social accounts', - 'social_accounts_empty' => 'No connected social accounts. Connect one first.', - 'target_slide_count' => 'Slides to generate', - 'prompt_template' => 'Prompt template', - 'prompt_template_hint' => 'Type {{ to insert data from earlier steps.', - 'image_count' => 'Images to generate', - 'image_count_hint' => '0 = text-only post (no image). 1 = single image. 2+ = carousel.', - 'use_brand_voice' => 'Use brand voice', - 'use_brand_voice_hint' => 'Apply your brand description and voice. Turn off for faithful curation of third-party sources (news, RSS).', - 'use_brand_visuals' => 'Use brand visuals', - 'use_brand_visuals_hint' => 'Steer AI images with your brand colors and identity. Turn off for neutral imagery driven only by the post topic.', - 'style' => 'Style', - 'account_summary' => ':count account · :format|:count accounts · :format', - 'formats' => [ - 'single' => 'single', - 'carousel' => 'carousel', - ], - ], - 'delay' => [ - 'duration' => 'Duration', - 'unit' => 'Unit', - 'units' => [ - 'minutes' => 'Minutes', - 'hours' => 'Hours', - 'days' => 'Days', - ], - ], - 'condition' => [ - 'field' => 'Field', - 'operator' => 'Operator', - 'operators' => [ - 'contains' => 'contains', - 'not_contains' => 'not contains', - 'equals' => 'equals', - 'not_equals' => 'not equals', - 'matches' => 'matches (regex)', - 'greater_than' => 'greater than', - 'less_than' => 'less than', - ], - 'value' => 'Value', - ], - 'publish' => [ - 'mode' => 'Mode', - 'modes' => [ - 'now' => 'Publish now', - 'scheduled' => 'Schedule', - 'draft' => 'Save as draft', - ], - 'scheduled_offset' => 'Offset from trigger (minutes)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Reason (optional)', - 'reason_placeholder' => 'e.g. Filtered out by condition', - ], - 'fetch_rss' => [ - 'feed_url' => 'Feed URL', - 'feed_url_hint' => 'On first run, the watermark is set to "now" so historical items don\'t flood downstream nodes. Subsequent runs only see items newer than the previous poll.', - 'inspect' => 'Inspect feed', - 'inspecting' => 'Inspecting…', - 'inspect_hint' => 'Fetch a sample to discover the available fields for use in downstream nodes.', - 'inspect_error' => 'Could not read this feed. Check the URL and try again.', - 'discovered_fields' => 'Available fields', - 'discovered_empty' => 'No fields found in the latest item.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Method', - 'auth_type' => 'Authentication', - 'auth' => [ - 'none' => 'None (public)', - 'bearer' => 'Bearer token', - 'basic' => 'Basic auth', - 'api_key' => 'API key header', - ], - 'bearer_token' => 'Bearer token', - 'basic_username' => 'Username', - 'basic_password' => 'Password', - 'api_key_header' => 'Header name', - 'api_key_value' => 'API key', - 'body_template' => 'Body template (JSON)', - 'headers' => 'Headers', - 'header_name' => 'Header name', - 'header_value' => 'Value', - 'add_header' => 'Add header', - 'polling_section' => 'List & deduplication (optional)', - 'polling_hint' => 'When the response is a list, each item runs the workflow separately. A single object runs once.', - 'items_path' => 'Items path', - 'items_path_hint' => 'Leave blank if the response is already an array. Use a dotted path (e.g. data.items) for a nested array, or * for an object keyed by id.', - 'item_key_path' => 'Item key path', - 'item_key_path_hint' => 'JSON path to a unique id (e.g. id). Items already seen are skipped, so a feed without dates still only forwards new entries.', - 'item_date_path' => 'Item date path', - 'item_date_path_hint' => 'JSON path to the item timestamp (e.g. published_at). Preferred over the key path when available. The first poll records the baseline and forwards nothing, so an existing feed never floods on day one.', - ], - ], - - 'delete' => [ - 'title' => 'Delete automation', - 'description' => 'Are you sure you want to delete this automation? All runs and trigger items will also be removed. This action cannot be undone.', - 'confirm' => 'Delete', - 'cancel' => 'Cancel', - ], - - 'flash' => [ - 'deleted' => 'Automation deleted successfully!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'No active social accounts configured for this automation.', - 'must_have_one_trigger' => 'Automation must have exactly one trigger node.', - 'trigger_must_be_connected' => 'Trigger node must be connected to at least one node.', - 'graph_contains_cycle' => 'Automation graph contains a cycle.', - 'only_failed_can_retry' => 'Only failed runs can be retried.', - 'no_generated_post' => 'No generated post found on run.', - 'url_not_allowed' => 'The request URL points to a private or unreachable address and was blocked.', - 'node_no_longer_exists' => 'Node :node_id no longer exists in the automation.', - 'no_trigger_connection' => 'No node connected to the Trigger node.', - 'fetch_rss_missing_url' => 'The Fetch RSS node is missing a feed URL.', - 'fetch_rss_request_failed' => 'The RSS feed request failed.', - 'fetch_rss_malformed' => 'The RSS feed is malformed.', - 'http_missing_url' => 'The HTTP request node is missing a URL.', - 'http_request_exception' => 'The HTTP request threw an exception.', - 'http_request_failed' => 'The HTTP request failed.', - 'http_items_path_not_array' => 'The items path did not resolve to a list.', - 'generate_image_format_required' => 'AI generate only creates images. Pick an image format (not video).', - ], -]; diff --git a/lang/en/common.php b/lang/en/common.php index cdb34a74..c665762d 100644 --- a/lang/en/common.php +++ b/lang/en/common.php @@ -6,8 +6,6 @@ 'back' => 'Back', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'This cannot be undone.', 'type' => 'Type', diff --git a/lang/en/sidebar.php b/lang/en/sidebar.php index 60fc9193..0365d998 100644 --- a/lang/en/sidebar.php +++ b/lang/en/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Others', ], 'analytics' => 'Analytics', - 'automations' => 'Automations', 'onboarding' => 'Getting started', 'onboarding_hint' => 'Finish setup', 'posts' => [ diff --git a/lang/es/automations.php b/lang/es/automations.php deleted file mode 100644 index 3d484ae2..00000000 --- a/lang/es/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'El editor de automatizaciones funciona mejor en una pantalla más grande. Ábrelo en un ordenador para crear tu flujo de trabajo.', - 'title' => 'Automatizaciones', - 'default_name' => 'Nueva automatización', - - 'actions' => [ - 'new' => 'Nueva automatización', - 'edit' => 'Editar', - 'save' => 'Guardar', - 'activate' => 'Activar', - 'pause' => 'Pausar', - 'delete' => 'Eliminar', - 'retry' => 'Reintentar', - 'guide' => 'Aprende cómo funciona', - ], - - 'tabs' => [ - 'build' => 'Construir', - 'variables' => 'Variables', - 'test' => 'Probar', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Invocaciones', - 'metrics' => 'Métricas', - 'settings' => 'Configuración', - ], - - 'settings' => [ - 'general' => 'General', - 'general_description' => 'Renombra esta automatización.', - 'name_label' => 'Nombre', - 'name_saved' => 'Automatización renombrada.', - 'status_title' => 'Estado', - 'status_description' => 'Actívala para que empiece a ejecutarse, o pausa para detenerla.', - 'activated_at' => 'Activada el :date', - 'paused_at' => 'Pausada el :date', - 'created_at' => 'Creada el :date', - 'danger_title' => 'Zona de peligro', - 'danger_description' => 'Acciones irreversibles.', - 'delete_title' => 'Eliminar esta automatización', - 'delete_description' => 'Elimina permanentemente la automatización y su historial de ejecuciones.', - ], - - 'status_run' => [ - 'pending' => 'Pendiente', - 'running' => 'Ejecutando', - 'waiting' => 'Esperando', - 'completed' => 'Completado', - 'failed' => 'Fallido', - 'cancelled' => 'Cancelado', - ], - - 'node_type' => [ - 'trigger' => 'Disparador', - 'generate' => 'Generar contenido', - 'delay' => 'Espera', - 'condition' => 'Condición', - 'publish' => 'Publicar', - 'end' => 'Fin', - 'fetch_rss' => 'Obtener RSS', - 'http_request' => 'Petición HTTP', - ], - - 'invocations' => [ - 'empty' => 'Aún no hay invocaciones.', - 'refresh' => 'Actualizar', - 'search_placeholder' => 'Buscar por ID de ejecución…', - 'copied' => 'ID de ejecución copiado.', - 'loading' => 'Cargando pasos…', - 'no_steps' => 'Sin pasos registrados.', - 'load_error' => 'No se pudieron cargar los pasos.', - 'steps' => '{0}Sin pasos|{1}:count paso|[2,*]:count pasos', - 'filter' => [ - 'all' => 'Todos los estados', - ], - 'columns' => [ - 'timestamp' => 'Fecha', - 'run' => 'Ejecución', - 'status' => 'Estado', - 'message' => 'Último mensaje', - 'duration' => 'Duración', - ], - 'summary' => [ - 'completed' => 'Workflow completado', - 'failed' => 'Workflow fallido', - 'running' => 'Workflow en ejecución', - 'cancelled' => 'Workflow cancelado', - 'pending' => 'Workflow pendiente', - ], - ], - - 'metrics' => [ - 'overview' => 'Resumen', - 'runs_over_time' => 'Ejecuciones a lo largo del tiempo', - 'posts_by_platform' => 'Posts por plataforma', - 'no_posts' => 'No se publicaron posts en este período.', - 'cards' => [ - 'runs' => 'Total de ejecuciones', - 'completed' => 'Completadas', - 'failed' => 'Fallidas', - 'in_progress' => 'En progreso', - 'success_rate' => 'Tasa de éxito', - 'avg_duration' => 'Duración media', - 'posts_created' => 'Posts creados', - ], - 'legend' => [ - 'started' => 'Iniciadas', - 'completed' => 'Completadas', - 'failed' => 'Fallidas', - ], - ], - - 'categories' => [ - 'sources' => 'Fuentes', - 'content' => 'Contenido', - 'flow' => 'Flujo', - 'output' => 'Salida', - ], - - 'variables' => [ - 'title' => 'Variables del workflow', - 'hint' => 'Valores reutilizables referenciados en cualquier lugar con {{ variables.KEY }}. Almacenados cifrados.', - 'empty' => 'Aún no hay variables.', - 'key' => 'Clave', - 'value' => 'Valor', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Valor', - 'add' => 'Nueva variable', - ], - - 'expr' => [ - 'trigger_event' => 'Nombre del evento del disparador', - 'trigger_fired_at' => 'Cuándo se disparó', - 'trigger_post_id' => 'ID del post que disparó', - 'trigger_post_content' => 'Contenido del post que disparó', - 'trigger_post_status' => 'Estado del post que disparó', - 'trigger_post_scheduled_at' => 'Cuándo está programado el post', - 'trigger_post_published_at' => 'Cuándo se publicó el post', - 'fetched_title' => 'Título del ítem obtenido', - 'fetched_link' => 'Enlace del ítem obtenido', - 'fetched_date' => 'Fecha de publicación del ítem obtenido', - 'fetched_content' => 'Contenido completo del ítem obtenido', - 'fetched_description' => 'Resumen del ítem obtenido', - 'fetched_author' => 'Autor del ítem obtenido', - 'fetched_image' => 'URL de la imagen del ítem obtenido', - 'fetched_categories' => 'Categorías del ítem obtenido', - 'fetched_enclosure' => 'Multimedia del ítem (audio/vídeo/archivo)', - 'fetched_pubdate' => 'Fecha de publicación del ítem obtenido', - 'fetched_http' => 'Ítem HTTP obtenido (añade un campo)', - 'generated_content' => 'Contenido del post generado por IA', - 'generated_post_url' => 'URL del post generado por IA', - 'variable' => 'Variable del flujo', - 'now' => 'Fecha y hora actuales', - ], - - 'test' => [ - 'description' => 'Ejecuta la automatización de punta a punta usando un payload de disparo sintético. Útil para validar cada nodo sin esperar el cronograma o el feed real.', - 'starting' => 'Iniciando ejecución de prueba…', - 'in_progress' => 'En progreso', - 'completed' => 'Completado', - 'failed' => 'Fallido', - 'waiting' => 'Esperando', - 'close' => 'Cerrar', - 'no_node_runs' => 'Esperando que el primer nodo comience…', - 'node_input' => 'Entrada', - 'node_output' => 'Salida', - 'node_error' => 'Error', - 'no_new_items' => 'Sin elementos nuevos — no se ejecutó nada después.', - 'error_starting' => 'No se pudo iniciar la ejecución de prueba.', - 'with_real_data' => 'Con datos reales', - 'run' => 'Ejecutar prueba', - 'idle_hint' => 'Pulsa Ejecutar prueba para correr la automatización de principio a fin.', - 'real_data_hint' => 'Esta prueba publicará posts, avanzará marcadores de polling y disparará efectos secundarios externos.', - 'dry_badge' => 'Prueba seca', - ], - - 'status' => [ - 'draft' => 'Borrador', - 'active' => 'Activa', - 'paused' => 'Pausada', - ], - - 'index' => [ - 'empty_title' => 'Aún no hay automatizaciones', - 'empty_description' => 'Crea tu primera automatización para empezar a publicar en piloto automático.', - 'columns' => [ - 'name' => 'Nombre', - 'status' => 'Estado', - 'created' => 'Creada', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'No se pudo activar la automatización.', - 'pause_error_fallback' => 'No se pudo pausar la automatización.', - 'save_error_fallback' => 'No se pudo guardar la automatización.', - 'save_success' => 'Automatización guardada.', - 'empty_canvas_title' => 'Empieza a construir tu automatización', - 'empty_canvas_description' => 'Arrastra un nodo del panel izquierdo para empezar.', - 'name_placeholder' => 'Automatización sin título', - ], - - 'nodes' => [ - 'trigger' => 'Disparador', - 'generate' => 'Generar', - 'delay' => 'Retraso', - 'condition' => 'Condición', - 'publish' => 'Publicar', - 'end' => 'Terminar', - 'end_summary' => 'Termina la automatización aquí', - 'fetch_rss' => 'Obtener RSS', - 'http_request' => 'Petición HTTP', - 'handles' => [ - 'items' => 'con elementos', - 'no_items' => 'sin elementos', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Selecciona…', - 'invalid_json' => 'Esto aún no es un JSON válido.', - 'expand_editor' => 'Expandir editor', - 'minimize_editor' => 'Minimizar', - - 'trigger' => [ - 'type' => 'Tipo de disparador', - 'types' => [ - 'schedule' => 'Programación', - 'post_published' => 'Cuando un post se publica', - 'post_scheduled' => 'Cuando un post se programa', - ], - 'post_published_hint' => 'Se ejecuta cada vez que un post en este workspace se publica. El post queda disponible en {{ trigger.post }} para los siguientes nodos.', - 'post_scheduled_hint' => 'Se ejecuta cada vez que un post en este workspace se programa. El post queda disponible en {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Intervalo de disparo', - 'fields' => [ - 'minutes' => 'Minutos', - 'hours' => 'Horas', - 'days' => 'Días', - 'weeks' => 'Semanas', - 'months' => 'Meses', - ], - 'minutes_interval' => 'Minutos entre disparos', - 'hours_interval' => 'Horas entre disparos', - 'days_interval' => 'Días entre disparos', - 'hour' => 'Disparar a la hora', - 'minute' => 'Disparar al minuto', - 'weekdays' => 'Disparar en días', - 'day_of_month' => 'Día del mes', - 'weekday_names' => [ - 'sun' => 'Dom', - 'mon' => 'Lun', - 'tue' => 'Mar', - 'wed' => 'Mié', - 'thu' => 'Jue', - 'fri' => 'Vie', - 'sat' => 'Sáb', - ], - 'summary' => [ - 'every_n_minutes' => 'Se ejecuta cada minuto|Se ejecuta cada :count minutos', - 'every_n_hours' => 'Se ejecuta cada hora en el minuto :minute|Se ejecuta cada :count horas en el minuto :minute', - 'every_n_days' => 'Se ejecuta cada día a las :time|Se ejecuta cada :count días a las :time', - 'weekly' => 'Se ejecuta :days a las :time', - 'monthly' => 'Se ejecuta el día :day de cada mes a las :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Cuentas sociales', - 'social_accounts_empty' => 'Sin cuentas sociales conectadas. Conecta una primero.', - 'target_slide_count' => 'Diapositivas a generar', - 'prompt_template' => 'Plantilla de prompt', - 'prompt_template_hint' => 'Escribe {{ para insertar datos de pasos anteriores.', - 'image_count' => 'Imágenes a generar', - 'image_count_hint' => '0 = post solo texto (sin imagen). 1 = imagen única. 2+ = carrusel.', - 'use_brand_voice' => 'Usar voz de marca', - 'use_brand_voice_hint' => 'Aplica la descripción y la voz de tu marca. Desactiva para curaduría fiel de fuentes de terceros (noticias, RSS).', - 'use_brand_visuals' => 'Usar visual de marca', - 'use_brand_visuals_hint' => 'Guía las imágenes de IA con los colores e identidad de tu marca. Desactiva para imágenes neutrales, guiadas solo por el tema del post.', - 'style' => 'Estilo', - 'account_summary' => ':count cuenta · :format|:count cuentas · :format', - 'formats' => [ - 'single' => 'único', - 'carousel' => 'carrusel', - ], - ], - 'delay' => [ - 'duration' => 'Duración', - 'unit' => 'Unidad', - 'units' => [ - 'minutes' => 'Minutos', - 'hours' => 'Horas', - 'days' => 'Días', - ], - ], - 'condition' => [ - 'field' => 'Campo', - 'operator' => 'Operador', - 'operators' => [ - 'contains' => 'contiene', - 'not_contains' => 'no contiene', - 'equals' => 'es igual a', - 'not_equals' => 'no es igual a', - 'matches' => 'coincide (regex)', - 'greater_than' => 'mayor que', - 'less_than' => 'menor que', - ], - 'value' => 'Valor', - ], - 'publish' => [ - 'mode' => 'Modo', - 'modes' => [ - 'now' => 'Publicar ahora', - 'scheduled' => 'Programar', - 'draft' => 'Guardar como borrador', - ], - 'scheduled_offset' => 'Diferencia desde el disparador (minutos)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Razón (opcional)', - 'reason_placeholder' => 'p.ej. Filtrado por la condición', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL del feed', - 'feed_url_hint' => 'En la primera ejecución, el watermark se fija en "ahora" para no inundar los siguientes nodos con ítems históricos. Ejecuciones siguientes solo ven ítems nuevos.', - 'inspect' => 'Inspeccionar feed', - 'inspecting' => 'Inspeccionando…', - 'inspect_hint' => 'Obtén una muestra para descubrir los campos disponibles para usar en los siguientes nodos.', - 'inspect_error' => 'No se pudo leer este feed. Revisa la URL e inténtalo de nuevo.', - 'discovered_fields' => 'Campos disponibles', - 'discovered_empty' => 'No se encontraron campos en el último ítem.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Método', - 'auth_type' => 'Autenticación', - 'auth' => [ - 'none' => 'Ninguna (pública)', - 'bearer' => 'Bearer token', - 'basic' => 'Basic auth', - 'api_key' => 'Header de API key', - ], - 'bearer_token' => 'Bearer token', - 'basic_username' => 'Usuario', - 'basic_password' => 'Contraseña', - 'api_key_header' => 'Nombre del header', - 'api_key_value' => 'API key', - 'body_template' => 'Plantilla del body (JSON)', - 'headers' => 'Headers', - 'header_name' => 'Nombre del header', - 'header_value' => 'Valor', - 'add_header' => 'Agregar header', - 'polling_section' => 'Lista y deduplicación (opcional)', - 'polling_hint' => 'Cuando la respuesta es una lista, cada ítem ejecuta el flujo por separado. Un objeto único se ejecuta una vez.', - 'items_path' => 'Ruta de ítems', - 'items_path_hint' => 'Deja vacío si la respuesta ya es un array. Usa una ruta con puntos (ej: data.items) para un array anidado, o * para un objeto con claves por id.', - 'item_key_path' => 'Ruta de clave del ítem', - 'item_key_path_hint' => 'Ruta JSON a un id único (ej: id). Los ítems ya vistos se omiten, así un feed sin fechas aún reenvía solo los nuevos.', - 'item_date_path' => 'Ruta de fecha del ítem', - 'item_date_path_hint' => 'Ruta JSON al timestamp del ítem (ej: published_at). Preferido sobre la ruta de clave cuando existe. La primera obtención registra el punto de partida y no reenvía nada, así un feed existente nunca inunda el primer día.', - ], - ], - - 'delete' => [ - 'title' => 'Eliminar automatización', - 'description' => '¿Estás seguro de que deseas eliminar esta automatización? Todas las ejecuciones y elementos del disparador también serán eliminados. Esta acción no se puede deshacer.', - 'confirm' => 'Eliminar', - 'cancel' => 'Cancelar', - ], - - 'flash' => [ - 'deleted' => '¡Automatización eliminada correctamente!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'No hay cuentas sociales activas configuradas para esta automatización.', - 'must_have_one_trigger' => 'La automatización debe tener exactamente un nodo disparador.', - 'trigger_must_be_connected' => 'El nodo disparador debe estar conectado a al menos un nodo.', - 'graph_contains_cycle' => 'El grafo de la automatización contiene un ciclo.', - 'only_failed_can_retry' => 'Solo se pueden reintentar ejecuciones fallidas.', - 'no_generated_post' => 'No se encontró un post generado en la ejecución.', - 'url_not_allowed' => 'La URL de la petición apunta a una dirección privada o inaccesible y fue bloqueada.', - 'node_no_longer_exists' => 'El nodo :node_id ya no existe en la automatización.', - 'no_trigger_connection' => 'Ningún nodo está conectado al nodo disparador.', - 'fetch_rss_missing_url' => 'Al nodo Obtener RSS le falta la URL del feed.', - 'fetch_rss_request_failed' => 'La solicitud del feed RSS falló.', - 'fetch_rss_malformed' => 'El feed RSS está mal formado.', - 'http_missing_url' => 'Al nodo de petición HTTP le falta la URL.', - 'http_request_exception' => 'La petición HTTP lanzó una excepción.', - 'http_request_failed' => 'La petición HTTP falló.', - 'http_items_path_not_array' => 'El items path no resolvió a una lista.', - 'generate_image_format_required' => 'La generación con IA solo crea imágenes. Elige un formato de imagen (no vídeo).', - ], -]; diff --git a/lang/es/common.php b/lang/es/common.php index bbf28c6e..572e05f2 100644 --- a/lang/es/common.php +++ b/lang/es/common.php @@ -6,8 +6,6 @@ 'back' => 'Volver', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Esta acción no se puede deshacer.', 'type' => 'Escribe', diff --git a/lang/es/sidebar.php b/lang/es/sidebar.php index 10624163..185bc85c 100644 --- a/lang/es/sidebar.php +++ b/lang/es/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Otros', ], 'analytics' => 'Analytics', - 'automations' => 'Automatizaciones', 'onboarding' => 'Primeros pasos', 'onboarding_hint' => 'Termina la configuración', 'posts' => [ diff --git a/lang/fr/automations.php b/lang/fr/automations.php deleted file mode 100644 index f57341d4..00000000 --- a/lang/fr/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'L\'éditeur d\'automatisation fonctionne mieux sur un grand écran. Ouvrez-le sur un ordinateur pour créer votre workflow.', - 'title' => 'Automatisations', - 'default_name' => 'Nouvelle automatisation', - - 'actions' => [ - 'new' => 'Nouvelle automatisation', - 'edit' => 'Modifier', - 'save' => 'Enregistrer', - 'activate' => 'Activer', - 'pause' => 'Mettre en pause', - 'delete' => 'Supprimer', - 'retry' => 'Réessayer', - 'guide' => 'Découvrir comment ça marche', - ], - - 'tabs' => [ - 'build' => 'Construire', - 'variables' => 'Variables', - 'test' => 'Tester', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Invocations', - 'metrics' => 'Métriques', - 'settings' => 'Paramètres', - ], - - 'settings' => [ - 'general' => 'Général', - 'general_description' => 'Renommez cette automatisation.', - 'name_label' => 'Nom', - 'name_saved' => 'Automatisation renommée.', - 'status_title' => 'Statut', - 'status_description' => 'Activez pour la lancer, ou mettez en pause pour l\'arrêter.', - 'activated_at' => 'Activée le :date', - 'paused_at' => 'Mise en pause le :date', - 'created_at' => 'Créée le :date', - 'danger_title' => 'Zone de danger', - 'danger_description' => 'Actions irréversibles.', - 'delete_title' => 'Supprimer cette automatisation', - 'delete_description' => 'Supprime définitivement l\'automatisation et son historique d\'exécution.', - ], - - 'status_run' => [ - 'pending' => 'En attente', - 'running' => 'En cours', - 'waiting' => 'En attente', - 'completed' => 'Terminée', - 'failed' => 'Échouée', - 'cancelled' => 'Annulée', - ], - - 'node_type' => [ - 'trigger' => 'Déclencheur', - 'generate' => 'Générer du contenu', - 'delay' => 'Délai', - 'condition' => 'Condition', - 'publish' => 'Publier', - 'end' => 'Fin', - 'fetch_rss' => 'Récupérer RSS', - 'http_request' => 'Requête HTTP', - ], - - 'invocations' => [ - 'empty' => 'Aucune invocation pour le moment.', - 'refresh' => 'Actualiser', - 'search_placeholder' => 'Rechercher par ID d\'exécution…', - 'copied' => 'ID d\'exécution copié.', - 'loading' => 'Chargement des étapes…', - 'no_steps' => 'Aucune étape enregistrée.', - 'load_error' => 'Impossible de charger les étapes.', - 'steps' => '{0}Aucune étape|{1}:count étape|[2,*]:count étapes', - 'filter' => [ - 'all' => 'Tous les statuts', - ], - 'columns' => [ - 'timestamp' => 'Horodatage', - 'run' => 'Exécution', - 'status' => 'Statut', - 'message' => 'Dernier message', - 'duration' => 'Durée', - ], - 'summary' => [ - 'completed' => 'Workflow terminé', - 'failed' => 'Workflow échoué', - 'running' => 'Workflow en cours', - 'cancelled' => 'Workflow annulé', - 'pending' => 'Workflow en attente', - ], - ], - - 'metrics' => [ - 'overview' => 'Vue d\'ensemble', - 'runs_over_time' => 'Exécutions dans le temps', - 'posts_by_platform' => 'Publications par plateforme', - 'no_posts' => 'Aucune publication publiée sur cette période.', - 'cards' => [ - 'runs' => 'Total des exécutions', - 'completed' => 'Terminées', - 'failed' => 'Échouées', - 'in_progress' => 'En cours', - 'success_rate' => 'Taux de réussite', - 'avg_duration' => 'Durée moyenne', - 'posts_created' => 'Publications créées', - ], - 'legend' => [ - 'started' => 'Démarrées', - 'completed' => 'Terminées', - 'failed' => 'Échouées', - ], - ], - - 'categories' => [ - 'sources' => 'Sources', - 'content' => 'Contenu', - 'flow' => 'Flux', - 'output' => 'Sortie', - ], - - 'variables' => [ - 'title' => 'Variables du workflow', - 'hint' => 'Valeurs réutilisables référencées partout avec {{ variables.KEY }}. Stockées de manière chiffrée.', - 'empty' => 'Aucune variable pour le moment.', - 'key' => 'Clé', - 'value' => 'Valeur', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Valeur', - 'add' => 'Nouvelle variable', - ], - - 'expr' => [ - 'trigger_event' => 'Nom de l\'événement déclencheur', - 'trigger_fired_at' => 'Moment du déclenchement', - 'trigger_post_id' => 'ID de la publication déclencheuse', - 'trigger_post_content' => 'Contenu de la publication déclencheuse', - 'trigger_post_status' => 'Statut de la publication déclencheuse', - 'trigger_post_scheduled_at' => 'Moment de programmation de la publication', - 'trigger_post_published_at' => 'Moment de publication de la publication', - 'fetched_title' => 'Titre de l\'élément récupéré', - 'fetched_link' => 'Lien de l\'élément récupéré', - 'fetched_date' => 'Date de publication de l\'élément récupéré', - 'fetched_content' => 'Contenu complet de l\'élément récupéré', - 'fetched_description' => 'Résumé de l\'élément récupéré', - 'fetched_author' => 'Auteur de l\'élément récupéré', - 'fetched_image' => 'URL de l\'image de l\'élément récupéré', - 'fetched_categories' => 'Catégories de l\'élément récupéré', - 'fetched_enclosure' => 'Média de l\'élément récupéré (audio/vidéo/fichier)', - 'fetched_pubdate' => 'Date de publication de l\'élément récupéré', - 'fetched_http' => 'Élément HTTP récupéré (ajoutez un champ)', - 'generated_content' => 'Contenu de publication généré par l\'IA', - 'generated_post_url' => 'URL de la publication générée par l\'IA', - 'variable' => 'Variable du workflow', - 'now' => 'Date et heure actuelles', - ], - - 'test' => [ - 'description' => 'Exécute l\'automatisation de bout en bout à l\'aide d\'un déclencheur synthétisé. Utile pour valider chaque nœud sans attendre la programmation réelle ou le flux.', - 'starting' => 'Démarrage du test…', - 'in_progress' => 'En cours', - 'completed' => 'Terminé', - 'failed' => 'Échoué', - 'waiting' => 'En attente', - 'close' => 'Fermer', - 'no_node_runs' => 'En attente du démarrage du premier nœud…', - 'node_input' => 'Entrée', - 'node_output' => 'Sortie', - 'node_error' => 'Erreur', - 'no_new_items' => 'Aucun nouvel élément — rien n\'a été exécuté en aval.', - 'error_starting' => 'Impossible de démarrer le test.', - 'with_real_data' => 'Avec des données réelles', - 'run' => 'Lancer le test', - 'idle_hint' => 'Cliquez sur Lancer le test pour exécuter l\'automatisation de bout en bout.', - 'real_data_hint' => 'Ce test publiera des publications, fera avancer les repères de polling et déclenchera des effets externes.', - 'dry_badge' => 'Simulation', - ], - - 'status' => [ - 'draft' => 'Brouillon', - 'active' => 'Active', - 'paused' => 'En pause', - ], - - 'index' => [ - 'empty_title' => 'Aucune automatisation pour le moment', - 'empty_description' => 'Créez votre première automatisation pour commencer à publier en pilote automatique.', - 'columns' => [ - 'name' => 'Nom', - 'status' => 'Statut', - 'created' => 'Créée le', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Impossible d\'activer l\'automatisation.', - 'pause_error_fallback' => 'Impossible de mettre en pause l\'automatisation.', - 'save_error_fallback' => 'Impossible d\'enregistrer l\'automatisation.', - 'save_success' => 'Automatisation enregistrée.', - 'empty_canvas_title' => 'Commencez à construire votre automatisation', - 'empty_canvas_description' => 'Faites glisser un nœud depuis le panneau de gauche pour commencer.', - 'name_placeholder' => 'Automatisation sans titre', - ], - - 'nodes' => [ - 'trigger' => 'Déclencheur', - 'generate' => 'Générer', - 'delay' => 'Délai', - 'condition' => 'Condition', - 'publish' => 'Publier', - 'end' => 'Fin', - 'end_summary' => 'Arrête l\'automatisation ici', - 'fetch_rss' => 'Récupérer RSS', - 'http_request' => 'Requête HTTP', - 'handles' => [ - 'items' => 'a des éléments', - 'no_items' => 'aucun élément', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Sélectionner…', - 'invalid_json' => 'Ce n\'est pas encore du JSON valide.', - 'expand_editor' => 'Agrandir l\'éditeur', - 'minimize_editor' => 'Réduire', - - 'trigger' => [ - 'type' => 'Type de déclencheur', - 'types' => [ - 'schedule' => 'Programmation', - 'post_published' => 'Lorsqu\'une publication est publiée', - 'post_scheduled' => 'Lorsqu\'une publication est programmée', - ], - 'post_published_hint' => 'S\'exécute chaque fois qu\'une publication de cet espace de travail est publiée. La publication publiée est disponible via {{ trigger.post }} pour les nœuds en aval.', - 'post_scheduled_hint' => 'S\'exécute chaque fois qu\'une publication de cet espace de travail est programmée. La publication programmée est disponible via {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Intervalle de déclenchement', - 'fields' => [ - 'minutes' => 'Minutes', - 'hours' => 'Heures', - 'days' => 'Jours', - 'weeks' => 'Semaines', - 'months' => 'Mois', - ], - 'minutes_interval' => 'Minutes entre les déclenchements', - 'hours_interval' => 'Heures entre les déclenchements', - 'days_interval' => 'Jours entre les déclenchements', - 'hour' => 'Déclencher à l\'heure', - 'minute' => 'Déclencher à la minute', - 'weekdays' => 'Déclencher les jours de la semaine', - 'day_of_month' => 'Jour du mois', - 'weekday_names' => [ - 'sun' => 'Dim', - 'mon' => 'Lun', - 'tue' => 'Mar', - 'wed' => 'Mer', - 'thu' => 'Jeu', - 'fri' => 'Ven', - 'sat' => 'Sam', - ], - 'summary' => [ - 'every_n_minutes' => 'S\'exécute chaque minute|S\'exécute toutes les :count minutes', - 'every_n_hours' => 'S\'exécute chaque heure à la minute :minute|S\'exécute toutes les :count heures à la minute :minute', - 'every_n_days' => 'S\'exécute chaque jour à :time|S\'exécute tous les :count jours à :time', - 'weekly' => 'S\'exécute chaque :days à :time', - 'monthly' => 'S\'exécute le :day de chaque mois à :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Comptes sociaux', - 'social_accounts_empty' => 'Aucun compte social connecté. Connectez-en un d\'abord.', - 'target_slide_count' => 'Diapositives à générer', - 'prompt_template' => 'Modèle de prompt', - 'prompt_template_hint' => 'Tapez {{ pour insérer des données des étapes précédentes.', - 'image_count' => 'Images à générer', - 'image_count_hint' => '0 = publication texte seul (sans image). 1 = image unique. 2+ = carrousel.', - 'use_brand_voice' => 'Utiliser la voix de la marque', - 'use_brand_voice_hint' => 'Appliquez la description et la voix de votre marque. Désactivez pour une curation fidèle de sources tierces (actualités, RSS).', - 'use_brand_visuals' => 'Utiliser les visuels de la marque', - 'use_brand_visuals_hint' => 'Orientez les images de l\'IA avec les couleurs et l\'identité de votre marque. Désactivez pour des visuels neutres guidés uniquement par le sujet de la publication.', - 'style' => 'Style', - 'account_summary' => ':count compte · :format|:count comptes · :format', - 'formats' => [ - 'single' => 'unique', - 'carousel' => 'carrousel', - ], - ], - 'delay' => [ - 'duration' => 'Durée', - 'unit' => 'Unité', - 'units' => [ - 'minutes' => 'Minutes', - 'hours' => 'Heures', - 'days' => 'Jours', - ], - ], - 'condition' => [ - 'field' => 'Champ', - 'operator' => 'Opérateur', - 'operators' => [ - 'contains' => 'contient', - 'not_contains' => 'ne contient pas', - 'equals' => 'égal à', - 'not_equals' => 'différent de', - 'matches' => 'correspond (regex)', - 'greater_than' => 'supérieur à', - 'less_than' => 'inférieur à', - ], - 'value' => 'Valeur', - ], - 'publish' => [ - 'mode' => 'Mode', - 'modes' => [ - 'now' => 'Publier maintenant', - 'scheduled' => 'Programmer', - 'draft' => 'Enregistrer comme brouillon', - ], - 'scheduled_offset' => 'Décalage par rapport au déclencheur (minutes)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Raison (facultatif)', - 'reason_placeholder' => 'par ex. Filtré par la condition', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL du flux', - 'feed_url_hint' => 'Lors de la première exécution, le repère est fixé à « maintenant » afin que les éléments historiques n\'inondent pas les nœuds en aval. Les exécutions suivantes ne voient que les éléments plus récents que le dernier relevé.', - 'inspect' => 'Inspecter le flux', - 'inspecting' => 'Inspection…', - 'inspect_hint' => 'Récupérez un échantillon pour découvrir les champs disponibles pour les nœuds en aval.', - 'inspect_error' => 'Impossible de lire ce flux. Vérifiez l\'URL et réessayez.', - 'discovered_fields' => 'Champs disponibles', - 'discovered_empty' => 'Aucun champ trouvé dans le dernier élément.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Méthode', - 'auth_type' => 'Authentification', - 'auth' => [ - 'none' => 'Aucune (public)', - 'bearer' => 'Jeton Bearer', - 'basic' => 'Authentification basique', - 'api_key' => 'En-tête de clé API', - ], - 'bearer_token' => 'Jeton Bearer', - 'basic_username' => 'Nom d\'utilisateur', - 'basic_password' => 'Mot de passe', - 'api_key_header' => 'Nom de l\'en-tête', - 'api_key_value' => 'Clé API', - 'body_template' => 'Modèle de corps (JSON)', - 'headers' => 'En-têtes', - 'header_name' => 'Nom de l\'en-tête', - 'header_value' => 'Valeur', - 'add_header' => 'Ajouter un en-tête', - 'polling_section' => 'Liste et déduplication (facultatif)', - 'polling_hint' => 'Lorsque la réponse est une liste, chaque élément exécute le workflow séparément. Un objet unique s\'exécute une fois.', - 'items_path' => 'Chemin des éléments', - 'items_path_hint' => 'Laissez vide si la réponse est déjà un tableau. Utilisez un chemin à points (par ex. data.items) pour un tableau imbriqué, ou * pour un objet indexé par id.', - 'item_key_path' => 'Chemin de la clé d\'élément', - 'item_key_path_hint' => 'Chemin JSON vers un id unique (par ex. id). Les éléments déjà vus sont ignorés, de sorte qu\'un flux sans dates ne transmet que les nouvelles entrées.', - 'item_date_path' => 'Chemin de la date d\'élément', - 'item_date_path_hint' => 'Chemin JSON vers l\'horodatage de l\'élément (par ex. published_at). Privilégié au chemin de clé lorsqu\'il est disponible. Le premier relevé enregistre la référence et ne transmet rien, de sorte qu\'un flux existant n\'inonde jamais dès le premier jour.', - ], - ], - - 'delete' => [ - 'title' => 'Supprimer l\'automatisation', - 'description' => 'Voulez-vous vraiment supprimer cette automatisation ? Toutes les exécutions et les éléments déclencheurs seront également supprimés. Cette action est irréversible.', - 'confirm' => 'Supprimer', - 'cancel' => 'Annuler', - ], - - 'flash' => [ - 'deleted' => 'Automatisation supprimée avec succès !', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Aucun compte social actif configuré pour cette automatisation.', - 'must_have_one_trigger' => 'L\'automatisation doit comporter exactement un nœud déclencheur.', - 'trigger_must_be_connected' => 'Le nœud déclencheur doit être connecté à au moins un nœud.', - 'graph_contains_cycle' => 'Le graphe de l\'automatisation contient un cycle.', - 'only_failed_can_retry' => 'Seules les exécutions échouées peuvent être relancées.', - 'no_generated_post' => 'Aucune publication générée trouvée pour cette exécution.', - 'url_not_allowed' => 'L\'URL de la requête pointe vers une adresse privée ou inaccessible et a été bloquée.', - 'node_no_longer_exists' => 'Le nœud :node_id n\'existe plus dans l\'automatisation.', - 'no_trigger_connection' => 'Aucun nœud connecté au nœud déclencheur.', - 'fetch_rss_missing_url' => 'Il manque une URL de flux au nœud Récupérer RSS.', - 'fetch_rss_request_failed' => 'La requête du flux RSS a échoué.', - 'fetch_rss_malformed' => 'Le flux RSS est mal formé.', - 'http_missing_url' => 'Il manque une URL au nœud de requête HTTP.', - 'http_request_exception' => 'La requête HTTP a levé une exception.', - 'http_request_failed' => 'La requête HTTP a échoué.', - 'http_items_path_not_array' => 'Le chemin des éléments ne correspond pas à une liste.', - 'generate_image_format_required' => 'La génération IA ne crée que des images. Choisissez un format image (pas vidéo).', - ], -]; diff --git a/lang/fr/common.php b/lang/fr/common.php index f8d9c249..2fb056b1 100644 --- a/lang/fr/common.php +++ b/lang/fr/common.php @@ -6,8 +6,6 @@ 'back' => 'Retour', - 'beta' => 'Bêta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Cette action est irréversible.', 'type' => 'Saisissez', diff --git a/lang/fr/sidebar.php b/lang/fr/sidebar.php index 2933a9d9..a5eac546 100644 --- a/lang/fr/sidebar.php +++ b/lang/fr/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Autres', ], 'analytics' => 'Statistiques', - 'automations' => 'Automatisations', 'onboarding' => 'Premiers pas', 'onboarding_hint' => 'Terminer la configuration', 'posts' => [ diff --git a/lang/it/automations.php b/lang/it/automations.php deleted file mode 100644 index 12fc87d2..00000000 --- a/lang/it/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'L\'editor delle automazioni funziona meglio su uno schermo più grande. Aprilo su un desktop per creare il tuo flusso di lavoro.', - 'title' => 'Automazioni', - 'default_name' => 'Nuova automazione', - - 'actions' => [ - 'new' => 'Nuova automazione', - 'edit' => 'Modifica', - 'save' => 'Salva', - 'activate' => 'Attiva', - 'pause' => 'Metti in pausa', - 'delete' => 'Elimina', - 'retry' => 'Riprova', - 'guide' => 'Scopri come funziona', - ], - - 'tabs' => [ - 'build' => 'Crea', - 'variables' => 'Variabili', - 'test' => 'Test', - ], - - 'nav' => [ - 'workflow' => 'Flusso di lavoro', - 'invocations' => 'Esecuzioni', - 'metrics' => 'Metriche', - 'settings' => 'Impostazioni', - ], - - 'settings' => [ - 'general' => 'Generale', - 'general_description' => 'Rinomina questa automazione.', - 'name_label' => 'Nome', - 'name_saved' => 'Automazione rinominata.', - 'status_title' => 'Stato', - 'status_description' => 'Attiva per avviarla, o metti in pausa per fermarla.', - 'activated_at' => 'Attivata il :date', - 'paused_at' => 'Messa in pausa il :date', - 'created_at' => 'Creata il :date', - 'danger_title' => 'Zona pericolosa', - 'danger_description' => 'Azioni irreversibili.', - 'delete_title' => 'Elimina questa automazione', - 'delete_description' => 'Rimuove definitivamente l\'automazione e la sua cronologia di esecuzioni.', - ], - - 'status_run' => [ - 'pending' => 'In sospeso', - 'running' => 'In esecuzione', - 'waiting' => 'In attesa', - 'completed' => 'Completata', - 'failed' => 'Non riuscita', - 'cancelled' => 'Annullata', - ], - - 'node_type' => [ - 'trigger' => 'Trigger', - 'generate' => 'Genera contenuto', - 'delay' => 'Ritardo', - 'condition' => 'Condizione', - 'publish' => 'Pubblica', - 'end' => 'Fine', - 'fetch_rss' => 'Recupera RSS', - 'http_request' => 'Richiesta HTTP', - ], - - 'invocations' => [ - 'empty' => 'Ancora nessuna esecuzione.', - 'refresh' => 'Aggiorna', - 'search_placeholder' => 'Cerca per ID esecuzione…', - 'copied' => 'ID esecuzione copiato.', - 'loading' => 'Caricamento passaggi…', - 'no_steps' => 'Nessun passaggio registrato.', - 'load_error' => 'Impossibile caricare i passaggi.', - 'steps' => '{0}Nessun passaggio|{1}:count passaggio|[2,*]:count passaggi', - 'filter' => [ - 'all' => 'Tutti gli stati', - ], - 'columns' => [ - 'timestamp' => 'Data e ora', - 'run' => 'Esecuzione', - 'status' => 'Stato', - 'message' => 'Ultimo messaggio', - 'duration' => 'Durata', - ], - 'summary' => [ - 'completed' => 'Flusso di lavoro completato', - 'failed' => 'Flusso di lavoro non riuscito', - 'running' => 'Flusso di lavoro in esecuzione', - 'cancelled' => 'Flusso di lavoro annullato', - 'pending' => 'Flusso di lavoro in sospeso', - ], - ], - - 'metrics' => [ - 'overview' => 'Panoramica', - 'runs_over_time' => 'Esecuzioni nel tempo', - 'posts_by_platform' => 'Post per piattaforma', - 'no_posts' => 'Nessun post pubblicato in questo periodo.', - 'cards' => [ - 'runs' => 'Esecuzioni totali', - 'completed' => 'Completate', - 'failed' => 'Non riuscite', - 'in_progress' => 'In corso', - 'success_rate' => 'Tasso di successo', - 'avg_duration' => 'Durata media', - 'posts_created' => 'Post creati', - ], - 'legend' => [ - 'started' => 'Avviate', - 'completed' => 'Completate', - 'failed' => 'Non riuscite', - ], - ], - - 'categories' => [ - 'sources' => 'Fonti', - 'content' => 'Contenuto', - 'flow' => 'Flusso', - 'output' => 'Output', - ], - - 'variables' => [ - 'title' => 'Variabili del flusso di lavoro', - 'hint' => 'Valori riutilizzabili richiamabili ovunque con {{ variables.KEY }}. Memorizzati in forma cifrata.', - 'empty' => 'Ancora nessuna variabile.', - 'key' => 'Chiave', - 'value' => 'Valore', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Valore', - 'add' => 'Nuova variabile', - ], - - 'expr' => [ - 'trigger_event' => 'Nome dell\'evento trigger', - 'trigger_fired_at' => 'Quando il trigger è scattato', - 'trigger_post_id' => 'ID del post che ha attivato', - 'trigger_post_content' => 'Contenuto del post che ha attivato', - 'trigger_post_status' => 'Stato del post che ha attivato', - 'trigger_post_scheduled_at' => 'Quando il post è programmato', - 'trigger_post_published_at' => 'Quando il post è stato pubblicato', - 'fetched_title' => 'Titolo dell\'elemento recuperato', - 'fetched_link' => 'Link dell\'elemento recuperato', - 'fetched_date' => 'Data di pubblicazione dell\'elemento recuperato', - 'fetched_content' => 'Contenuto completo dell\'elemento recuperato', - 'fetched_description' => 'Riassunto dell\'elemento recuperato', - 'fetched_author' => 'Autore dell\'elemento recuperato', - 'fetched_image' => 'URL immagine dell\'elemento recuperato', - 'fetched_categories' => 'Categorie dell\'elemento recuperato', - 'fetched_enclosure' => 'Media dell\'elemento recuperato (audio/video/file)', - 'fetched_pubdate' => 'Data di pubblicazione dell\'elemento recuperato', - 'fetched_http' => 'Elemento HTTP recuperato (aggiungi un campo)', - 'generated_content' => 'Contenuto del post generato dall\'IA', - 'generated_post_url' => 'URL del post generato dall\'IA', - 'variable' => 'Variabile del flusso di lavoro', - 'now' => 'Data e ora correnti', - ], - - 'test' => [ - 'description' => 'Esegue l\'automazione dall\'inizio alla fine usando un payload di trigger sintetizzato. Utile per convalidare ogni nodo senza aspettare la pianificazione o il feed reali.', - 'starting' => 'Avvio dell\'esecuzione di test…', - 'in_progress' => 'In corso', - 'completed' => 'Completata', - 'failed' => 'Non riuscita', - 'waiting' => 'In attesa', - 'close' => 'Chiudi', - 'no_node_runs' => 'In attesa dell\'avvio del primo nodo…', - 'node_input' => 'Input', - 'node_output' => 'Output', - 'node_error' => 'Errore', - 'no_new_items' => 'Nessun nuovo elemento — nessun nodo a valle è stato eseguito.', - 'error_starting' => 'Impossibile avviare l\'esecuzione di test.', - 'with_real_data' => 'Con dati reali', - 'run' => 'Esegui test', - 'idle_hint' => 'Premi Esegui test per eseguire l\'automazione dall\'inizio alla fine.', - 'real_data_hint' => 'Questo test pubblicherà i post, farà avanzare i watermark di polling e attiverà effetti collaterali esterni.', - 'dry_badge' => 'Simulazione', - ], - - 'status' => [ - 'draft' => 'Bozza', - 'active' => 'Attiva', - 'paused' => 'In pausa', - ], - - 'index' => [ - 'empty_title' => 'Ancora nessuna automazione', - 'empty_description' => 'Crea la tua prima automazione per iniziare a pubblicare in automatico.', - 'columns' => [ - 'name' => 'Nome', - 'status' => 'Stato', - 'created' => 'Creata', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Impossibile attivare l\'automazione.', - 'pause_error_fallback' => 'Impossibile mettere in pausa l\'automazione.', - 'save_error_fallback' => 'Impossibile salvare l\'automazione.', - 'save_success' => 'Automazione salvata.', - 'empty_canvas_title' => 'Inizia a creare la tua automazione', - 'empty_canvas_description' => 'Trascina un nodo dal pannello di sinistra per iniziare.', - 'name_placeholder' => 'Automazione senza titolo', - ], - - 'nodes' => [ - 'trigger' => 'Trigger', - 'generate' => 'Genera', - 'delay' => 'Ritardo', - 'condition' => 'Condizione', - 'publish' => 'Pubblica', - 'end' => 'Fine', - 'end_summary' => 'Interrompe l\'automazione qui', - 'fetch_rss' => 'Recupera RSS', - 'http_request' => 'Richiesta HTTP', - 'handles' => [ - 'items' => 'ha elementi', - 'no_items' => 'nessun elemento', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Seleziona…', - 'invalid_json' => 'Questo non è ancora un JSON valido.', - 'expand_editor' => 'Espandi editor', - 'minimize_editor' => 'Riduci', - - 'trigger' => [ - 'type' => 'Tipo di trigger', - 'types' => [ - 'schedule' => 'Pianificazione', - 'post_published' => 'Quando un post viene pubblicato', - 'post_scheduled' => 'Quando un post viene programmato', - ], - 'post_published_hint' => 'Viene eseguito ogni volta che un post di questo workspace viene pubblicato. Il post pubblicato diventa disponibile in {{ trigger.post }} per i nodi a valle.', - 'post_scheduled_hint' => 'Viene eseguito ogni volta che un post di questo workspace viene programmato. Il post programmato è disponibile in {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Intervallo del trigger', - 'fields' => [ - 'minutes' => 'Minuti', - 'hours' => 'Ore', - 'days' => 'Giorni', - 'weeks' => 'Settimane', - 'months' => 'Mesi', - ], - 'minutes_interval' => 'Minuti tra un trigger e l\'altro', - 'hours_interval' => 'Ore tra un trigger e l\'altro', - 'days_interval' => 'Giorni tra un trigger e l\'altro', - 'hour' => 'Attiva all\'ora', - 'minute' => 'Attiva al minuto', - 'weekdays' => 'Attiva nei giorni della settimana', - 'day_of_month' => 'Giorno del mese', - 'weekday_names' => [ - 'sun' => 'Dom', - 'mon' => 'Lun', - 'tue' => 'Mar', - 'wed' => 'Mer', - 'thu' => 'Gio', - 'fri' => 'Ven', - 'sat' => 'Sab', - ], - 'summary' => [ - 'every_n_minutes' => 'Viene eseguito ogni minuto|Viene eseguito ogni :count minuti', - 'every_n_hours' => 'Viene eseguito ogni ora al minuto :minute|Viene eseguito ogni :count ore al minuto :minute', - 'every_n_days' => 'Viene eseguito ogni giorno alle :time|Viene eseguito ogni :count giorni alle :time', - 'weekly' => 'Viene eseguito ogni :days alle :time', - 'monthly' => 'Viene eseguito il giorno :day di ogni mese alle :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Account social', - 'social_accounts_empty' => 'Nessun account social collegato. Collegane uno prima.', - 'target_slide_count' => 'Slide da generare', - 'prompt_template' => 'Modello di prompt', - 'prompt_template_hint' => 'Digita {{ per inserire dati dai passaggi precedenti.', - 'image_count' => 'Immagini da generare', - 'image_count_hint' => '0 = post di solo testo (nessuna immagine). 1 = immagine singola. 2+ = carosello.', - 'use_brand_voice' => 'Usa il tono del brand', - 'use_brand_voice_hint' => 'Applica la descrizione e il tono del tuo brand. Disattiva per una curatela fedele di fonti di terze parti (notizie, RSS).', - 'use_brand_visuals' => 'Usa la grafica del brand', - 'use_brand_visuals_hint' => 'Orienta le immagini IA con i colori e l\'identità del tuo brand. Disattiva per immagini neutre guidate solo dall\'argomento del post.', - 'style' => 'Stile', - 'account_summary' => ':count account · :format|:count account · :format', - 'formats' => [ - 'single' => 'singola', - 'carousel' => 'carosello', - ], - ], - 'delay' => [ - 'duration' => 'Durata', - 'unit' => 'Unità', - 'units' => [ - 'minutes' => 'Minuti', - 'hours' => 'Ore', - 'days' => 'Giorni', - ], - ], - 'condition' => [ - 'field' => 'Campo', - 'operator' => 'Operatore', - 'operators' => [ - 'contains' => 'contiene', - 'not_contains' => 'non contiene', - 'equals' => 'uguale a', - 'not_equals' => 'diverso da', - 'matches' => 'corrisponde (regex)', - 'greater_than' => 'maggiore di', - 'less_than' => 'minore di', - ], - 'value' => 'Valore', - ], - 'publish' => [ - 'mode' => 'Modalità', - 'modes' => [ - 'now' => 'Pubblica ora', - 'scheduled' => 'Programma', - 'draft' => 'Salva come bozza', - ], - 'scheduled_offset' => 'Scostamento dal trigger (minuti)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Motivo (facoltativo)', - 'reason_placeholder' => 'es. Escluso dalla condizione', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL del feed', - 'feed_url_hint' => 'Alla prima esecuzione, il watermark viene impostato su "adesso" così gli elementi storici non inondano i nodi a valle. Le esecuzioni successive vedono solo gli elementi più recenti del polling precedente.', - 'inspect' => 'Ispeziona feed', - 'inspecting' => 'Ispezione in corso…', - 'inspect_hint' => 'Recupera un campione per scoprire i campi disponibili da usare nei nodi a valle.', - 'inspect_error' => 'Impossibile leggere questo feed. Controlla l\'URL e riprova.', - 'discovered_fields' => 'Campi disponibili', - 'discovered_empty' => 'Nessun campo trovato nell\'ultimo elemento.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Metodo', - 'auth_type' => 'Autenticazione', - 'auth' => [ - 'none' => 'Nessuna (pubblica)', - 'bearer' => 'Token Bearer', - 'basic' => 'Autenticazione di base', - 'api_key' => 'Header con chiave API', - ], - 'bearer_token' => 'Token Bearer', - 'basic_username' => 'Nome utente', - 'basic_password' => 'Password', - 'api_key_header' => 'Nome dell\'header', - 'api_key_value' => 'Chiave API', - 'body_template' => 'Modello del corpo (JSON)', - 'headers' => 'Header', - 'header_name' => 'Nome dell\'header', - 'header_value' => 'Valore', - 'add_header' => 'Aggiungi header', - 'polling_section' => 'Elenco e deduplicazione (facoltativo)', - 'polling_hint' => 'Quando la risposta è un elenco, ogni elemento esegue il flusso di lavoro separatamente. Un singolo oggetto viene eseguito una volta.', - 'items_path' => 'Percorso degli elementi', - 'items_path_hint' => 'Lascia vuoto se la risposta è già un array. Usa un percorso con punti (es. data.items) per un array annidato, oppure * per un oggetto con chiave per id.', - 'item_key_path' => 'Percorso della chiave elemento', - 'item_key_path_hint' => 'Percorso JSON a un id univoco (es. id). Gli elementi già visti vengono saltati, così un feed senza date inoltra comunque solo le voci nuove.', - 'item_date_path' => 'Percorso della data elemento', - 'item_date_path_hint' => 'Percorso JSON al timestamp dell\'elemento (es. published_at). Preferito rispetto al percorso della chiave quando disponibile. Il primo polling registra la baseline e non inoltra nulla, così un feed esistente non inonda mai il primo giorno.', - ], - ], - - 'delete' => [ - 'title' => 'Elimina automazione', - 'description' => 'Vuoi davvero eliminare questa automazione? Verranno rimossi anche tutte le esecuzioni e gli elementi trigger. Questa azione non può essere annullata.', - 'confirm' => 'Elimina', - 'cancel' => 'Annulla', - ], - - 'flash' => [ - 'deleted' => 'Automazione eliminata con successo!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Nessun account social attivo configurato per questa automazione.', - 'must_have_one_trigger' => 'L\'automazione deve avere esattamente un nodo trigger.', - 'trigger_must_be_connected' => 'Il nodo trigger deve essere collegato ad almeno un nodo.', - 'graph_contains_cycle' => 'Il grafo dell\'automazione contiene un ciclo.', - 'only_failed_can_retry' => 'Solo le esecuzioni non riuscite possono essere ritentate.', - 'no_generated_post' => 'Nessun post generato trovato nell\'esecuzione.', - 'url_not_allowed' => 'L\'URL della richiesta punta a un indirizzo privato o irraggiungibile ed è stato bloccato.', - 'node_no_longer_exists' => 'Il nodo :node_id non esiste più nell\'automazione.', - 'no_trigger_connection' => 'Nessun nodo collegato al nodo Trigger.', - 'fetch_rss_missing_url' => 'Al nodo Recupera RSS manca un URL del feed.', - 'fetch_rss_request_failed' => 'La richiesta al feed RSS non è riuscita.', - 'fetch_rss_malformed' => 'Il feed RSS è malformato.', - 'http_missing_url' => 'Al nodo richiesta HTTP manca un URL.', - 'http_request_exception' => 'La richiesta HTTP ha generato un\'eccezione.', - 'http_request_failed' => 'La richiesta HTTP non è riuscita.', - 'http_items_path_not_array' => 'Il percorso degli elementi non ha restituito un elenco.', - 'generate_image_format_required' => 'La generazione AI crea solo immagini. Scegli un formato immagine (non video).', - ], -]; diff --git a/lang/it/common.php b/lang/it/common.php index 8f8d7503..6415ac1e 100644 --- a/lang/it/common.php +++ b/lang/it/common.php @@ -6,8 +6,6 @@ 'back' => 'Indietro', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Questa azione non può essere annullata.', 'type' => 'Digita', diff --git a/lang/it/sidebar.php b/lang/it/sidebar.php index 8d0de920..55c1e47a 100644 --- a/lang/it/sidebar.php +++ b/lang/it/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Altro', ], 'analytics' => 'Statistiche', - 'automations' => 'Automazioni', 'onboarding' => 'Primi passi', 'onboarding_hint' => 'Completa la configurazione', 'posts' => [ diff --git a/lang/ja/automations.php b/lang/ja/automations.php deleted file mode 100644 index 003d4807..00000000 --- a/lang/ja/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'オートメーションエディタは大きな画面での利用に最適です。ワークフローを作成するにはデスクトップで開いてください。', - 'title' => 'オートメーション', - 'default_name' => '新しいオートメーション', - - 'actions' => [ - 'new' => '新しいオートメーション', - 'edit' => '編集', - 'save' => '保存', - 'activate' => '有効化', - 'pause' => '一時停止', - 'delete' => '削除', - 'retry' => '再試行', - 'guide' => '仕組みを学ぶ', - ], - - 'tabs' => [ - 'build' => '作成', - 'variables' => '変数', - 'test' => 'テスト', - ], - - 'nav' => [ - 'workflow' => 'ワークフロー', - 'invocations' => '実行履歴', - 'metrics' => 'メトリクス', - 'settings' => '設定', - ], - - 'settings' => [ - 'general' => '一般', - 'general_description' => 'このオートメーションの名前を変更します。', - 'name_label' => '名前', - 'name_saved' => 'オートメーションの名前を変更しました。', - 'status_title' => 'ステータス', - 'status_description' => '有効化すると実行を開始し、一時停止すると停止します。', - 'activated_at' => ':date に有効化', - 'paused_at' => ':date に一時停止', - 'created_at' => ':date に作成', - 'danger_title' => '危険な操作', - 'danger_description' => '取り消せない操作です。', - 'delete_title' => 'このオートメーションを削除', - 'delete_description' => 'オートメーションとその実行履歴を完全に削除します。', - ], - - 'status_run' => [ - 'pending' => '保留中', - 'running' => '実行中', - 'waiting' => '待機中', - 'completed' => '完了', - 'failed' => '失敗', - 'cancelled' => 'キャンセル済み', - ], - - 'node_type' => [ - 'trigger' => 'トリガー', - 'generate' => 'コンテンツを生成', - 'delay' => '遅延', - 'condition' => '条件', - 'publish' => '公開', - 'end' => '終了', - 'fetch_rss' => 'RSS を取得', - 'http_request' => 'HTTP リクエスト', - ], - - 'invocations' => [ - 'empty' => 'まだ実行履歴がありません。', - 'refresh' => '更新', - 'search_placeholder' => '実行 ID で検索…', - 'copied' => '実行 ID をコピーしました。', - 'loading' => 'ステップを読み込み中…', - 'no_steps' => '記録されたステップがありません。', - 'load_error' => 'ステップを読み込めませんでした。', - 'steps' => '{0}ステップなし|{1}:count 件のステップ|[2,*]:count 件のステップ', - 'filter' => [ - 'all' => 'すべてのステータス', - ], - 'columns' => [ - 'timestamp' => 'タイムスタンプ', - 'run' => '実行', - 'status' => 'ステータス', - 'message' => '最新メッセージ', - 'duration' => '所要時間', - ], - 'summary' => [ - 'completed' => 'ワークフローが完了しました', - 'failed' => 'ワークフローが失敗しました', - 'running' => 'ワークフローを実行中', - 'cancelled' => 'ワークフローをキャンセルしました', - 'pending' => 'ワークフローは保留中です', - ], - ], - - 'metrics' => [ - 'overview' => '概要', - 'runs_over_time' => '実行数の推移', - 'posts_by_platform' => 'プラットフォーム別の投稿', - 'no_posts' => 'この期間に公開された投稿はありません。', - 'cards' => [ - 'runs' => '総実行数', - 'completed' => '完了', - 'failed' => '失敗', - 'in_progress' => '進行中', - 'success_rate' => '成功率', - 'avg_duration' => '平均所要時間', - 'posts_created' => '作成された投稿数', - ], - 'legend' => [ - 'started' => '開始', - 'completed' => '完了', - 'failed' => '失敗', - ], - ], - - 'categories' => [ - 'sources' => 'ソース', - 'content' => 'コンテンツ', - 'flow' => 'フロー', - 'output' => '出力', - ], - - 'variables' => [ - 'title' => 'ワークフロー変数', - 'hint' => '{{ variables.KEY }} でどこからでも参照できる再利用可能な値です。暗号化して保存されます。', - 'empty' => 'まだ変数がありません。', - 'key' => 'キー', - 'value' => '値', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => '値', - 'add' => '新しい変数', - ], - - 'expr' => [ - 'trigger_event' => 'トリガーイベント名', - 'trigger_fired_at' => 'トリガーが発火した時刻', - 'trigger_post_id' => 'トリガーとなった投稿 ID', - 'trigger_post_content' => 'トリガーとなった投稿の内容', - 'trigger_post_status' => 'トリガーとなった投稿のステータス', - 'trigger_post_scheduled_at' => '投稿の予約日時', - 'trigger_post_published_at' => '投稿が公開された日時', - 'fetched_title' => '取得したアイテムのタイトル', - 'fetched_link' => '取得したアイテムのリンク', - 'fetched_date' => '取得したアイテムの公開日', - 'fetched_content' => '取得したアイテムの全文', - 'fetched_description' => '取得したアイテムの概要', - 'fetched_author' => '取得したアイテムの作成者', - 'fetched_image' => '取得したアイテムの画像 URL', - 'fetched_categories' => '取得したアイテムのカテゴリ', - 'fetched_enclosure' => '取得したアイテムのメディア(音声・動画・ファイル)', - 'fetched_pubdate' => '取得したアイテムの公開日', - 'fetched_http' => '取得した HTTP アイテム(フィールドを追加)', - 'generated_content' => 'AI が生成した投稿の内容', - 'generated_post_url' => 'AI が生成した投稿の URL', - 'variable' => 'ワークフロー変数', - 'now' => '現在の日時', - ], - - 'test' => [ - 'description' => '合成したトリガーペイロードを使って、オートメーションを最初から最後まで実行します。実際のスケジュールやフィードを待たずに各ノードを検証するのに便利です。', - 'starting' => 'テスト実行を開始しています…', - 'in_progress' => '進行中', - 'completed' => '完了', - 'failed' => '失敗', - 'waiting' => '待機中', - 'close' => '閉じる', - 'no_node_runs' => '最初のノードの開始を待っています…', - 'node_input' => '入力', - 'node_output' => '出力', - 'node_error' => 'エラー', - 'no_new_items' => '新しいアイテムがありません — 後続の処理は実行されませんでした。', - 'error_starting' => 'テスト実行を開始できませんでした。', - 'with_real_data' => '実データを使用', - 'run' => 'テストを実行', - 'idle_hint' => '「テストを実行」を押すと、オートメーションを最初から最後まで実行します。', - 'real_data_hint' => 'このテストは投稿を公開し、ポーリングの基準を進め、外部への副作用を発生させます。', - 'dry_badge' => 'ドライラン', - ], - - 'status' => [ - 'draft' => '下書き', - 'active' => '有効', - 'paused' => '一時停止中', - ], - - 'index' => [ - 'empty_title' => 'まだオートメーションがありません', - 'empty_description' => '最初のオートメーションを作成して、自動で投稿を公開しましょう。', - 'columns' => [ - 'name' => '名前', - 'status' => 'ステータス', - 'created' => '作成日', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'オートメーションを有効化できませんでした。', - 'pause_error_fallback' => 'オートメーションを一時停止できませんでした。', - 'save_error_fallback' => 'オートメーションを保存できませんでした。', - 'save_success' => 'オートメーションを保存しました。', - 'empty_canvas_title' => 'オートメーションの作成を始めましょう', - 'empty_canvas_description' => '左側のパネルからノードをドラッグして始めましょう。', - 'name_placeholder' => '無題のオートメーション', - ], - - 'nodes' => [ - 'trigger' => 'トリガー', - 'generate' => '生成', - 'delay' => '遅延', - 'condition' => '条件', - 'publish' => '公開', - 'end' => '終了', - 'end_summary' => 'ここでオートメーションを停止します', - 'fetch_rss' => 'RSS を取得', - 'http_request' => 'HTTP リクエスト', - 'handles' => [ - 'items' => 'アイテムあり', - 'no_items' => 'アイテムなし', - ], - ], - - 'config' => [ - 'select_placeholder' => '選択…', - 'invalid_json' => 'まだ有効な JSON ではありません。', - 'expand_editor' => 'エディターを拡大', - 'minimize_editor' => '最小化', - - 'trigger' => [ - 'type' => 'トリガーの種類', - 'types' => [ - 'schedule' => 'スケジュール', - 'post_published' => '投稿が公開されたとき', - 'post_scheduled' => '投稿が予約されたとき', - ], - 'post_published_hint' => 'このワークスペースで投稿が公開されるたびに実行されます。公開された投稿は {{ trigger.post }} として後続のノードで利用できます。', - 'post_scheduled_hint' => 'このワークスペースで投稿が予約されるたびに実行されます。予約された投稿は {{ trigger.post }} で利用できます。', - - 'schedule' => [ - 'field' => 'トリガー間隔', - 'fields' => [ - 'minutes' => '分', - 'hours' => '時間', - 'days' => '日', - 'weeks' => '週', - 'months' => '月', - ], - 'minutes_interval' => 'トリガー間の分数', - 'hours_interval' => 'トリガー間の時間数', - 'days_interval' => 'トリガー間の日数', - 'hour' => '実行する時', - 'minute' => '実行する分', - 'weekdays' => '実行する曜日', - 'day_of_month' => '実行する日', - 'weekday_names' => [ - 'sun' => '日', - 'mon' => '月', - 'tue' => '火', - 'wed' => '水', - 'thu' => '木', - 'fri' => '金', - 'sat' => '土', - ], - 'summary' => [ - 'every_n_minutes' => ':count 分ごとに実行|:count 分ごとに実行', - 'every_n_hours' => ':count 時間ごとに :minute 分に実行|:count 時間ごとに :minute 分に実行', - 'every_n_days' => ':count 日ごとに :time に実行|:count 日ごとに :time に実行', - 'weekly' => '毎週 :days の :time に実行', - 'monthly' => '毎月 :day 日の :time に実行', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'ソーシャルアカウント', - 'social_accounts_empty' => '接続済みのソーシャルアカウントがありません。まず 1 つ接続してください。', - 'target_slide_count' => '生成するスライド数', - 'prompt_template' => 'プロンプトテンプレート', - 'prompt_template_hint' => '{{ と入力すると、前のステップのデータを挿入できます。', - 'image_count' => '生成する画像数', - 'image_count_hint' => '0 = テキストのみの投稿(画像なし)。1 = 単一画像。2 以上 = カルーセル。', - 'use_brand_voice' => 'ブランドボイスを使用', - 'use_brand_voice_hint' => 'ブランドの説明とボイスを適用します。第三者ソース(ニュース、RSS)を忠実にキュレーションする場合はオフにしてください。', - 'use_brand_visuals' => 'ブランドビジュアルを使用', - 'use_brand_visuals_hint' => 'AI 画像をブランドカラーとアイデンティティで方向づけます。投稿のトピックのみに基づく中立的な画像にする場合はオフにしてください。', - 'style' => 'スタイル', - 'account_summary' => ':count 件のアカウント · :format|:count 件のアカウント · :format', - 'formats' => [ - 'single' => '単一', - 'carousel' => 'カルーセル', - ], - ], - 'delay' => [ - 'duration' => '期間', - 'unit' => '単位', - 'units' => [ - 'minutes' => '分', - 'hours' => '時間', - 'days' => '日', - ], - ], - 'condition' => [ - 'field' => 'フィールド', - 'operator' => '演算子', - 'operators' => [ - 'contains' => '含む', - 'not_contains' => '含まない', - 'equals' => '等しい', - 'not_equals' => '等しくない', - 'matches' => '一致(正規表現)', - 'greater_than' => 'より大きい', - 'less_than' => 'より小さい', - ], - 'value' => '値', - ], - 'publish' => [ - 'mode' => 'モード', - 'modes' => [ - 'now' => '今すぐ公開', - 'scheduled' => '予約', - 'draft' => '下書きとして保存', - ], - 'scheduled_offset' => 'トリガーからのオフセット(分)', - 'offset_summary' => ':mode · +:offset 分', - ], - 'end' => [ - 'reason' => '理由(任意)', - 'reason_placeholder' => '例: 条件により除外', - ], - 'fetch_rss' => [ - 'feed_url' => 'フィード URL', - 'feed_url_hint' => '初回実行時、基準は「現在」に設定され、過去のアイテムが後続ノードに大量に流れ込まないようにします。以降の実行では、前回のポーリングより新しいアイテムのみが対象になります。', - 'inspect' => 'フィードを確認', - 'inspecting' => '確認中…', - 'inspect_hint' => 'サンプルを取得して、後続のノードで使用できるフィールドを確認します。', - 'inspect_error' => 'このフィードを読み込めませんでした。URL を確認してもう一度お試しください。', - 'discovered_fields' => '利用可能なフィールド', - 'discovered_empty' => '最新のアイテムにフィールドが見つかりませんでした。', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'メソッド', - 'auth_type' => '認証', - 'auth' => [ - 'none' => 'なし(公開)', - 'bearer' => 'ベアラートークン', - 'basic' => 'Basic 認証', - 'api_key' => 'API キーヘッダー', - ], - 'bearer_token' => 'ベアラートークン', - 'basic_username' => 'ユーザー名', - 'basic_password' => 'パスワード', - 'api_key_header' => 'ヘッダー名', - 'api_key_value' => 'API キー', - 'body_template' => 'ボディテンプレート(JSON)', - 'headers' => 'ヘッダー', - 'header_name' => 'ヘッダー名', - 'header_value' => '値', - 'add_header' => 'ヘッダーを追加', - 'polling_section' => 'リストと重複排除(任意)', - 'polling_hint' => 'レスポンスがリストの場合、各アイテムがワークフローを個別に実行します。単一のオブジェクトの場合は 1 回だけ実行されます。', - 'items_path' => 'アイテムのパス', - 'items_path_hint' => 'レスポンスがすでに配列の場合は空欄にしてください。ネストされた配列にはドット区切りのパス(例: data.items)を、id をキーとするオブジェクトには * を使用します。', - 'item_key_path' => 'アイテムキーのパス', - 'item_key_path_hint' => '一意の id への JSON パス(例: id)。すでに処理済みのアイテムはスキップされるため、日付のないフィードでも新しいエントリのみが転送されます。', - 'item_date_path' => 'アイテム日付のパス', - 'item_date_path_hint' => 'アイテムのタイムスタンプへの JSON パス(例: published_at)。利用可能な場合はキーのパスより優先されます。初回のポーリングでは基準を記録し何も転送しないため、既存のフィードでも初日から大量に流れ込むことはありません。', - ], - ], - - 'delete' => [ - 'title' => 'オートメーションを削除', - 'description' => 'このオートメーションを削除してもよろしいですか?すべての実行とトリガーアイテムも削除されます。この操作は取り消せません。', - 'confirm' => '削除', - 'cancel' => 'キャンセル', - ], - - 'flash' => [ - 'deleted' => 'オートメーションを正常に削除しました!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'このオートメーションに有効なソーシャルアカウントが設定されていません。', - 'must_have_one_trigger' => 'オートメーションにはトリガーノードが 1 つだけ必要です。', - 'trigger_must_be_connected' => 'トリガーノードは少なくとも 1 つのノードに接続されている必要があります。', - 'graph_contains_cycle' => 'オートメーションのグラフに循環が含まれています。', - 'only_failed_can_retry' => '失敗した実行のみ再試行できます。', - 'no_generated_post' => '実行で生成された投稿が見つかりません。', - 'url_not_allowed' => 'リクエスト URL がプライベートまたは到達不能なアドレスを指しているためブロックされました。', - 'node_no_longer_exists' => 'ノード :node_id はオートメーションに存在しなくなりました。', - 'no_trigger_connection' => 'トリガーノードに接続されたノードがありません。', - 'fetch_rss_missing_url' => 'RSS 取得ノードにフィード URL がありません。', - 'fetch_rss_request_failed' => 'RSS フィードのリクエストが失敗しました。', - 'fetch_rss_malformed' => 'RSS フィードの形式が不正です。', - 'http_missing_url' => 'HTTP リクエストノードに URL がありません。', - 'http_request_exception' => 'HTTP リクエストで例外が発生しました。', - 'http_request_failed' => 'HTTP リクエストが失敗しました。', - 'http_items_path_not_array' => 'アイテムのパスがリストとして解決されませんでした。', - 'generate_image_format_required' => 'AI生成は画像のみ作成します。画像フォーマットを選んでください(動画不可)。', - ], -]; diff --git a/lang/ja/common.php b/lang/ja/common.php index 37140022..94cab782 100644 --- a/lang/ja/common.php +++ b/lang/ja/common.php @@ -6,8 +6,6 @@ 'back' => '戻る', - 'beta' => 'ベータ', - 'confirm_modal' => [ 'cannot_be_undone' => 'この操作は取り消せません。', 'type' => '入力', diff --git a/lang/ja/sidebar.php b/lang/ja/sidebar.php index 4e406298..a333f21a 100644 --- a/lang/ja/sidebar.php +++ b/lang/ja/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'その他', ], 'analytics' => 'アナリティクス', - 'automations' => 'オートメーション', 'onboarding' => 'はじめに', 'onboarding_hint' => 'セットアップを完了', 'posts' => [ diff --git a/lang/ko/automations.php b/lang/ko/automations.php deleted file mode 100644 index 426839c7..00000000 --- a/lang/ko/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - '자동화 편집기는 큰 화면에서 가장 잘 작동합니다. 워크플로를 만들려면 데스크톱에서 열어주세요.', - 'title' => '자동화', - 'default_name' => '새 자동화', - - 'actions' => [ - 'new' => '새 자동화', - 'edit' => '편집', - 'save' => '저장', - 'activate' => '활성화', - 'pause' => '일시정지', - 'delete' => '삭제', - 'retry' => '재시도', - 'guide' => '작동 방식 알아보기', - ], - - 'tabs' => [ - 'build' => '빌드', - 'variables' => '변수', - 'test' => '테스트', - ], - - 'nav' => [ - 'workflow' => '워크플로', - 'invocations' => '실행 기록', - 'metrics' => '지표', - 'settings' => '설정', - ], - - 'settings' => [ - 'general' => '일반', - 'general_description' => '이 자동화의 이름을 변경하세요.', - 'name_label' => '이름', - 'name_saved' => '자동화 이름이 변경되었습니다.', - 'status_title' => '상태', - 'status_description' => '활성화하여 실행을 시작하거나 일시정지하여 중지하세요.', - 'activated_at' => ':date에 활성화됨', - 'paused_at' => ':date에 일시정지됨', - 'created_at' => ':date에 생성됨', - 'danger_title' => '위험 구역', - 'danger_description' => '되돌릴 수 없는 작업입니다.', - 'delete_title' => '이 자동화 삭제', - 'delete_description' => '자동화와 실행 기록을 영구적으로 제거합니다.', - ], - - 'status_run' => [ - 'pending' => '대기 중', - 'running' => '실행 중', - 'waiting' => '기다리는 중', - 'completed' => '완료됨', - 'failed' => '실패', - 'cancelled' => '취소됨', - ], - - 'node_type' => [ - 'trigger' => '트리거', - 'generate' => '콘텐츠 생성', - 'delay' => '지연', - 'condition' => '조건', - 'publish' => '게시', - 'end' => '종료', - 'fetch_rss' => 'RSS 가져오기', - 'http_request' => 'HTTP 요청', - ], - - 'invocations' => [ - 'empty' => '아직 실행 기록이 없습니다.', - 'refresh' => '새로고침', - 'search_placeholder' => '실행 ID로 검색…', - 'copied' => '실행 ID가 복사되었습니다.', - 'loading' => '단계를 불러오는 중…', - 'no_steps' => '기록된 단계가 없습니다.', - 'load_error' => '단계를 불러올 수 없습니다.', - 'steps' => '{0}단계 없음|{1}:count개 단계|[2,*]:count개 단계', - 'filter' => [ - 'all' => '모든 상태', - ], - 'columns' => [ - 'timestamp' => '시각', - 'run' => '실행', - 'status' => '상태', - 'message' => '마지막 메시지', - 'duration' => '소요 시간', - ], - 'summary' => [ - 'completed' => '워크플로 완료됨', - 'failed' => '워크플로 실패', - 'running' => '워크플로 실행 중', - 'cancelled' => '워크플로 취소됨', - 'pending' => '워크플로 대기 중', - ], - ], - - 'metrics' => [ - 'overview' => '개요', - 'runs_over_time' => '시간별 실행 횟수', - 'posts_by_platform' => '플랫폼별 게시물', - 'no_posts' => '이 기간에 게시된 게시물이 없습니다.', - 'cards' => [ - 'runs' => '총 실행 횟수', - 'completed' => '완료됨', - 'failed' => '실패', - 'in_progress' => '진행 중', - 'success_rate' => '성공률', - 'avg_duration' => '평균 소요 시간', - 'posts_created' => '생성된 게시물', - ], - 'legend' => [ - 'started' => '시작됨', - 'completed' => '완료됨', - 'failed' => '실패', - ], - ], - - 'categories' => [ - 'sources' => '소스', - 'content' => '콘텐츠', - 'flow' => '흐름', - 'output' => '출력', - ], - - 'variables' => [ - 'title' => '워크플로 변수', - 'hint' => '{{ variables.KEY }}로 어디서나 참조할 수 있는 재사용 가능한 값입니다. 암호화되어 저장됩니다.', - 'empty' => '아직 변수가 없습니다.', - 'key' => '키', - 'value' => '값', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => '값', - 'add' => '새 변수', - ], - - 'expr' => [ - 'trigger_event' => '트리거 이벤트 이름', - 'trigger_fired_at' => '트리거가 발생한 시각', - 'trigger_post_id' => '트리거 게시물 ID', - 'trigger_post_content' => '트리거 게시물 내용', - 'trigger_post_status' => '트리거 게시물 상태', - 'trigger_post_scheduled_at' => '게시물 예약 시각', - 'trigger_post_published_at' => '게시물 게시 시각', - 'fetched_title' => '가져온 항목 제목', - 'fetched_link' => '가져온 항목 링크', - 'fetched_date' => '가져온 항목 게시일', - 'fetched_content' => '가져온 항목 전체 내용', - 'fetched_description' => '가져온 항목 요약', - 'fetched_author' => '가져온 항목 작성자', - 'fetched_image' => '가져온 항목 이미지 URL', - 'fetched_categories' => '가져온 항목 카테고리', - 'fetched_enclosure' => '가져온 항목 미디어 (오디오/동영상/파일)', - 'fetched_pubdate' => '가져온 항목 게시일', - 'fetched_http' => '가져온 HTTP 항목 (필드 추가)', - 'generated_content' => 'AI가 생성한 게시물 내용', - 'generated_post_url' => 'AI가 생성한 게시물 URL', - 'variable' => '워크플로 변수', - 'now' => '현재 날짜 및 시간', - ], - - 'test' => [ - 'description' => '합성된 트리거 페이로드를 사용하여 자동화를 처음부터 끝까지 실행합니다. 실제 일정이나 피드를 기다리지 않고 각 노드를 검증하는 데 유용합니다.', - 'starting' => '테스트 실행을 시작하는 중…', - 'in_progress' => '진행 중', - 'completed' => '완료됨', - 'failed' => '실패', - 'waiting' => '기다리는 중', - 'close' => '닫기', - 'no_node_runs' => '첫 번째 노드가 시작되기를 기다리는 중…', - 'node_input' => '입력', - 'node_output' => '출력', - 'node_error' => '오류', - 'no_new_items' => '새 항목 없음 — 이후 노드가 실행되지 않았습니다.', - 'error_starting' => '테스트 실행을 시작할 수 없습니다.', - 'with_real_data' => '실제 데이터로', - 'run' => '테스트 실행', - 'idle_hint' => '테스트 실행을 눌러 자동화를 처음부터 끝까지 실행하세요.', - 'real_data_hint' => '이 테스트는 게시물을 실제로 게시하고, 폴링 워터마크를 진행시키며, 외부 사이드 이펙트를 트리거합니다.', - 'dry_badge' => '드라이 런', - ], - - 'status' => [ - 'draft' => '초안', - 'active' => '활성', - 'paused' => '일시정지됨', - ], - - 'index' => [ - 'empty_title' => '아직 자동화가 없습니다', - 'empty_description' => '첫 자동화를 만들어 자동으로 게시를 시작하세요.', - 'columns' => [ - 'name' => '이름', - 'status' => '상태', - 'created' => '생성일', - ], - ], - - 'form' => [ - 'activate_error_fallback' => '자동화를 활성화할 수 없습니다.', - 'pause_error_fallback' => '자동화를 일시정지할 수 없습니다.', - 'save_error_fallback' => '자동화를 저장할 수 없습니다.', - 'save_success' => '자동화가 저장되었습니다.', - 'empty_canvas_title' => '자동화 구성 시작하기', - 'empty_canvas_description' => '시작하려면 왼쪽 패널에서 노드를 끌어다 놓으세요.', - 'name_placeholder' => '제목 없는 자동화', - ], - - 'nodes' => [ - 'trigger' => '트리거', - 'generate' => '생성', - 'delay' => '지연', - 'condition' => '조건', - 'publish' => '게시', - 'end' => '종료', - 'end_summary' => '여기서 자동화를 중지합니다', - 'fetch_rss' => 'RSS 가져오기', - 'http_request' => 'HTTP 요청', - 'handles' => [ - 'items' => '항목 있음', - 'no_items' => '항목 없음', - ], - ], - - 'config' => [ - 'select_placeholder' => '선택…', - 'invalid_json' => '아직 유효한 JSON이 아닙니다.', - 'expand_editor' => '편집기 확장', - 'minimize_editor' => '최소화', - - 'trigger' => [ - 'type' => '트리거 유형', - 'types' => [ - 'schedule' => '일정', - 'post_published' => '게시물이 게시될 때', - 'post_scheduled' => '게시물이 예약될 때', - ], - 'post_published_hint' => '이 워크스페이스의 게시물이 게시될 때마다 실행됩니다. 게시된 게시물은 이후 노드에서 {{ trigger.post }}로 사용할 수 있습니다.', - 'post_scheduled_hint' => '이 워크스페이스의 게시물이 예약될 때마다 실행됩니다. 예약된 게시물은 {{ trigger.post }}로 사용할 수 있습니다.', - - 'schedule' => [ - 'field' => '트리거 간격', - 'fields' => [ - 'minutes' => '분', - 'hours' => '시간', - 'days' => '일', - 'weeks' => '주', - 'months' => '개월', - ], - 'minutes_interval' => '트리거 간격 (분)', - 'hours_interval' => '트리거 간격 (시간)', - 'days_interval' => '트리거 간격 (일)', - 'hour' => '트리거 시각 (시)', - 'minute' => '트리거 시각 (분)', - 'weekdays' => '실행할 요일', - 'day_of_month' => '매월 며칠', - 'weekday_names' => [ - 'sun' => '일', - 'mon' => '월', - 'tue' => '화', - 'wed' => '수', - 'thu' => '목', - 'fri' => '금', - 'sat' => '토', - ], - 'summary' => [ - 'every_n_minutes' => '매 분마다 실행|:count분마다 실행', - 'every_n_hours' => '매시간 :minute분에 실행|:count시간마다 :minute분에 실행', - 'every_n_days' => '매일 :time에 실행|:count일마다 :time에 실행', - 'weekly' => '매주 :days :time에 실행', - 'monthly' => '매월 :day일 :time에 실행', - ], - ], - ], - 'generate' => [ - 'social_accounts' => '소셜 계정', - 'social_accounts_empty' => '연결된 소셜 계정이 없습니다. 먼저 하나를 연결하세요.', - 'target_slide_count' => '생성할 슬라이드 수', - 'prompt_template' => '프롬프트 템플릿', - 'prompt_template_hint' => '{{를 입력하여 이전 단계의 데이터를 삽입하세요.', - 'image_count' => '생성할 이미지 수', - 'image_count_hint' => '0 = 텍스트 전용 게시물 (이미지 없음). 1 = 단일 이미지. 2+ = 캐러셀.', - 'use_brand_voice' => '브랜드 보이스 사용', - 'use_brand_voice_hint' => '브랜드 설명과 보이스를 적용합니다. 서드파티 소스(뉴스, RSS)를 충실히 큐레이션하려면 끄세요.', - 'use_brand_visuals' => '브랜드 비주얼 사용', - 'use_brand_visuals_hint' => '브랜드 색상과 아이덴티티로 AI 이미지를 조정합니다. 게시물 주제만으로 중립적인 이미지를 만들려면 끄세요.', - 'style' => '스타일', - 'account_summary' => ':count개 계정 · :format|:count개 계정 · :format', - 'formats' => [ - 'single' => '단일', - 'carousel' => '캐러셀', - ], - ], - 'delay' => [ - 'duration' => '기간', - 'unit' => '단위', - 'units' => [ - 'minutes' => '분', - 'hours' => '시간', - 'days' => '일', - ], - ], - 'condition' => [ - 'field' => '필드', - 'operator' => '연산자', - 'operators' => [ - 'contains' => '포함', - 'not_contains' => '포함하지 않음', - 'equals' => '같음', - 'not_equals' => '같지 않음', - 'matches' => '일치 (정규식)', - 'greater_than' => '초과', - 'less_than' => '미만', - ], - 'value' => '값', - ], - 'publish' => [ - 'mode' => '모드', - 'modes' => [ - 'now' => '지금 게시', - 'scheduled' => '예약', - 'draft' => '초안으로 저장', - ], - 'scheduled_offset' => '트리거 기준 오프셋 (분)', - 'offset_summary' => ':mode · +:offset분', - ], - 'end' => [ - 'reason' => '사유 (선택)', - 'reason_placeholder' => '예: 조건에 의해 필터링됨', - ], - 'fetch_rss' => [ - 'feed_url' => '피드 URL', - 'feed_url_hint' => '첫 실행 시 워터마크가 "현재"로 설정되어 과거 항목이 이후 노드로 쏟아지지 않습니다. 이후 실행에서는 이전 폴링보다 새로운 항목만 표시됩니다.', - 'inspect' => '피드 검사', - 'inspecting' => '검사 중…', - 'inspect_hint' => '샘플을 가져와 이후 노드에서 사용할 수 있는 필드를 확인하세요.', - 'inspect_error' => '이 피드를 읽을 수 없습니다. URL을 확인한 후 다시 시도하세요.', - 'discovered_fields' => '사용 가능한 필드', - 'discovered_empty' => '최신 항목에서 필드를 찾을 수 없습니다.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => '메서드', - 'auth_type' => '인증', - 'auth' => [ - 'none' => '없음 (공개)', - 'bearer' => 'Bearer 토큰', - 'basic' => 'Basic 인증', - 'api_key' => 'API 키 헤더', - ], - 'bearer_token' => 'Bearer 토큰', - 'basic_username' => '사용자 이름', - 'basic_password' => '비밀번호', - 'api_key_header' => '헤더 이름', - 'api_key_value' => 'API 키', - 'body_template' => '본문 템플릿 (JSON)', - 'headers' => '헤더', - 'header_name' => '헤더 이름', - 'header_value' => '값', - 'add_header' => '헤더 추가', - 'polling_section' => '목록 및 중복 제거 (선택)', - 'polling_hint' => '응답이 목록인 경우 각 항목이 워크플로를 개별적으로 실행합니다. 단일 객체는 한 번 실행됩니다.', - 'items_path' => '항목 경로', - 'items_path_hint' => '응답이 이미 배열이면 비워 두세요. 중첩 배열의 경우 점 표기 경로(예: data.items)를, id로 키가 지정된 객체의 경우 *를 사용하세요.', - 'item_key_path' => '항목 키 경로', - 'item_key_path_hint' => '고유 id의 JSON 경로(예: id). 이미 본 항목은 건너뛰므로 날짜가 없는 피드도 새 항목만 전달됩니다.', - 'item_date_path' => '항목 날짜 경로', - 'item_date_path_hint' => '항목 타임스탬프의 JSON 경로(예: published_at). 가능한 경우 키 경로보다 우선합니다. 첫 폴링은 기준선을 기록하고 아무것도 전달하지 않으므로 기존 피드가 첫날 쏟아지지 않습니다.', - ], - ], - - 'delete' => [ - 'title' => '자동화 삭제', - 'description' => '이 자동화를 삭제하시겠습니까? 모든 실행 및 트리거 항목도 함께 제거됩니다. 이 작업은 되돌릴 수 없습니다.', - 'confirm' => '삭제', - 'cancel' => '취소', - ], - - 'flash' => [ - 'deleted' => '자동화가 성공적으로 삭제되었습니다!', - ], - - 'errors' => [ - 'no_active_social_accounts' => '이 자동화에 구성된 활성 소셜 계정이 없습니다.', - 'must_have_one_trigger' => '자동화에는 정확히 하나의 트리거 노드가 있어야 합니다.', - 'trigger_must_be_connected' => '트리거 노드는 최소 하나의 노드에 연결되어야 합니다.', - 'graph_contains_cycle' => '자동화 그래프에 순환이 포함되어 있습니다.', - 'only_failed_can_retry' => '실패한 실행만 재시도할 수 있습니다.', - 'no_generated_post' => '실행에서 생성된 게시물을 찾을 수 없습니다.', - 'url_not_allowed' => '요청 URL이 비공개이거나 접근할 수 없는 주소를 가리켜 차단되었습니다.', - 'node_no_longer_exists' => '노드 :node_id이(가) 자동화에 더 이상 존재하지 않습니다.', - 'no_trigger_connection' => '트리거 노드에 연결된 노드가 없습니다.', - 'fetch_rss_missing_url' => 'RSS 가져오기 노드에 피드 URL이 없습니다.', - 'fetch_rss_request_failed' => 'RSS 피드 요청이 실패했습니다.', - 'fetch_rss_malformed' => 'RSS 피드 형식이 잘못되었습니다.', - 'http_missing_url' => 'HTTP 요청 노드에 URL이 없습니다.', - 'http_request_exception' => 'HTTP 요청에서 예외가 발생했습니다.', - 'http_request_failed' => 'HTTP 요청이 실패했습니다.', - 'http_items_path_not_array' => '항목 경로가 목록으로 해석되지 않았습니다.', - 'generate_image_format_required' => 'AI 생성은 이미지만 만듭니다. 이미지 형식을 선택하세요(동영상 불가).', - ], -]; diff --git a/lang/ko/common.php b/lang/ko/common.php index 8d418371..3ae65be8 100644 --- a/lang/ko/common.php +++ b/lang/ko/common.php @@ -6,8 +6,6 @@ 'back' => '뒤로', - 'beta' => '베타', - 'confirm_modal' => [ 'cannot_be_undone' => '이 작업은 되돌릴 수 없습니다.', 'type' => '입력', diff --git a/lang/ko/sidebar.php b/lang/ko/sidebar.php index a54a72b1..2fcb2cbc 100644 --- a/lang/ko/sidebar.php +++ b/lang/ko/sidebar.php @@ -26,7 +26,6 @@ 'others' => '기타', ], 'analytics' => '분석', - 'automations' => '자동화', 'onboarding' => '시작하기', 'onboarding_hint' => '설정 마치기', 'posts' => [ diff --git a/lang/nl/automations.php b/lang/nl/automations.php deleted file mode 100644 index 90de073a..00000000 --- a/lang/nl/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'De automatiserings-editor werkt het best op een groter scherm. Open het op een desktop om je workflow te bouwen.', - 'title' => 'Automatiseringen', - 'default_name' => 'Nieuwe automatisering', - - 'actions' => [ - 'new' => 'Nieuwe automatisering', - 'edit' => 'Bewerken', - 'save' => 'Opslaan', - 'activate' => 'Activeren', - 'pause' => 'Pauzeren', - 'delete' => 'Verwijderen', - 'retry' => 'Opnieuw proberen', - 'guide' => 'Ontdek hoe het werkt', - ], - - 'tabs' => [ - 'build' => 'Bouwen', - 'variables' => 'Variabelen', - 'test' => 'Testen', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Uitvoeringen', - 'metrics' => 'Statistieken', - 'settings' => 'Instellingen', - ], - - 'settings' => [ - 'general' => 'Algemeen', - 'general_description' => 'Wijzig de naam van deze automatisering.', - 'name_label' => 'Naam', - 'name_saved' => 'Automatisering hernoemd.', - 'status_title' => 'Status', - 'status_description' => 'Activeer om te starten, of pauzeer om te stoppen.', - 'activated_at' => 'Geactiveerd :date', - 'paused_at' => 'Gepauzeerd :date', - 'created_at' => 'Aangemaakt :date', - 'danger_title' => 'Gevarenzone', - 'danger_description' => 'Onomkeerbare acties.', - 'delete_title' => 'Deze automatisering verwijderen', - 'delete_description' => 'Verwijdert de automatisering en de uitvoeringsgeschiedenis permanent.', - ], - - 'status_run' => [ - 'pending' => 'In afwachting', - 'running' => 'Bezig', - 'waiting' => 'Wachten', - 'completed' => 'Voltooid', - 'failed' => 'Mislukt', - 'cancelled' => 'Geannuleerd', - ], - - 'node_type' => [ - 'trigger' => 'Trigger', - 'generate' => 'Content genereren', - 'delay' => 'Vertraging', - 'condition' => 'Voorwaarde', - 'publish' => 'Publiceren', - 'end' => 'Einde', - 'fetch_rss' => 'RSS ophalen', - 'http_request' => 'HTTP-verzoek', - ], - - 'invocations' => [ - 'empty' => 'Nog geen uitvoeringen.', - 'refresh' => 'Vernieuwen', - 'search_placeholder' => 'Zoeken op uitvoerings-ID…', - 'copied' => 'Uitvoerings-ID gekopieerd.', - 'loading' => 'Stappen laden…', - 'no_steps' => 'Geen stappen geregistreerd.', - 'load_error' => 'Kon de stappen niet laden.', - 'steps' => '{0}Geen stappen|{1}:count stap|[2,*]:count stappen', - 'filter' => [ - 'all' => 'Alle statussen', - ], - 'columns' => [ - 'timestamp' => 'Tijdstip', - 'run' => 'Uitvoering', - 'status' => 'Status', - 'message' => 'Laatste bericht', - 'duration' => 'Duur', - ], - 'summary' => [ - 'completed' => 'Workflow voltooid', - 'failed' => 'Workflow mislukt', - 'running' => 'Workflow bezig', - 'cancelled' => 'Workflow geannuleerd', - 'pending' => 'Workflow in afwachting', - ], - ], - - 'metrics' => [ - 'overview' => 'Overzicht', - 'runs_over_time' => 'Uitvoeringen in de tijd', - 'posts_by_platform' => 'Posts per platform', - 'no_posts' => 'Geen posts gepubliceerd in deze periode.', - 'cards' => [ - 'runs' => 'Totaal uitvoeringen', - 'completed' => 'Voltooid', - 'failed' => 'Mislukt', - 'in_progress' => 'Bezig', - 'success_rate' => 'Slagingspercentage', - 'avg_duration' => 'Gem. duur', - 'posts_created' => 'Posts aangemaakt', - ], - 'legend' => [ - 'started' => 'Gestart', - 'completed' => 'Voltooid', - 'failed' => 'Mislukt', - ], - ], - - 'categories' => [ - 'sources' => 'Bronnen', - 'content' => 'Content', - 'flow' => 'Flow', - 'output' => 'Uitvoer', - ], - - 'variables' => [ - 'title' => 'Workflowvariabelen', - 'hint' => 'Herbruikbare waarden die je overal kunt gebruiken met {{ variables.KEY }}. Versleuteld opgeslagen.', - 'empty' => 'Nog geen variabelen.', - 'key' => 'Sleutel', - 'value' => 'Waarde', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Waarde', - 'add' => 'Nieuwe variabele', - ], - - 'expr' => [ - 'trigger_event' => 'Naam van triggergebeurtenis', - 'trigger_fired_at' => 'Wanneer de trigger afging', - 'trigger_post_id' => 'ID van triggerende post', - 'trigger_post_content' => 'Inhoud van triggerende post', - 'trigger_post_status' => 'Status van triggerende post', - 'trigger_post_scheduled_at' => 'Wanneer de post is gepland', - 'trigger_post_published_at' => 'Wanneer de post is gepubliceerd', - 'fetched_title' => 'Titel van opgehaald item', - 'fetched_link' => 'Link van opgehaald item', - 'fetched_date' => 'Publicatiedatum van opgehaald item', - 'fetched_content' => 'Volledige inhoud van opgehaald item', - 'fetched_description' => 'Samenvatting van opgehaald item', - 'fetched_author' => 'Auteur van opgehaald item', - 'fetched_image' => 'Afbeeldings-URL van opgehaald item', - 'fetched_categories' => 'Categorieën van opgehaald item', - 'fetched_enclosure' => 'Media van opgehaald item (audio/video/bestand)', - 'fetched_pubdate' => 'Publicatiedatum van opgehaald item', - 'fetched_http' => 'Opgehaald HTTP-item (voeg een veld toe)', - 'generated_content' => 'AI-gegenereerde postinhoud', - 'generated_post_url' => 'AI-gegenereerde post-URL', - 'variable' => 'Workflowvariabele', - 'now' => 'Huidige datum en tijd', - ], - - 'test' => [ - 'description' => 'Voert de automatisering van begin tot eind uit met een gesynthetiseerde triggerpayload. Handig om elke node te valideren zonder te wachten op de echte planning of feed.', - 'starting' => 'Testuitvoering starten…', - 'in_progress' => 'Bezig', - 'completed' => 'Voltooid', - 'failed' => 'Mislukt', - 'waiting' => 'Wachten', - 'close' => 'Sluiten', - 'no_node_runs' => 'Wachten tot de eerste node start…', - 'node_input' => 'Invoer', - 'node_output' => 'Uitvoer', - 'node_error' => 'Fout', - 'no_new_items' => 'Geen nieuwe items — er is niets stroomafwaarts uitgevoerd.', - 'error_starting' => 'Kon de testuitvoering niet starten.', - 'with_real_data' => 'Met echte gegevens', - 'run' => 'Test uitvoeren', - 'idle_hint' => 'Klik op Test uitvoeren om de automatisering van begin tot eind uit te voeren.', - 'real_data_hint' => 'Deze test publiceert posts, verplaatst polling-watermerken en activeert externe neveneffecten.', - 'dry_badge' => 'Testuitvoering', - ], - - 'status' => [ - 'draft' => 'Concept', - 'active' => 'Actief', - 'paused' => 'Gepauzeerd', - ], - - 'index' => [ - 'empty_title' => 'Nog geen automatiseringen', - 'empty_description' => 'Maak je eerste automatisering aan om automatisch te gaan publiceren.', - 'columns' => [ - 'name' => 'Naam', - 'status' => 'Status', - 'created' => 'Aangemaakt', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Kon de automatisering niet activeren.', - 'pause_error_fallback' => 'Kon de automatisering niet pauzeren.', - 'save_error_fallback' => 'Kon de automatisering niet opslaan.', - 'save_success' => 'Automatisering opgeslagen.', - 'empty_canvas_title' => 'Begin met het bouwen van je automatisering', - 'empty_canvas_description' => 'Sleep een node uit het linkerpaneel om te beginnen.', - 'name_placeholder' => 'Naamloze automatisering', - ], - - 'nodes' => [ - 'trigger' => 'Trigger', - 'generate' => 'Genereren', - 'delay' => 'Vertraging', - 'condition' => 'Voorwaarde', - 'publish' => 'Publiceren', - 'end' => 'Einde', - 'end_summary' => 'Stopt de automatisering hier', - 'fetch_rss' => 'RSS ophalen', - 'http_request' => 'HTTP-verzoek', - 'handles' => [ - 'items' => 'heeft items', - 'no_items' => 'geen items', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Selecteren…', - 'invalid_json' => 'Dit is nog geen geldige JSON.', - 'expand_editor' => 'Editor uitvouwen', - 'minimize_editor' => 'Minimaliseren', - - 'trigger' => [ - 'type' => 'Triggertype', - 'types' => [ - 'schedule' => 'Planning', - 'post_published' => 'Wanneer een post wordt gepubliceerd', - 'post_scheduled' => 'Wanneer een post wordt gepland', - ], - 'post_published_hint' => 'Wordt uitgevoerd telkens wanneer een post in deze workspace wordt gepubliceerd. De gepubliceerde post is beschikbaar op {{ trigger.post }} voor stroomafwaartse nodes.', - 'post_scheduled_hint' => 'Wordt uitgevoerd telkens wanneer een post in deze workspace wordt gepland. De geplande post is beschikbaar op {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Triggerinterval', - 'fields' => [ - 'minutes' => 'Minuten', - 'hours' => 'Uren', - 'days' => 'Dagen', - 'weeks' => 'Weken', - 'months' => 'Maanden', - ], - 'minutes_interval' => 'Minuten tussen triggers', - 'hours_interval' => 'Uren tussen triggers', - 'days_interval' => 'Dagen tussen triggers', - 'hour' => 'Triggeren op uur', - 'minute' => 'Triggeren op minuut', - 'weekdays' => 'Triggeren op weekdagen', - 'day_of_month' => 'Dag van de maand', - 'weekday_names' => [ - 'sun' => 'Zo', - 'mon' => 'Ma', - 'tue' => 'Di', - 'wed' => 'Wo', - 'thu' => 'Do', - 'fri' => 'Vr', - 'sat' => 'Za', - ], - 'summary' => [ - 'every_n_minutes' => 'Wordt elke minuut uitgevoerd|Wordt elke :count minuten uitgevoerd', - 'every_n_hours' => 'Wordt elk uur uitgevoerd op minuut :minute|Wordt elke :count uur uitgevoerd op minuut :minute', - 'every_n_days' => 'Wordt elke dag uitgevoerd om :time|Wordt elke :count dagen uitgevoerd om :time', - 'weekly' => 'Wordt elke :days uitgevoerd om :time', - 'monthly' => 'Wordt op dag :day van elke maand uitgevoerd om :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Social accounts', - 'social_accounts_empty' => 'Geen gekoppelde social accounts. Koppel er eerst een.', - 'target_slide_count' => 'Te genereren slides', - 'prompt_template' => 'Prompt-sjabloon', - 'prompt_template_hint' => 'Typ {{ om gegevens uit eerdere stappen in te voegen.', - 'image_count' => 'Te genereren afbeeldingen', - 'image_count_hint' => '0 = tekstpost (geen afbeelding). 1 = enkele afbeelding. 2+ = carrousel.', - 'use_brand_voice' => 'Merkstem gebruiken', - 'use_brand_voice_hint' => 'Pas je merkomschrijving en -stem toe. Schakel uit voor getrouwe curatie van externe bronnen (nieuws, RSS).', - 'use_brand_visuals' => 'Merkvisuals gebruiken', - 'use_brand_visuals_hint' => 'Stuur AI-afbeeldingen met je merkkleuren en -identiteit. Schakel uit voor neutrale beelden die alleen door het onderwerp worden bepaald.', - 'style' => 'Stijl', - 'account_summary' => ':count account · :format|:count accounts · :format', - 'formats' => [ - 'single' => 'enkel', - 'carousel' => 'carrousel', - ], - ], - 'delay' => [ - 'duration' => 'Duur', - 'unit' => 'Eenheid', - 'units' => [ - 'minutes' => 'Minuten', - 'hours' => 'Uren', - 'days' => 'Dagen', - ], - ], - 'condition' => [ - 'field' => 'Veld', - 'operator' => 'Operator', - 'operators' => [ - 'contains' => 'bevat', - 'not_contains' => 'bevat niet', - 'equals' => 'is gelijk aan', - 'not_equals' => 'is niet gelijk aan', - 'matches' => 'komt overeen met (regex)', - 'greater_than' => 'groter dan', - 'less_than' => 'kleiner dan', - ], - 'value' => 'Waarde', - ], - 'publish' => [ - 'mode' => 'Modus', - 'modes' => [ - 'now' => 'Nu publiceren', - 'scheduled' => 'Plannen', - 'draft' => 'Opslaan als concept', - ], - 'scheduled_offset' => 'Verschuiving vanaf trigger (minuten)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Reden (optioneel)', - 'reason_placeholder' => 'bijv. Uitgefilterd door voorwaarde', - ], - 'fetch_rss' => [ - 'feed_url' => 'Feed-URL', - 'feed_url_hint' => 'Bij de eerste uitvoering wordt het watermerk op "nu" gezet, zodat historische items stroomafwaartse nodes niet overspoelen. Volgende uitvoeringen zien alleen items die nieuwer zijn dan de vorige poll.', - 'inspect' => 'Feed inspecteren', - 'inspecting' => 'Inspecteren…', - 'inspect_hint' => 'Haal een voorbeeld op om de beschikbare velden te ontdekken voor gebruik in stroomafwaartse nodes.', - 'inspect_error' => 'Kon deze feed niet lezen. Controleer de URL en probeer het opnieuw.', - 'discovered_fields' => 'Beschikbare velden', - 'discovered_empty' => 'Geen velden gevonden in het laatste item.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Methode', - 'auth_type' => 'Authenticatie', - 'auth' => [ - 'none' => 'Geen (openbaar)', - 'bearer' => 'Bearer-token', - 'basic' => 'Basic auth', - 'api_key' => 'API-sleutelheader', - ], - 'bearer_token' => 'Bearer-token', - 'basic_username' => 'Gebruikersnaam', - 'basic_password' => 'Wachtwoord', - 'api_key_header' => 'Headernaam', - 'api_key_value' => 'API-sleutel', - 'body_template' => 'Body-sjabloon (JSON)', - 'headers' => 'Headers', - 'header_name' => 'Headernaam', - 'header_value' => 'Waarde', - 'add_header' => 'Header toevoegen', - 'polling_section' => 'Lijst en ontdubbeling (optioneel)', - 'polling_hint' => 'Wanneer het antwoord een lijst is, voert elk item de workflow apart uit. Een enkel object wordt één keer uitgevoerd.', - 'items_path' => 'Itempad', - 'items_path_hint' => 'Laat leeg als het antwoord al een array is. Gebruik een pad met punten (bijv. data.items) voor een geneste array, of * voor een object met id als sleutel.', - 'item_key_path' => 'Itemsleutelpad', - 'item_key_path_hint' => 'JSON-pad naar een uniek id (bijv. id). Al eerder geziene items worden overgeslagen, zodat een feed zonder datums toch alleen nieuwe items doorstuurt.', - 'item_date_path' => 'Itemdatumpad', - 'item_date_path_hint' => 'JSON-pad naar de tijdstempel van het item (bijv. published_at). Heeft de voorkeur boven het sleutelpad indien beschikbaar. De eerste poll legt de basislijn vast en stuurt niets door, zodat een bestaande feed op dag één nooit overspoelt.', - ], - ], - - 'delete' => [ - 'title' => 'Automatisering verwijderen', - 'description' => 'Weet je zeker dat je deze automatisering wilt verwijderen? Alle uitvoeringen en triggeritems worden ook verwijderd. Deze actie kan niet ongedaan worden gemaakt.', - 'confirm' => 'Verwijderen', - 'cancel' => 'Annuleren', - ], - - 'flash' => [ - 'deleted' => 'Automatisering succesvol verwijderd!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Geen actieve social accounts geconfigureerd voor deze automatisering.', - 'must_have_one_trigger' => 'Een automatisering moet precies één triggernode hebben.', - 'trigger_must_be_connected' => 'De triggernode moet met ten minste één node verbonden zijn.', - 'graph_contains_cycle' => 'De automatiseringsgraaf bevat een cyclus.', - 'only_failed_can_retry' => 'Alleen mislukte uitvoeringen kunnen opnieuw worden geprobeerd.', - 'no_generated_post' => 'Geen gegenereerde post gevonden bij de uitvoering.', - 'url_not_allowed' => 'De verzoek-URL verwijst naar een privé of onbereikbaar adres en is geblokkeerd.', - 'node_no_longer_exists' => 'Node :node_id bestaat niet meer in de automatisering.', - 'no_trigger_connection' => 'Geen node verbonden met de triggernode.', - 'fetch_rss_missing_url' => 'De RSS-ophaalnode mist een feed-URL.', - 'fetch_rss_request_failed' => 'Het verzoek voor de RSS-feed is mislukt.', - 'fetch_rss_malformed' => 'De RSS-feed is onjuist opgemaakt.', - 'http_missing_url' => 'De HTTP-verzoeknode mist een URL.', - 'http_request_exception' => 'Het HTTP-verzoek gaf een uitzondering.', - 'http_request_failed' => 'Het HTTP-verzoek is mislukt.', - 'http_items_path_not_array' => 'Het itempad verwees niet naar een lijst.', - 'generate_image_format_required' => 'AI-generatie maakt alleen afbeeldingen. Kies een afbeeldingsformaat (geen video).', - ], -]; diff --git a/lang/nl/common.php b/lang/nl/common.php index 5dbc4b61..db23bc10 100644 --- a/lang/nl/common.php +++ b/lang/nl/common.php @@ -6,8 +6,6 @@ 'back' => 'Terug', - 'beta' => 'Bèta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Dit kan niet ongedaan worden gemaakt.', 'type' => 'Typ', diff --git a/lang/nl/sidebar.php b/lang/nl/sidebar.php index 65c28fe5..adce46db 100644 --- a/lang/nl/sidebar.php +++ b/lang/nl/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Overige', ], 'analytics' => 'Statistieken', - 'automations' => 'Automatiseringen', 'onboarding' => 'Aan de slag', 'onboarding_hint' => 'Setup afronden', 'posts' => [ diff --git a/lang/pl/automations.php b/lang/pl/automations.php deleted file mode 100644 index c968ff74..00000000 --- a/lang/pl/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Edytor automatyzacji działa najlepiej na większym ekranie. Otwórz go na komputerze, aby zbudować swój workflow.', - 'title' => 'Automatyzacje', - 'default_name' => 'Nowa automatyzacja', - - 'actions' => [ - 'new' => 'Nowa automatyzacja', - 'edit' => 'Edytuj', - 'save' => 'Zapisz', - 'activate' => 'Aktywuj', - 'pause' => 'Wstrzymaj', - 'delete' => 'Usuń', - 'retry' => 'Ponów', - 'guide' => 'Dowiedz się, jak to działa', - ], - - 'tabs' => [ - 'build' => 'Kreator', - 'variables' => 'Zmienne', - 'test' => 'Test', - ], - - 'nav' => [ - 'workflow' => 'Przepływ pracy', - 'invocations' => 'Wywołania', - 'metrics' => 'Metryki', - 'settings' => 'Ustawienia', - ], - - 'settings' => [ - 'general' => 'Ogólne', - 'general_description' => 'Zmień nazwę tej automatyzacji.', - 'name_label' => 'Nazwa', - 'name_saved' => 'Zmieniono nazwę automatyzacji.', - 'status_title' => 'Status', - 'status_description' => 'Aktywuj, aby ją uruchomić, lub wstrzymaj, aby zatrzymać.', - 'activated_at' => 'Aktywowano :date', - 'paused_at' => 'Wstrzymano :date', - 'created_at' => 'Utworzono :date', - 'danger_title' => 'Strefa zagrożenia', - 'danger_description' => 'Nieodwracalne działania.', - 'delete_title' => 'Usuń tę automatyzację', - 'delete_description' => 'Trwale usuwa automatyzację i jej historię uruchomień.', - ], - - 'status_run' => [ - 'pending' => 'Oczekuje', - 'running' => 'W trakcie', - 'waiting' => 'Oczekiwanie', - 'completed' => 'Zakończone', - 'failed' => 'Nieudane', - 'cancelled' => 'Anulowane', - ], - - 'node_type' => [ - 'trigger' => 'Wyzwalacz', - 'generate' => 'Generuj treść', - 'delay' => 'Opóźnienie', - 'condition' => 'Warunek', - 'publish' => 'Publikuj', - 'end' => 'Koniec', - 'fetch_rss' => 'Pobierz RSS', - 'http_request' => 'Żądanie HTTP', - ], - - 'invocations' => [ - 'empty' => 'Brak wywołań.', - 'refresh' => 'Odśwież', - 'search_placeholder' => 'Szukaj po identyfikatorze uruchomienia…', - 'copied' => 'Skopiowano identyfikator uruchomienia.', - 'loading' => 'Wczytywanie kroków…', - 'no_steps' => 'Nie zarejestrowano żadnych kroków.', - 'load_error' => 'Nie udało się wczytać kroków.', - 'steps' => '{0}Brak kroków|{1}:count krok|[2,4]:count kroki|[5,*]:count kroków', - 'filter' => [ - 'all' => 'Wszystkie statusy', - ], - 'columns' => [ - 'timestamp' => 'Znacznik czasu', - 'run' => 'Uruchomienie', - 'status' => 'Status', - 'message' => 'Ostatnia wiadomość', - 'duration' => 'Czas trwania', - ], - 'summary' => [ - 'completed' => 'Przepływ zakończony', - 'failed' => 'Przepływ nieudany', - 'running' => 'Przepływ w trakcie', - 'cancelled' => 'Przepływ anulowany', - 'pending' => 'Przepływ oczekuje', - ], - ], - - 'metrics' => [ - 'overview' => 'Przegląd', - 'runs_over_time' => 'Uruchomienia w czasie', - 'posts_by_platform' => 'Posty według platformy', - 'no_posts' => 'W tym okresie nie opublikowano żadnych postów.', - 'cards' => [ - 'runs' => 'Łączna liczba uruchomień', - 'completed' => 'Zakończone', - 'failed' => 'Nieudane', - 'in_progress' => 'W trakcie', - 'success_rate' => 'Wskaźnik powodzenia', - 'avg_duration' => 'Śr. czas trwania', - 'posts_created' => 'Utworzone posty', - ], - 'legend' => [ - 'started' => 'Rozpoczęte', - 'completed' => 'Zakończone', - 'failed' => 'Nieudane', - ], - ], - - 'categories' => [ - 'sources' => 'Źródła', - 'content' => 'Treść', - 'flow' => 'Przepływ', - 'output' => 'Wyjście', - ], - - 'variables' => [ - 'title' => 'Zmienne przepływu pracy', - 'hint' => 'Wielokrotnego użytku wartości, do których odwołujesz się w dowolnym miejscu za pomocą {{ variables.KEY }}. Przechowywane w formie zaszyfrowanej.', - 'empty' => 'Brak zmiennych.', - 'key' => 'Klucz', - 'value' => 'Wartość', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Wartość', - 'add' => 'Nowa zmienna', - ], - - 'expr' => [ - 'trigger_event' => 'Nazwa zdarzenia wyzwalacza', - 'trigger_fired_at' => 'Kiedy wyzwalacz się uruchomił', - 'trigger_post_id' => 'Identyfikator posta wyzwalającego', - 'trigger_post_content' => 'Treść posta wyzwalającego', - 'trigger_post_status' => 'Status posta wyzwalającego', - 'trigger_post_scheduled_at' => 'Kiedy post jest zaplanowany', - 'trigger_post_published_at' => 'Kiedy post został opublikowany', - 'fetched_title' => 'Tytuł pobranego elementu', - 'fetched_link' => 'Link pobranego elementu', - 'fetched_date' => 'Data publikacji pobranego elementu', - 'fetched_content' => 'Pełna treść pobranego elementu', - 'fetched_description' => 'Podsumowanie pobranego elementu', - 'fetched_author' => 'Autor pobranego elementu', - 'fetched_image' => 'Adres URL obrazu pobranego elementu', - 'fetched_categories' => 'Kategorie pobranego elementu', - 'fetched_enclosure' => 'Multimedia pobranego elementu (audio/wideo/plik)', - 'fetched_pubdate' => 'Data publikacji pobranego elementu', - 'fetched_http' => 'Pobrany element HTTP (dodaj pole)', - 'generated_content' => 'Treść posta wygenerowana przez AI', - 'generated_post_url' => 'Adres URL posta wygenerowanego przez AI', - 'variable' => 'Zmienna przepływu pracy', - 'now' => 'Bieżąca data i godzina', - ], - - 'test' => [ - 'description' => 'Uruchamia automatyzację od początku do końca, używając wygenerowanego ładunku wyzwalacza. Przydatne do sprawdzania każdego węzła bez czekania na rzeczywisty harmonogram lub kanał.', - 'starting' => 'Rozpoczynanie przebiegu testowego…', - 'in_progress' => 'W trakcie', - 'completed' => 'Zakończone', - 'failed' => 'Nieudane', - 'waiting' => 'Oczekiwanie', - 'close' => 'Zamknij', - 'no_node_runs' => 'Oczekiwanie na uruchomienie pierwszego węzła…', - 'node_input' => 'Wejście', - 'node_output' => 'Wyjście', - 'node_error' => 'Błąd', - 'no_new_items' => 'Brak nowych elementów — nic dalej się nie uruchomiło.', - 'error_starting' => 'Nie udało się rozpocząć przebiegu testowego.', - 'with_real_data' => 'Z rzeczywistymi danymi', - 'run' => 'Uruchom test', - 'idle_hint' => 'Kliknij Uruchom test, aby wykonać automatyzację od początku do końca.', - 'real_data_hint' => 'Ten test opublikuje posty, przesunie znaczniki odpytywania i wywoła zewnętrzne skutki uboczne.', - 'dry_badge' => 'Przebieg próbny', - ], - - 'status' => [ - 'draft' => 'Szkic', - 'active' => 'Aktywna', - 'paused' => 'Wstrzymana', - ], - - 'index' => [ - 'empty_title' => 'Brak automatyzacji', - 'empty_description' => 'Utwórz swoją pierwszą automatyzację, aby publikować na autopilocie.', - 'columns' => [ - 'name' => 'Nazwa', - 'status' => 'Status', - 'created' => 'Utworzono', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Nie udało się aktywować automatyzacji.', - 'pause_error_fallback' => 'Nie udało się wstrzymać automatyzacji.', - 'save_error_fallback' => 'Nie udało się zapisać automatyzacji.', - 'save_success' => 'Automatyzacja zapisana.', - 'empty_canvas_title' => 'Zacznij budować swoją automatyzację', - 'empty_canvas_description' => 'Przeciągnij węzeł z lewego panelu, aby rozpocząć.', - 'name_placeholder' => 'Automatyzacja bez tytułu', - ], - - 'nodes' => [ - 'trigger' => 'Wyzwalacz', - 'generate' => 'Generuj', - 'delay' => 'Opóźnienie', - 'condition' => 'Warunek', - 'publish' => 'Publikuj', - 'end' => 'Koniec', - 'end_summary' => 'Zatrzymuje automatyzację w tym miejscu', - 'fetch_rss' => 'Pobierz RSS', - 'http_request' => 'Żądanie HTTP', - 'handles' => [ - 'items' => 'ma elementy', - 'no_items' => 'brak elementów', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Wybierz…', - 'invalid_json' => 'To jeszcze nie jest prawidłowy JSON.', - 'expand_editor' => 'Rozwiń edytor', - 'minimize_editor' => 'Zminimalizuj', - - 'trigger' => [ - 'type' => 'Typ wyzwalacza', - 'types' => [ - 'schedule' => 'Harmonogram', - 'post_published' => 'Gdy post zostaje opublikowany', - 'post_scheduled' => 'Gdy post zostaje zaplanowany', - ], - 'post_published_hint' => 'Uruchamia się za każdym razem, gdy dowolny post w tej przestrzeni roboczej zostanie opublikowany. Opublikowany post staje się dostępny pod {{ trigger.post }} dla kolejnych węzłów.', - 'post_scheduled_hint' => 'Uruchamia się za każdym razem, gdy dowolny post w tej przestrzeni roboczej zostanie zaplanowany. Zaplanowany post jest dostępny pod {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Interwał wyzwalania', - 'fields' => [ - 'minutes' => 'Minuty', - 'hours' => 'Godziny', - 'days' => 'Dni', - 'weeks' => 'Tygodnie', - 'months' => 'Miesiące', - ], - 'minutes_interval' => 'Minuty między wyzwoleniami', - 'hours_interval' => 'Godziny między wyzwoleniami', - 'days_interval' => 'Dni między wyzwoleniami', - 'hour' => 'Wyzwól o godzinie', - 'minute' => 'Wyzwól w minucie', - 'weekdays' => 'Wyzwól w dni tygodnia', - 'day_of_month' => 'Dzień miesiąca', - 'weekday_names' => [ - 'sun' => 'Nd', - 'mon' => 'Pn', - 'tue' => 'Wt', - 'wed' => 'Śr', - 'thu' => 'Cz', - 'fri' => 'Pt', - 'sat' => 'Sb', - ], - 'summary' => [ - 'every_n_minutes' => 'Uruchamia się co minutę|Uruchamia się co :count minuty|Uruchamia się co :count minut', - 'every_n_hours' => 'Uruchamia się co godzinę o minucie :minute|Uruchamia się co :count godziny o minucie :minute|Uruchamia się co :count godzin o minucie :minute', - 'every_n_days' => 'Uruchamia się codziennie o :time|Uruchamia się co :count dni o :time|Uruchamia się co :count dni o :time', - 'weekly' => 'Uruchamia się w :days o :time', - 'monthly' => 'Uruchamia się :day. dnia każdego miesiąca o :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Konta społecznościowe', - 'social_accounts_empty' => 'Brak połączonych kont społecznościowych. Najpierw połącz jedno.', - 'target_slide_count' => 'Slajdy do wygenerowania', - 'prompt_template' => 'Szablon promptu', - 'prompt_template_hint' => 'Wpisz {{, aby wstawić dane z wcześniejszych kroków.', - 'image_count' => 'Obrazy do wygenerowania', - 'image_count_hint' => '0 = post tylko tekstowy (bez obrazu). 1 = pojedynczy obraz. 2+ = karuzela.', - 'use_brand_voice' => 'Użyj głosu marki', - 'use_brand_voice_hint' => 'Zastosuj opis i głos swojej marki. Wyłącz dla wiernego kuratorowania źródeł zewnętrznych (wiadomości, RSS).', - 'use_brand_visuals' => 'Użyj wizualnej identyfikacji marki', - 'use_brand_visuals_hint' => 'Kieruj obrazami AI za pomocą kolorów i tożsamości swojej marki. Wyłącz, aby uzyskać neutralne obrazy oparte wyłącznie na temacie posta.', - 'style' => 'Styl', - 'account_summary' => ':count konto · :format|:count konta · :format|:count kont · :format', - 'formats' => [ - 'single' => 'pojedynczy', - 'carousel' => 'karuzela', - ], - ], - 'delay' => [ - 'duration' => 'Czas trwania', - 'unit' => 'Jednostka', - 'units' => [ - 'minutes' => 'Minuty', - 'hours' => 'Godziny', - 'days' => 'Dni', - ], - ], - 'condition' => [ - 'field' => 'Pole', - 'operator' => 'Operator', - 'operators' => [ - 'contains' => 'zawiera', - 'not_contains' => 'nie zawiera', - 'equals' => 'równa się', - 'not_equals' => 'nie równa się', - 'matches' => 'pasuje (regex)', - 'greater_than' => 'większe niż', - 'less_than' => 'mniejsze niż', - ], - 'value' => 'Wartość', - ], - 'publish' => [ - 'mode' => 'Tryb', - 'modes' => [ - 'now' => 'Opublikuj teraz', - 'scheduled' => 'Zaplanuj', - 'draft' => 'Zapisz jako szkic', - ], - 'scheduled_offset' => 'Przesunięcie od wyzwolenia (minuty)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Powód (opcjonalnie)', - 'reason_placeholder' => 'np. Odfiltrowane przez warunek', - ], - 'fetch_rss' => [ - 'feed_url' => 'Adres URL kanału', - 'feed_url_hint' => 'Przy pierwszym uruchomieniu znacznik jest ustawiany na „teraz”, aby elementy historyczne nie zalały kolejnych węzłów. Kolejne uruchomienia widzą tylko elementy nowsze niż poprzednie odpytanie.', - 'inspect' => 'Zbadaj kanał', - 'inspecting' => 'Badanie…', - 'inspect_hint' => 'Pobierz próbkę, aby odkryć dostępne pola do użycia w kolejnych węzłach.', - 'inspect_error' => 'Nie udało się odczytać tego kanału. Sprawdź adres URL i spróbuj ponownie.', - 'discovered_fields' => 'Dostępne pola', - 'discovered_empty' => 'Nie znaleziono pól w najnowszym elemencie.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Metoda', - 'auth_type' => 'Uwierzytelnianie', - 'auth' => [ - 'none' => 'Brak (publiczne)', - 'bearer' => 'Token Bearer', - 'basic' => 'Uwierzytelnianie podstawowe', - 'api_key' => 'Nagłówek z kluczem API', - ], - 'bearer_token' => 'Token Bearer', - 'basic_username' => 'Nazwa użytkownika', - 'basic_password' => 'Hasło', - 'api_key_header' => 'Nazwa nagłówka', - 'api_key_value' => 'Klucz API', - 'body_template' => 'Szablon treści (JSON)', - 'headers' => 'Nagłówki', - 'header_name' => 'Nazwa nagłówka', - 'header_value' => 'Wartość', - 'add_header' => 'Dodaj nagłówek', - 'polling_section' => 'Lista i deduplikacja (opcjonalnie)', - 'polling_hint' => 'Gdy odpowiedź jest listą, każdy element uruchamia przepływ osobno. Pojedynczy obiekt uruchamia się raz.', - 'items_path' => 'Ścieżka do elementów', - 'items_path_hint' => 'Pozostaw puste, jeśli odpowiedź jest już tablicą. Użyj ścieżki z kropkami (np. data.items) dla zagnieżdżonej tablicy lub * dla obiektu z kluczami po id.', - 'item_key_path' => 'Ścieżka do klucza elementu', - 'item_key_path_hint' => 'Ścieżka JSON do unikalnego id (np. id). Elementy już widziane są pomijane, więc kanał bez dat nadal przekazuje dalej tylko nowe wpisy.', - 'item_date_path' => 'Ścieżka do daty elementu', - 'item_date_path_hint' => 'Ścieżka JSON do znacznika czasu elementu (np. published_at). Preferowana zamiast ścieżki klucza, gdy jest dostępna. Pierwsze odpytanie zapisuje punkt odniesienia i nic nie przekazuje, więc istniejący kanał nigdy nie zaleje systemu pierwszego dnia.', - ], - ], - - 'delete' => [ - 'title' => 'Usuń automatyzację', - 'description' => 'Czy na pewno chcesz usunąć tę automatyzację? Wszystkie uruchomienia i elementy wyzwalacza również zostaną usunięte. Tej operacji nie można cofnąć.', - 'confirm' => 'Usuń', - 'cancel' => 'Anuluj', - ], - - 'flash' => [ - 'deleted' => 'Automatyzacja została pomyślnie usunięta!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Brak aktywnych kont społecznościowych skonfigurowanych dla tej automatyzacji.', - 'must_have_one_trigger' => 'Automatyzacja musi mieć dokładnie jeden węzeł wyzwalacza.', - 'trigger_must_be_connected' => 'Węzeł wyzwalacza musi być połączony z co najmniej jednym węzłem.', - 'graph_contains_cycle' => 'Graf automatyzacji zawiera cykl.', - 'only_failed_can_retry' => 'Tylko nieudane uruchomienia można ponowić.', - 'no_generated_post' => 'Nie znaleziono wygenerowanego posta w uruchomieniu.', - 'url_not_allowed' => 'Adres URL żądania wskazuje na prywatny lub nieosiągalny adres i został zablokowany.', - 'node_no_longer_exists' => 'Węzeł :node_id już nie istnieje w automatyzacji.', - 'no_trigger_connection' => 'Żaden węzeł nie jest połączony z węzłem wyzwalacza.', - 'fetch_rss_missing_url' => 'W węźle Pobierz RSS brakuje adresu URL kanału.', - 'fetch_rss_request_failed' => 'Żądanie kanału RSS nie powiodło się.', - 'fetch_rss_malformed' => 'Kanał RSS jest uszkodzony.', - 'http_missing_url' => 'W węźle żądania HTTP brakuje adresu URL.', - 'http_request_exception' => 'Żądanie HTTP zgłosiło wyjątek.', - 'http_request_failed' => 'Żądanie HTTP nie powiodło się.', - 'http_items_path_not_array' => 'Ścieżka do elementów nie zwróciła listy.', - 'generate_image_format_required' => 'Generowanie AI tworzy tylko obrazy. Wybierz format obrazu (nie wideo).', - ], -]; diff --git a/lang/pl/common.php b/lang/pl/common.php index 7315d1c1..227b44bb 100644 --- a/lang/pl/common.php +++ b/lang/pl/common.php @@ -6,8 +6,6 @@ 'back' => 'Wstecz', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Tej operacji nie można cofnąć.', 'type' => 'Wpisz', diff --git a/lang/pl/sidebar.php b/lang/pl/sidebar.php index e62c03f4..213feb27 100644 --- a/lang/pl/sidebar.php +++ b/lang/pl/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Inne', ], 'analytics' => 'Analityka', - 'automations' => 'Automatyzacje', 'onboarding' => 'Pierwsze kroki', 'onboarding_hint' => 'Dokończ konfigurację', 'posts' => [ diff --git a/lang/pt-BR/automations.php b/lang/pt-BR/automations.php deleted file mode 100644 index 81c5f9c1..00000000 --- a/lang/pt-BR/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'O editor de automações funciona melhor em uma tela maior. Abra no desktop para montar seu fluxo de trabalho.', - 'title' => 'Automações', - 'default_name' => 'Nova automação', - - 'actions' => [ - 'new' => 'Nova automação', - 'edit' => 'Editar', - 'save' => 'Salvar', - 'activate' => 'Ativar', - 'pause' => 'Pausar', - 'delete' => 'Excluir', - 'retry' => 'Tentar novamente', - 'guide' => 'Aprenda como funciona', - ], - - 'tabs' => [ - 'build' => 'Montar', - 'variables' => 'Variáveis', - 'test' => 'Testar', - ], - - 'nav' => [ - 'workflow' => 'Workflow', - 'invocations' => 'Invocações', - 'metrics' => 'Métricas', - 'settings' => 'Configurações', - ], - - 'settings' => [ - 'general' => 'Geral', - 'general_description' => 'Renomeie esta automação.', - 'name_label' => 'Nome', - 'name_saved' => 'Automação renomeada.', - 'status_title' => 'Status', - 'status_description' => 'Ative para começar a rodar, ou pause para parar.', - 'activated_at' => 'Ativada em :date', - 'paused_at' => 'Pausada em :date', - 'created_at' => 'Criada em :date', - 'danger_title' => 'Zona de perigo', - 'danger_description' => 'Ações irreversíveis.', - 'delete_title' => 'Excluir esta automação', - 'delete_description' => 'Remove permanentemente a automação e o histórico de execuções.', - ], - - 'status_run' => [ - 'pending' => 'Pendente', - 'running' => 'Executando', - 'waiting' => 'Aguardando', - 'completed' => 'Concluído', - 'failed' => 'Falhou', - 'cancelled' => 'Cancelado', - ], - - 'node_type' => [ - 'trigger' => 'Gatilho', - 'generate' => 'Gerar conteúdo', - 'delay' => 'Espera', - 'condition' => 'Condição', - 'publish' => 'Publicar', - 'end' => 'Fim', - 'fetch_rss' => 'Buscar RSS', - 'http_request' => 'Requisição HTTP', - ], - - 'invocations' => [ - 'empty' => 'Nenhuma invocação ainda.', - 'refresh' => 'Atualizar', - 'search_placeholder' => 'Buscar por ID do run…', - 'copied' => 'ID do run copiado.', - 'loading' => 'Carregando passos…', - 'no_steps' => 'Nenhum passo registrado.', - 'load_error' => 'Não foi possível carregar os passos.', - 'steps' => '{0}Nenhum passo|{1}:count passo|[2,*]:count passos', - 'filter' => [ - 'all' => 'Todos os status', - ], - 'columns' => [ - 'timestamp' => 'Data', - 'run' => 'Run', - 'status' => 'Status', - 'message' => 'Última mensagem', - 'duration' => 'Duração', - ], - 'summary' => [ - 'completed' => 'Workflow concluído', - 'failed' => 'Workflow falhou', - 'running' => 'Workflow em execução', - 'cancelled' => 'Workflow cancelado', - 'pending' => 'Workflow pendente', - ], - ], - - 'metrics' => [ - 'overview' => 'Visão geral', - 'runs_over_time' => 'Execuções ao longo do tempo', - 'posts_by_platform' => 'Posts por plataforma', - 'no_posts' => 'Nenhum post publicado neste período.', - 'cards' => [ - 'runs' => 'Total de execuções', - 'completed' => 'Concluídas', - 'failed' => 'Falhas', - 'in_progress' => 'Em progresso', - 'success_rate' => 'Taxa de sucesso', - 'avg_duration' => 'Duração média', - 'posts_created' => 'Posts criados', - ], - 'legend' => [ - 'started' => 'Iniciadas', - 'completed' => 'Concluídas', - 'failed' => 'Falhas', - ], - ], - - 'categories' => [ - 'sources' => 'Fontes', - 'content' => 'Conteúdo', - 'flow' => 'Fluxo', - 'output' => 'Saída', - ], - - 'variables' => [ - 'title' => 'Variáveis do workflow', - 'hint' => 'Valores reutilizáveis referenciados em qualquer lugar com {{ variables.KEY }}. Armazenados encriptados.', - 'empty' => 'Nenhuma variável ainda.', - 'key' => 'Chave', - 'value' => 'Valor', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Valor', - 'add' => 'Nova variável', - ], - - 'expr' => [ - 'trigger_event' => 'Nome do evento do gatilho', - 'trigger_fired_at' => 'Quando o gatilho disparou', - 'trigger_post_id' => 'ID do post que disparou', - 'trigger_post_content' => 'Conteúdo do post que disparou', - 'trigger_post_status' => 'Status do post que disparou', - 'trigger_post_scheduled_at' => 'Quando o post está agendado', - 'trigger_post_published_at' => 'Quando o post foi publicado', - 'fetched_title' => 'Título do item buscado', - 'fetched_link' => 'Link do item buscado', - 'fetched_date' => 'Data de publicação do item buscado', - 'fetched_content' => 'Conteúdo completo do item buscado', - 'fetched_description' => 'Resumo do item buscado', - 'fetched_author' => 'Autor do item buscado', - 'fetched_image' => 'URL da imagem do item buscado', - 'fetched_categories' => 'Categorias do item buscado', - 'fetched_enclosure' => 'Mídia do item (áudio/vídeo/arquivo)', - 'fetched_pubdate' => 'Data de publicação do item buscado', - 'fetched_http' => 'Item HTTP buscado (adicione um campo)', - 'generated_content' => 'Conteúdo do post gerado por IA', - 'generated_post_url' => 'URL do post gerado por IA', - 'variable' => 'Variável do fluxo', - 'now' => 'Data e hora atuais', - ], - - 'test' => [ - 'description' => 'Executa a automação ponta a ponta usando um payload de gatilho sintético. Útil pra validar cada nó sem esperar o agendamento ou o feed real.', - 'starting' => 'Iniciando execução de teste…', - 'in_progress' => 'Em andamento', - 'completed' => 'Concluído', - 'failed' => 'Falhou', - 'waiting' => 'Aguardando', - 'close' => 'Fechar', - 'no_node_runs' => 'Aguardando o primeiro nó começar…', - 'node_input' => 'Entrada', - 'node_output' => 'Saída', - 'node_error' => 'Erro', - 'no_new_items' => 'Nenhum item novo — nada foi executado adiante.', - 'error_starting' => 'Não foi possível iniciar a execução de teste.', - 'with_real_data' => 'Com dados reais', - 'run' => 'Rodar teste', - 'idle_hint' => 'Clique em Rodar teste para executar a automação de ponta a ponta.', - 'real_data_hint' => 'Este teste vai publicar posts, avançar watermarks e disparar efeitos colaterais externos.', - 'dry_badge' => 'Teste seco', - ], - - 'status' => [ - 'draft' => 'Rascunho', - 'active' => 'Ativa', - 'paused' => 'Pausada', - ], - - 'index' => [ - 'empty_title' => 'Nenhuma automação ainda', - 'empty_description' => 'Crie sua primeira automação para começar a publicar no piloto automático.', - 'columns' => [ - 'name' => 'Nome', - 'status' => 'Status', - 'created' => 'Criada em', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Não foi possível ativar a automação.', - 'pause_error_fallback' => 'Não foi possível pausar a automação.', - 'save_error_fallback' => 'Não foi possível salvar a automação.', - 'save_success' => 'Automação salva.', - 'empty_canvas_title' => 'Comece a construir sua automação', - 'empty_canvas_description' => 'Arraste um nó do painel esquerdo para começar.', - 'name_placeholder' => 'Automação sem título', - ], - - 'nodes' => [ - 'trigger' => 'Trigger', - 'generate' => 'Gerar', - 'delay' => 'Esperar', - 'condition' => 'Condição', - 'publish' => 'Publicar', - 'end' => 'Encerrar', - 'end_summary' => 'Encerra a automação aqui', - 'fetch_rss' => 'Buscar RSS', - 'http_request' => 'Requisição HTTP', - 'handles' => [ - 'items' => 'tem itens', - 'no_items' => 'sem itens', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Selecione…', - 'invalid_json' => 'Isto ainda não é um JSON válido.', - 'expand_editor' => 'Expandir editor', - 'minimize_editor' => 'Minimizar', - - 'trigger' => [ - 'type' => 'Tipo de trigger', - 'types' => [ - 'schedule' => 'Agendamento', - 'post_published' => 'Quando um post é publicado', - 'post_scheduled' => 'Quando um post é agendado', - ], - 'post_published_hint' => 'Roda toda vez que algum post nesta workspace é publicado. O post fica disponível em {{ trigger.post }} pros próximos nós.', - 'post_scheduled_hint' => 'Roda toda vez que algum post nesta workspace é agendado. O post fica disponível em {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Intervalo de disparo', - 'fields' => [ - 'minutes' => 'Minutos', - 'hours' => 'Horas', - 'days' => 'Dias', - 'weeks' => 'Semanas', - 'months' => 'Meses', - ], - 'minutes_interval' => 'Minutos entre disparos', - 'hours_interval' => 'Horas entre disparos', - 'days_interval' => 'Dias entre disparos', - 'hour' => 'Disparar na hora', - 'minute' => 'Disparar no minuto', - 'weekdays' => 'Disparar nos dias', - 'day_of_month' => 'Dia do mês', - 'weekday_names' => [ - 'sun' => 'Dom', - 'mon' => 'Seg', - 'tue' => 'Ter', - 'wed' => 'Qua', - 'thu' => 'Qui', - 'fri' => 'Sex', - 'sat' => 'Sáb', - ], - 'summary' => [ - 'every_n_minutes' => 'Roda a cada minuto|Roda a cada :count minutos', - 'every_n_hours' => 'Roda a cada hora no minuto :minute|Roda a cada :count horas no minuto :minute', - 'every_n_days' => 'Roda todo dia às :time|Roda a cada :count dias às :time', - 'weekly' => 'Roda :days às :time', - 'monthly' => 'Roda no dia :day de cada mês às :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Contas sociais', - 'social_accounts_empty' => 'Nenhuma conta social conectada. Conecte uma primeiro.', - 'target_slide_count' => 'Slides a gerar', - 'prompt_template' => 'Template do prompt', - 'prompt_template_hint' => 'Digite {{ para inserir dados das etapas anteriores.', - 'image_count' => 'Imagens a gerar', - 'image_count_hint' => '0 = post só texto (sem imagem). 1 = imagem única. 2+ = carrossel.', - 'use_brand_voice' => 'Usar voz da marca', - 'use_brand_voice_hint' => 'Aplica a descrição e a voz da sua marca. Desligue para curadoria fiel de fontes de terceiros (notícias, RSS).', - 'use_brand_visuals' => 'Usar visual da marca', - 'use_brand_visuals_hint' => 'Guia as imagens de IA com as cores e identidade da sua marca. Desligue para imagens neutras, guiadas só pelo tema do post.', - 'style' => 'Estilo', - 'account_summary' => ':count conta · :format|:count contas · :format', - 'formats' => [ - 'single' => 'único', - 'carousel' => 'carrossel', - ], - ], - 'delay' => [ - 'duration' => 'Duração', - 'unit' => 'Unidade', - 'units' => [ - 'minutes' => 'Minutos', - 'hours' => 'Horas', - 'days' => 'Dias', - ], - ], - 'condition' => [ - 'field' => 'Campo', - 'operator' => 'Operador', - 'operators' => [ - 'contains' => 'contém', - 'not_contains' => 'não contém', - 'equals' => 'igual a', - 'not_equals' => 'diferente de', - 'matches' => 'corresponde (regex)', - 'greater_than' => 'maior que', - 'less_than' => 'menor que', - ], - 'value' => 'Valor', - ], - 'publish' => [ - 'mode' => 'Modo', - 'modes' => [ - 'now' => 'Publicar agora', - 'scheduled' => 'Agendar', - 'draft' => 'Salvar como rascunho', - ], - 'scheduled_offset' => 'Atraso a partir do trigger (minutos)', - 'offset_summary' => ':mode · +:offset min', - ], - 'end' => [ - 'reason' => 'Motivo (opcional)', - 'reason_placeholder' => 'ex: Filtrado pela condição', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL do feed', - 'feed_url_hint' => 'Na primeira execução, o watermark é setado pra "agora" pra não inundar os próximos nós com items históricos. Execuções seguintes só veem items novos.', - 'inspect' => 'Inspecionar feed', - 'inspecting' => 'Inspecionando…', - 'inspect_hint' => 'Busca uma amostra pra descobrir os campos disponíveis pra usar nos próximos nós.', - 'inspect_error' => 'Não foi possível ler esse feed. Confira a URL e tente de novo.', - 'discovered_fields' => 'Campos disponíveis', - 'discovered_empty' => 'Nenhum campo encontrado no último item.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Método', - 'auth_type' => 'Autenticação', - 'auth' => [ - 'none' => 'Nenhuma (público)', - 'bearer' => 'Bearer token', - 'basic' => 'Basic auth', - 'api_key' => 'Header de API key', - ], - 'bearer_token' => 'Bearer token', - 'basic_username' => 'Usuário', - 'basic_password' => 'Senha', - 'api_key_header' => 'Nome do header', - 'api_key_value' => 'API key', - 'body_template' => 'Template do body (JSON)', - 'headers' => 'Headers', - 'header_name' => 'Nome do header', - 'header_value' => 'Valor', - 'add_header' => 'Adicionar header', - 'polling_section' => 'Lista e deduplicação (opcional)', - 'polling_hint' => 'Quando a resposta é uma lista, cada item roda o fluxo separadamente. Um objeto único roda uma vez.', - 'items_path' => 'Caminho dos itens', - 'items_path_hint' => 'Deixe vazio se a resposta já é um array. Use um caminho com ponto (ex: data.items) para um array aninhado, ou * para um objeto com chaves por id.', - 'item_key_path' => 'Caminho da chave do item', - 'item_key_path_hint' => 'Caminho JSON pra um id único (ex: id). Itens já vistos são ignorados, então um feed sem datas ainda encaminha só os novos.', - 'item_date_path' => 'Caminho da data do item', - 'item_date_path_hint' => 'Caminho JSON pro timestamp do item (ex: published_at). Preferido sobre o caminho da chave quando existe. A primeira busca registra o ponto de partida e não encaminha nada, então um feed existente nunca inunda no primeiro dia.', - ], - ], - - 'delete' => [ - 'title' => 'Excluir automação', - 'description' => 'Tem certeza que deseja excluir esta automação? Todas as execuções e itens de gatilho também serão removidos. Esta ação não pode ser desfeita.', - 'confirm' => 'Excluir', - 'cancel' => 'Cancelar', - ], - - 'flash' => [ - 'deleted' => 'Automação excluída com sucesso!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Nenhuma conta social ativa configurada para esta automação.', - 'must_have_one_trigger' => 'A automação precisa ter exatamente um nó de trigger.', - 'trigger_must_be_connected' => 'O nó de trigger precisa estar conectado a pelo menos um nó.', - 'graph_contains_cycle' => 'O grafo da automação contém um ciclo.', - 'only_failed_can_retry' => 'Apenas execuções que falharam podem ser repetidas.', - 'no_generated_post' => 'Nenhum post gerado encontrado para esta execução.', - 'url_not_allowed' => 'A URL da requisição aponta para um endereço privado ou inacessível e foi bloqueada.', - 'node_no_longer_exists' => 'O nó :node_id não existe mais nesta automação.', - 'no_trigger_connection' => 'Nenhum nó conectado ao nó de trigger.', - 'fetch_rss_missing_url' => 'O nó Buscar RSS está sem a URL do feed.', - 'fetch_rss_request_failed' => 'A requisição do feed RSS falhou.', - 'fetch_rss_malformed' => 'O feed RSS está malformado.', - 'http_missing_url' => 'O nó de requisição HTTP está sem a URL.', - 'http_request_exception' => 'A requisição HTTP lançou uma exceção.', - 'http_request_failed' => 'A requisição HTTP falhou.', - 'http_items_path_not_array' => 'O items path não resultou em uma lista.', - 'generate_image_format_required' => 'A geração por IA só cria imagens. Escolha um formato de imagem (não vídeo).', - ], -]; diff --git a/lang/pt-BR/common.php b/lang/pt-BR/common.php index 4af658d4..9529fd80 100644 --- a/lang/pt-BR/common.php +++ b/lang/pt-BR/common.php @@ -6,8 +6,6 @@ 'back' => 'Voltar', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Esta ação não pode ser desfeita.', 'type' => 'Digite', diff --git a/lang/pt-BR/sidebar.php b/lang/pt-BR/sidebar.php index 7ddc2866..b82d527e 100644 --- a/lang/pt-BR/sidebar.php +++ b/lang/pt-BR/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Outros', ], 'analytics' => 'Analytics', - 'automations' => 'Automações', 'onboarding' => 'Primeiros passos', 'onboarding_hint' => 'Complete a configuração', 'posts' => [ diff --git a/lang/ru/automations.php b/lang/ru/automations.php deleted file mode 100644 index 9331e324..00000000 --- a/lang/ru/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Редактор автоматизаций лучше всего работает на большом экране. Откройте его на компьютере, чтобы создать рабочий процесс.', - 'title' => 'Автоматизации', - 'default_name' => 'Новая автоматизация', - - 'actions' => [ - 'new' => 'Новая автоматизация', - 'edit' => 'Изменить', - 'save' => 'Сохранить', - 'activate' => 'Активировать', - 'pause' => 'Приостановить', - 'delete' => 'Удалить', - 'retry' => 'Повторить', - 'guide' => 'Узнать, как это работает', - ], - - 'tabs' => [ - 'build' => 'Конструктор', - 'variables' => 'Переменные', - 'test' => 'Тест', - ], - - 'nav' => [ - 'workflow' => 'Процесс', - 'invocations' => 'Запуски', - 'metrics' => 'Метрики', - 'settings' => 'Настройки', - ], - - 'settings' => [ - 'general' => 'Основные', - 'general_description' => 'Переименуйте эту автоматизацию.', - 'name_label' => 'Название', - 'name_saved' => 'Автоматизация переименована.', - 'status_title' => 'Статус', - 'status_description' => 'Активируйте, чтобы запустить, или приостановите, чтобы остановить.', - 'activated_at' => 'Активирована :date', - 'paused_at' => 'Приостановлена :date', - 'created_at' => 'Создана :date', - 'danger_title' => 'Опасная зона', - 'danger_description' => 'Необратимые действия.', - 'delete_title' => 'Удалить эту автоматизацию', - 'delete_description' => 'Безвозвратно удаляет автоматизацию и историю её запусков.', - ], - - 'status_run' => [ - 'pending' => 'В ожидании', - 'running' => 'Выполняется', - 'waiting' => 'Ожидание', - 'completed' => 'Завершено', - 'failed' => 'Ошибка', - 'cancelled' => 'Отменено', - ], - - 'node_type' => [ - 'trigger' => 'Триггер', - 'generate' => 'Генерация контента', - 'delay' => 'Задержка', - 'condition' => 'Условие', - 'publish' => 'Публикация', - 'end' => 'Конец', - 'fetch_rss' => 'Получить RSS', - 'http_request' => 'HTTP-запрос', - ], - - 'invocations' => [ - 'empty' => 'Пока нет запусков.', - 'refresh' => 'Обновить', - 'search_placeholder' => 'Поиск по ID запуска…', - 'copied' => 'ID запуска скопирован.', - 'loading' => 'Загрузка шагов…', - 'no_steps' => 'Шаги не записаны.', - 'load_error' => 'Не удалось загрузить шаги.', - 'steps' => '{0}Нет шагов|{1}:count шаг|[2,4]:count шага|[5,*]:count шагов', - 'filter' => [ - 'all' => 'Все статусы', - ], - 'columns' => [ - 'timestamp' => 'Время', - 'run' => 'Запуск', - 'status' => 'Статус', - 'message' => 'Последнее сообщение', - 'duration' => 'Длительность', - ], - 'summary' => [ - 'completed' => 'Процесс завершён', - 'failed' => 'Процесс завершился с ошибкой', - 'running' => 'Процесс выполняется', - 'cancelled' => 'Процесс отменён', - 'pending' => 'Процесс в ожидании', - ], - ], - - 'metrics' => [ - 'overview' => 'Обзор', - 'runs_over_time' => 'Запуски по времени', - 'posts_by_platform' => 'Посты по платформам', - 'no_posts' => 'В этот период не опубликовано ни одного поста.', - 'cards' => [ - 'runs' => 'Всего запусков', - 'completed' => 'Завершено', - 'failed' => 'С ошибкой', - 'in_progress' => 'В процессе', - 'success_rate' => 'Доля успешных', - 'avg_duration' => 'Средняя длительность', - 'posts_created' => 'Создано постов', - ], - 'legend' => [ - 'started' => 'Запущено', - 'completed' => 'Завершено', - 'failed' => 'С ошибкой', - ], - ], - - 'categories' => [ - 'sources' => 'Источники', - 'content' => 'Контент', - 'flow' => 'Логика', - 'output' => 'Вывод', - ], - - 'variables' => [ - 'title' => 'Переменные процесса', - 'hint' => 'Многократно используемые значения, доступные везде через {{ variables.KEY }}. Хранятся в зашифрованном виде.', - 'empty' => 'Пока нет переменных.', - 'key' => 'Ключ', - 'value' => 'Значение', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Значение', - 'add' => 'Новая переменная', - ], - - 'expr' => [ - 'trigger_event' => 'Название события-триггера', - 'trigger_fired_at' => 'Когда сработал триггер', - 'trigger_post_id' => 'ID поста-триггера', - 'trigger_post_content' => 'Содержимое поста-триггера', - 'trigger_post_status' => 'Статус поста-триггера', - 'trigger_post_scheduled_at' => 'Когда запланирован пост', - 'trigger_post_published_at' => 'Когда пост был опубликован', - 'fetched_title' => 'Заголовок полученного элемента', - 'fetched_link' => 'Ссылка полученного элемента', - 'fetched_date' => 'Дата публикации полученного элемента', - 'fetched_content' => 'Полное содержимое полученного элемента', - 'fetched_description' => 'Краткое описание полученного элемента', - 'fetched_author' => 'Автор полученного элемента', - 'fetched_image' => 'URL изображения полученного элемента', - 'fetched_categories' => 'Категории полученного элемента', - 'fetched_enclosure' => 'Медиа полученного элемента (аудио/видео/файл)', - 'fetched_pubdate' => 'Дата публикации полученного элемента', - 'fetched_http' => 'Полученный HTTP-элемент (добавьте поле)', - 'generated_content' => 'Сгенерированное ИИ содержимое поста', - 'generated_post_url' => 'URL сгенерированного ИИ поста', - 'variable' => 'Переменная процесса', - 'now' => 'Текущие дата и время', - ], - - 'test' => [ - 'description' => 'Выполняет автоматизацию от начала до конца, используя синтезированные данные триггера. Полезно для проверки каждого узла без ожидания реального расписания или ленты.', - 'starting' => 'Запуск тестового прогона…', - 'in_progress' => 'В процессе', - 'completed' => 'Завершено', - 'failed' => 'Ошибка', - 'waiting' => 'Ожидание', - 'close' => 'Закрыть', - 'no_node_runs' => 'Ожидание запуска первого узла…', - 'node_input' => 'Вход', - 'node_output' => 'Выход', - 'node_error' => 'Ошибка', - 'no_new_items' => 'Нет новых элементов — последующие узлы не запускались.', - 'error_starting' => 'Не удалось запустить тестовый прогон.', - 'with_real_data' => 'С реальными данными', - 'run' => 'Запустить тест', - 'idle_hint' => 'Нажмите «Запустить тест», чтобы выполнить автоматизацию от начала до конца.', - 'real_data_hint' => 'Этот тест опубликует посты, продвинет отметки опроса и вызовет внешние побочные эффекты.', - 'dry_badge' => 'Тестовый прогон', - ], - - 'status' => [ - 'draft' => 'Черновик', - 'active' => 'Активна', - 'paused' => 'Приостановлена', - ], - - 'index' => [ - 'empty_title' => 'Пока нет автоматизаций', - 'empty_description' => 'Создайте первую автоматизацию, чтобы публиковать на автопилоте.', - 'columns' => [ - 'name' => 'Название', - 'status' => 'Статус', - 'created' => 'Создана', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Не удалось активировать автоматизацию.', - 'pause_error_fallback' => 'Не удалось приостановить автоматизацию.', - 'save_error_fallback' => 'Не удалось сохранить автоматизацию.', - 'save_success' => 'Автоматизация сохранена.', - 'empty_canvas_title' => 'Начните создавать автоматизацию', - 'empty_canvas_description' => 'Перетащите узел из левой панели, чтобы начать.', - 'name_placeholder' => 'Автоматизация без названия', - ], - - 'nodes' => [ - 'trigger' => 'Триггер', - 'generate' => 'Генерация', - 'delay' => 'Задержка', - 'condition' => 'Условие', - 'publish' => 'Публикация', - 'end' => 'Конец', - 'end_summary' => 'Останавливает автоматизацию здесь', - 'fetch_rss' => 'Получить RSS', - 'http_request' => 'HTTP-запрос', - 'handles' => [ - 'items' => 'есть элементы', - 'no_items' => 'нет элементов', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Выберите…', - 'invalid_json' => 'Это ещё не корректный JSON.', - 'expand_editor' => 'Развернуть редактор', - 'minimize_editor' => 'Свернуть', - - 'trigger' => [ - 'type' => 'Тип триггера', - 'types' => [ - 'schedule' => 'Расписание', - 'post_published' => 'Когда пост опубликован', - 'post_scheduled' => 'Когда пост запланирован', - ], - 'post_published_hint' => 'Запускается при публикации любого поста в этом рабочем пространстве. Опубликованный пост доступен в {{ trigger.post }} для последующих узлов.', - 'post_scheduled_hint' => 'Запускается при планировании любого поста в этом рабочем пространстве. Запланированный пост доступен в {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Интервал срабатывания', - 'fields' => [ - 'minutes' => 'Минуты', - 'hours' => 'Часы', - 'days' => 'Дни', - 'weeks' => 'Недели', - 'months' => 'Месяцы', - ], - 'minutes_interval' => 'Минут между срабатываниями', - 'hours_interval' => 'Часов между срабатываниями', - 'days_interval' => 'Дней между срабатываниями', - 'hour' => 'Час срабатывания', - 'minute' => 'Минута срабатывания', - 'weekdays' => 'Дни недели срабатывания', - 'day_of_month' => 'День месяца', - 'weekday_names' => [ - 'sun' => 'Вс', - 'mon' => 'Пн', - 'tue' => 'Вт', - 'wed' => 'Ср', - 'thu' => 'Чт', - 'fri' => 'Пт', - 'sat' => 'Сб', - ], - 'summary' => [ - 'every_n_minutes' => 'Запускается каждую :count минуту|Запускается каждые :count минуты|Запускается каждые :count минут', - 'every_n_hours' => 'Запускается каждый :count час на :minute-й минуте|Запускается каждые :count часа на :minute-й минуте|Запускается каждые :count часов на :minute-й минуте', - 'every_n_days' => 'Запускается каждый :count день в :time|Запускается каждые :count дня в :time|Запускается каждые :count дней в :time', - 'weekly' => 'Запускается по :days в :time', - 'monthly' => 'Запускается :day числа каждого месяца в :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Социальные аккаунты', - 'social_accounts_empty' => 'Нет подключённых социальных аккаунтов. Сначала подключите один.', - 'target_slide_count' => 'Сколько слайдов сгенерировать', - 'prompt_template' => 'Шаблон запроса', - 'prompt_template_hint' => 'Введите {{, чтобы вставить данные из предыдущих шагов.', - 'image_count' => 'Сколько изображений сгенерировать', - 'image_count_hint' => '0 = только текст (без изображения). 1 = одно изображение. 2+ = карусель.', - 'use_brand_voice' => 'Использовать голос бренда', - 'use_brand_voice_hint' => 'Применяйте описание и голос вашего бренда. Отключите для точной подачи сторонних источников (новости, RSS).', - 'use_brand_visuals' => 'Использовать визуал бренда', - 'use_brand_visuals_hint' => 'Направляйте ИИ-изображения цветами и айдентикой вашего бренда. Отключите для нейтральных изображений, основанных только на теме поста.', - 'style' => 'Стиль', - 'account_summary' => ':count аккаунт · :format|:count аккаунта · :format|:count аккаунтов · :format', - 'formats' => [ - 'single' => 'один', - 'carousel' => 'карусель', - ], - ], - 'delay' => [ - 'duration' => 'Длительность', - 'unit' => 'Единица', - 'units' => [ - 'minutes' => 'Минуты', - 'hours' => 'Часы', - 'days' => 'Дни', - ], - ], - 'condition' => [ - 'field' => 'Поле', - 'operator' => 'Оператор', - 'operators' => [ - 'contains' => 'содержит', - 'not_contains' => 'не содержит', - 'equals' => 'равно', - 'not_equals' => 'не равно', - 'matches' => 'соответствует (regex)', - 'greater_than' => 'больше чем', - 'less_than' => 'меньше чем', - ], - 'value' => 'Значение', - ], - 'publish' => [ - 'mode' => 'Режим', - 'modes' => [ - 'now' => 'Опубликовать сейчас', - 'scheduled' => 'Запланировать', - 'draft' => 'Сохранить как черновик', - ], - 'scheduled_offset' => 'Смещение от триггера (минуты)', - 'offset_summary' => ':mode · +:offset мин', - ], - 'end' => [ - 'reason' => 'Причина (необязательно)', - 'reason_placeholder' => 'например, Отфильтровано условием', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL ленты', - 'feed_url_hint' => 'При первом запуске отметка устанавливается на «сейчас», чтобы старые элементы не хлынули в последующие узлы. Последующие запуски видят только элементы новее предыдущего опроса.', - 'inspect' => 'Проверить ленту', - 'inspecting' => 'Проверка…', - 'inspect_hint' => 'Получите образец, чтобы узнать доступные поля для использования в последующих узлах.', - 'inspect_error' => 'Не удалось прочитать эту ленту. Проверьте URL и попробуйте снова.', - 'discovered_fields' => 'Доступные поля', - 'discovered_empty' => 'В последнем элементе поля не найдены.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Метод', - 'auth_type' => 'Аутентификация', - 'auth' => [ - 'none' => 'Нет (публичный)', - 'bearer' => 'Bearer-токен', - 'basic' => 'Basic-аутентификация', - 'api_key' => 'Заголовок с API-ключом', - ], - 'bearer_token' => 'Bearer-токен', - 'basic_username' => 'Имя пользователя', - 'basic_password' => 'Пароль', - 'api_key_header' => 'Имя заголовка', - 'api_key_value' => 'API-ключ', - 'body_template' => 'Шаблон тела (JSON)', - 'headers' => 'Заголовки', - 'header_name' => 'Имя заголовка', - 'header_value' => 'Значение', - 'add_header' => 'Добавить заголовок', - 'polling_section' => 'Список и дедупликация (необязательно)', - 'polling_hint' => 'Если ответ — список, каждый элемент запускает процесс отдельно. Один объект запускает его один раз.', - 'items_path' => 'Путь к элементам', - 'items_path_hint' => 'Оставьте пустым, если ответ уже является массивом. Используйте путь через точку (например, data.items) для вложенного массива или * для объекта с ключами по id.', - 'item_key_path' => 'Путь к ключу элемента', - 'item_key_path_hint' => 'JSON-путь к уникальному id (например, id). Уже встречавшиеся элементы пропускаются, поэтому лента без дат всё равно передаёт только новые записи.', - 'item_date_path' => 'Путь к дате элемента', - 'item_date_path_hint' => 'JSON-путь к отметке времени элемента (например, published_at). Предпочтительнее пути к ключу, когда доступен. Первый опрос фиксирует базовую отметку и ничего не передаёт, поэтому существующая лента не переполняет систему в первый день.', - ], - ], - - 'delete' => [ - 'title' => 'Удалить автоматизацию', - 'description' => 'Вы уверены, что хотите удалить эту автоматизацию? Все запуски и элементы триггеров также будут удалены. Это действие нельзя отменить.', - 'confirm' => 'Удалить', - 'cancel' => 'Отмена', - ], - - 'flash' => [ - 'deleted' => 'Автоматизация успешно удалена!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Для этой автоматизации не настроено ни одного активного социального аккаунта.', - 'must_have_one_trigger' => 'Автоматизация должна содержать ровно один узел-триггер.', - 'trigger_must_be_connected' => 'Узел-триггер должен быть связан хотя бы с одним узлом.', - 'graph_contains_cycle' => 'Граф автоматизации содержит цикл.', - 'only_failed_can_retry' => 'Повторить можно только запуски, завершившиеся с ошибкой.', - 'no_generated_post' => 'В запуске не найдено сгенерированного поста.', - 'url_not_allowed' => 'URL запроса указывает на приватный или недоступный адрес и был заблокирован.', - 'node_no_longer_exists' => 'Узел :node_id больше не существует в автоматизации.', - 'no_trigger_connection' => 'К узлу-триггеру не подключён ни один узел.', - 'fetch_rss_missing_url' => 'В узле «Получить RSS» отсутствует URL ленты.', - 'fetch_rss_request_failed' => 'Запрос RSS-ленты не удался.', - 'fetch_rss_malformed' => 'RSS-лента имеет некорректный формат.', - 'http_missing_url' => 'В узле HTTP-запроса отсутствует URL.', - 'http_request_exception' => 'HTTP-запрос вызвал исключение.', - 'http_request_failed' => 'HTTP-запрос не удался.', - 'http_items_path_not_array' => 'Путь к элементам не привёл к списку.', - 'generate_image_format_required' => 'ИИ генерирует только изображения. Выберите формат изображения (не видео).', - ], -]; diff --git a/lang/ru/common.php b/lang/ru/common.php index 7a58a033..9534ae1c 100644 --- a/lang/ru/common.php +++ b/lang/ru/common.php @@ -6,8 +6,6 @@ 'back' => 'Назад', - 'beta' => 'Бета', - 'confirm_modal' => [ 'cannot_be_undone' => 'Это действие нельзя отменить.', 'type' => 'Введите', diff --git a/lang/ru/sidebar.php b/lang/ru/sidebar.php index d0eeaa9b..358d1a30 100644 --- a/lang/ru/sidebar.php +++ b/lang/ru/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Прочее', ], 'analytics' => 'Аналитика', - 'automations' => 'Автоматизации', 'onboarding' => 'Начало работы', 'onboarding_hint' => 'Завершите настройку', 'posts' => [ diff --git a/lang/tr/automations.php b/lang/tr/automations.php deleted file mode 100644 index cbbd1e7e..00000000 --- a/lang/tr/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Otomasyon düzenleyicisi daha büyük bir ekranda en iyi şekilde çalışır. İş akışınızı oluşturmak için masaüstünde açın.', - 'title' => 'Otomasyonlar', - 'default_name' => 'Yeni otomasyon', - - 'actions' => [ - 'new' => 'Yeni otomasyon', - 'edit' => 'Düzenle', - 'save' => 'Kaydet', - 'activate' => 'Etkinleştir', - 'pause' => 'Duraklat', - 'delete' => 'Sil', - 'retry' => 'Tekrar dene', - 'guide' => 'Nasıl çalıştığını öğren', - ], - - 'tabs' => [ - 'build' => 'Oluştur', - 'variables' => 'Değişkenler', - 'test' => 'Test', - ], - - 'nav' => [ - 'workflow' => 'İş Akışı', - 'invocations' => 'Çalıştırmalar', - 'metrics' => 'Metrikler', - 'settings' => 'Ayarlar', - ], - - 'settings' => [ - 'general' => 'Genel', - 'general_description' => 'Bu otomasyonu yeniden adlandırın.', - 'name_label' => 'Ad', - 'name_saved' => 'Otomasyon yeniden adlandırıldı.', - 'status_title' => 'Durum', - 'status_description' => 'Çalıştırmaya başlamak için etkinleştirin veya durdurmak için duraklatın.', - 'activated_at' => 'Etkinleştirildi: :date', - 'paused_at' => 'Duraklatıldı: :date', - 'created_at' => 'Oluşturuldu: :date', - 'danger_title' => 'Tehlikeli bölge', - 'danger_description' => 'Geri alınamaz işlemler.', - 'delete_title' => 'Bu otomasyonu sil', - 'delete_description' => 'Otomasyonu ve çalışma geçmişini kalıcı olarak kaldırır.', - ], - - 'status_run' => [ - 'pending' => 'Beklemede', - 'running' => 'Çalışıyor', - 'waiting' => 'Bekliyor', - 'completed' => 'Tamamlandı', - 'failed' => 'Başarısız', - 'cancelled' => 'İptal edildi', - ], - - 'node_type' => [ - 'trigger' => 'Tetikleyici', - 'generate' => 'İçerik oluştur', - 'delay' => 'Gecikme', - 'condition' => 'Koşul', - 'publish' => 'Yayınla', - 'end' => 'Bitir', - 'fetch_rss' => 'RSS Getir', - 'http_request' => 'HTTP isteği', - ], - - 'invocations' => [ - 'empty' => 'Henüz çalıştırma yok.', - 'refresh' => 'Yenile', - 'search_placeholder' => 'Çalıştırma kimliğine göre ara…', - 'copied' => 'Çalıştırma kimliği kopyalandı.', - 'loading' => 'Adımlar yükleniyor…', - 'no_steps' => 'Kaydedilmiş adım yok.', - 'load_error' => 'Adımlar yüklenemedi.', - 'steps' => '{0}Adım yok|{1}:count adım|[2,*]:count adım', - 'filter' => [ - 'all' => 'Tüm durumlar', - ], - 'columns' => [ - 'timestamp' => 'Zaman damgası', - 'run' => 'Çalıştırma', - 'status' => 'Durum', - 'message' => 'Son mesaj', - 'duration' => 'Süre', - ], - 'summary' => [ - 'completed' => 'İş akışı tamamlandı', - 'failed' => 'İş akışı başarısız oldu', - 'running' => 'İş akışı çalışıyor', - 'cancelled' => 'İş akışı iptal edildi', - 'pending' => 'İş akışı beklemede', - ], - ], - - 'metrics' => [ - 'overview' => 'Genel Bakış', - 'runs_over_time' => 'Zaman içindeki çalıştırmalar', - 'posts_by_platform' => 'Platforma göre gönderiler', - 'no_posts' => 'Bu dönemde yayınlanan gönderi yok.', - 'cards' => [ - 'runs' => 'Toplam çalıştırma', - 'completed' => 'Tamamlanan', - 'failed' => 'Başarısız', - 'in_progress' => 'Devam eden', - 'success_rate' => 'Başarı oranı', - 'avg_duration' => 'Ort. süre', - 'posts_created' => 'Oluşturulan gönderi', - ], - 'legend' => [ - 'started' => 'Başlatıldı', - 'completed' => 'Tamamlandı', - 'failed' => 'Başarısız', - ], - ], - - 'categories' => [ - 'sources' => 'Kaynaklar', - 'content' => 'İçerik', - 'flow' => 'Akış', - 'output' => 'Çıktı', - ], - - 'variables' => [ - 'title' => 'İş akışı değişkenleri', - 'hint' => '{{ variables.KEY }} ile herhangi bir yerde başvurulan yeniden kullanılabilir değerler. Şifrelenmiş olarak saklanır.', - 'empty' => 'Henüz değişken yok.', - 'key' => 'Anahtar', - 'value' => 'Değer', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Değer', - 'add' => 'Yeni değişken', - ], - - 'expr' => [ - 'trigger_event' => 'Tetikleyici olay adı', - 'trigger_fired_at' => 'Tetikleyicinin tetiklenme zamanı', - 'trigger_post_id' => 'Tetikleyen gönderi kimliği', - 'trigger_post_content' => 'Tetikleyen gönderi içeriği', - 'trigger_post_status' => 'Tetikleyen gönderi durumu', - 'trigger_post_scheduled_at' => 'Gönderinin zamanlandığı an', - 'trigger_post_published_at' => 'Gönderinin yayınlandığı an', - 'fetched_title' => 'Getirilen öğe başlığı', - 'fetched_link' => 'Getirilen öğe bağlantısı', - 'fetched_date' => 'Getirilen öğe yayın tarihi', - 'fetched_content' => 'Getirilen öğenin tam içeriği', - 'fetched_description' => 'Getirilen öğe özeti', - 'fetched_author' => 'Getirilen öğe yazarı', - 'fetched_image' => 'Getirilen öğe görsel URL\'si', - 'fetched_categories' => 'Getirilen öğe kategorileri', - 'fetched_enclosure' => 'Getirilen öğe medyası (ses/video/dosya)', - 'fetched_pubdate' => 'Getirilen öğe yayın tarihi', - 'fetched_http' => 'Getirilen HTTP öğesi (bir alan ekleyin)', - 'generated_content' => 'AI ile oluşturulan gönderi içeriği', - 'generated_post_url' => 'AI ile oluşturulan gönderi URL\'si', - 'variable' => 'İş akışı değişkeni', - 'now' => 'Geçerli tarih ve saat', - ], - - 'test' => [ - 'description' => 'Otomasyonu, sentezlenmiş bir tetikleyici yükü kullanarak baştan sona çalıştırır. Gerçek zamanlamayı veya beslemeyi beklemeden her düğümü doğrulamak için kullanışlıdır.', - 'starting' => 'Test çalıştırması başlatılıyor…', - 'in_progress' => 'Devam ediyor', - 'completed' => 'Tamamlandı', - 'failed' => 'Başarısız', - 'waiting' => 'Bekliyor', - 'close' => 'Kapat', - 'no_node_runs' => 'İlk düğümün başlaması bekleniyor…', - 'node_input' => 'Girdi', - 'node_output' => 'Çıktı', - 'node_error' => 'Hata', - 'no_new_items' => 'Yeni öğe yok — sonraki hiçbir adım çalışmadı.', - 'error_starting' => 'Test çalıştırması başlatılamadı.', - 'with_real_data' => 'Gerçek verilerle', - 'run' => 'Testi çalıştır', - 'idle_hint' => 'Otomasyonu baştan sona çalıştırmak için Testi çalıştır\'a basın.', - 'real_data_hint' => 'Bu test gönderileri yayınlar, yoklama işaretlerini ilerletir ve dış yan etkileri tetikler.', - 'dry_badge' => 'Deneme çalıştırması', - ], - - 'status' => [ - 'draft' => 'Taslak', - 'active' => 'Etkin', - 'paused' => 'Duraklatıldı', - ], - - 'index' => [ - 'empty_title' => 'Henüz otomasyon yok', - 'empty_description' => 'Otomatik pilotta yayınlamaya başlamak için ilk otomasyonunuzu oluşturun.', - 'columns' => [ - 'name' => 'Ad', - 'status' => 'Durum', - 'created' => 'Oluşturuldu', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Otomasyon etkinleştirilemedi.', - 'pause_error_fallback' => 'Otomasyon duraklatılamadı.', - 'save_error_fallback' => 'Otomasyon kaydedilemedi.', - 'save_success' => 'Otomasyon kaydedildi.', - 'empty_canvas_title' => 'Otomasyonunuzu oluşturmaya başlayın', - 'empty_canvas_description' => 'Başlamak için sol panelden bir düğüm sürükleyin.', - 'name_placeholder' => 'Adsız otomasyon', - ], - - 'nodes' => [ - 'trigger' => 'Tetikleyici', - 'generate' => 'Oluştur', - 'delay' => 'Gecikme', - 'condition' => 'Koşul', - 'publish' => 'Yayınla', - 'end' => 'Bitir', - 'end_summary' => 'Otomasyonu burada durdurur', - 'fetch_rss' => 'RSS Getir', - 'http_request' => 'HTTP İsteği', - 'handles' => [ - 'items' => 'öğeleri var', - 'no_items' => 'öğe yok', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Seç…', - 'invalid_json' => 'Bu henüz geçerli JSON değil.', - 'expand_editor' => 'Düzenleyiciyi genişlet', - 'minimize_editor' => 'Küçült', - - 'trigger' => [ - 'type' => 'Tetikleyici türü', - 'types' => [ - 'schedule' => 'Zamanlama', - 'post_published' => 'Bir gönderi yayınlandığında', - 'post_scheduled' => 'Bir gönderi zamanlandığında', - ], - 'post_published_hint' => 'Bu çalışma alanındaki herhangi bir gönderi yayınlandığında çalışır. Yayınlanan gönderi, sonraki düğümler için {{ trigger.post }} adresinde kullanılabilir hale gelir.', - 'post_scheduled_hint' => 'Bu çalışma alanındaki herhangi bir gönderi zamanlandığında çalışır. Zamanlanan gönderi {{ trigger.post }} adresinde kullanılabilir.', - - 'schedule' => [ - 'field' => 'Tetikleme aralığı', - 'fields' => [ - 'minutes' => 'Dakika', - 'hours' => 'Saat', - 'days' => 'Gün', - 'weeks' => 'Hafta', - 'months' => 'Ay', - ], - 'minutes_interval' => 'Tetiklemeler arasındaki dakika', - 'hours_interval' => 'Tetiklemeler arasındaki saat', - 'days_interval' => 'Tetiklemeler arasındaki gün', - 'hour' => 'Şu saatte tetikle', - 'minute' => 'Şu dakikada tetikle', - 'weekdays' => 'Şu günlerde tetikle', - 'day_of_month' => 'Ayın günü', - 'weekday_names' => [ - 'sun' => 'Paz', - 'mon' => 'Pzt', - 'tue' => 'Sal', - 'wed' => 'Çar', - 'thu' => 'Per', - 'fri' => 'Cum', - 'sat' => 'Cmt', - ], - 'summary' => [ - 'every_n_minutes' => 'Her dakika çalışır|Her :count dakikada bir çalışır', - 'every_n_hours' => 'Her saat :minute. dakikada çalışır|Her :count saatte bir :minute. dakikada çalışır', - 'every_n_days' => 'Her gün :time saatinde çalışır|Her :count günde bir :time saatinde çalışır', - 'weekly' => 'Her :days günü :time saatinde çalışır', - 'monthly' => 'Her ayın :day. günü :time saatinde çalışır', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Sosyal hesaplar', - 'social_accounts_empty' => 'Bağlı sosyal hesap yok. Önce bir tane bağlayın.', - 'target_slide_count' => 'Oluşturulacak slayt', - 'prompt_template' => 'İstem şablonu', - 'prompt_template_hint' => 'Önceki adımlardan veri eklemek için {{ yazın.', - 'image_count' => 'Oluşturulacak görsel', - 'image_count_hint' => '0 = yalnızca metin gönderisi (görsel yok). 1 = tek görsel. 2+ = karusel.', - 'use_brand_voice' => 'Marka sesini kullan', - 'use_brand_voice_hint' => 'Marka açıklamanızı ve sesinizi uygular. Üçüncü taraf kaynakların (haber, RSS) sadık bir şekilde derlenmesi için kapatın.', - 'use_brand_visuals' => 'Marka görsellerini kullan', - 'use_brand_visuals_hint' => 'AI görsellerini marka renklerinizle ve kimliğinizle yönlendirin. Yalnızca gönderi konusuna dayalı nötr görseller için kapatın.', - 'style' => 'Stil', - 'account_summary' => ':count hesap · :format|:count hesap · :format', - 'formats' => [ - 'single' => 'tekli', - 'carousel' => 'karusel', - ], - ], - 'delay' => [ - 'duration' => 'Süre', - 'unit' => 'Birim', - 'units' => [ - 'minutes' => 'Dakika', - 'hours' => 'Saat', - 'days' => 'Gün', - ], - ], - 'condition' => [ - 'field' => 'Alan', - 'operator' => 'Operatör', - 'operators' => [ - 'contains' => 'içerir', - 'not_contains' => 'içermez', - 'equals' => 'eşittir', - 'not_equals' => 'eşit değildir', - 'matches' => 'eşleşir (regex)', - 'greater_than' => 'büyüktür', - 'less_than' => 'küçüktür', - ], - 'value' => 'Değer', - ], - 'publish' => [ - 'mode' => 'Mod', - 'modes' => [ - 'now' => 'Şimdi yayınla', - 'scheduled' => 'Zamanla', - 'draft' => 'Taslak olarak kaydet', - ], - 'scheduled_offset' => 'Tetikleyiciden kayma (dakika)', - 'offset_summary' => ':mode · +:offset dk', - ], - 'end' => [ - 'reason' => 'Neden (isteğe bağlı)', - 'reason_placeholder' => 'örn. Koşul tarafından filtrelendi', - ], - 'fetch_rss' => [ - 'feed_url' => 'Besleme URL\'si', - 'feed_url_hint' => 'İlk çalıştırmada işaret "şimdi" olarak ayarlanır; böylece eski öğeler sonraki düğümleri doldurmaz. Sonraki çalıştırmalar yalnızca önceki yoklamadan daha yeni öğeleri görür.', - 'inspect' => 'Beslemeyi incele', - 'inspecting' => 'İnceleniyor…', - 'inspect_hint' => 'Sonraki düğümlerde kullanmak üzere mevcut alanları keşfetmek için bir örnek getirin.', - 'inspect_error' => 'Bu besleme okunamadı. URL\'yi kontrol edip tekrar deneyin.', - 'discovered_fields' => 'Mevcut alanlar', - 'discovered_empty' => 'En son öğede alan bulunamadı.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Yöntem', - 'auth_type' => 'Kimlik doğrulama', - 'auth' => [ - 'none' => 'Yok (herkese açık)', - 'bearer' => 'Bearer token', - 'basic' => 'Temel kimlik doğrulama', - 'api_key' => 'API anahtarı başlığı', - ], - 'bearer_token' => 'Bearer token', - 'basic_username' => 'Kullanıcı adı', - 'basic_password' => 'Parola', - 'api_key_header' => 'Başlık adı', - 'api_key_value' => 'API anahtarı', - 'body_template' => 'Gövde şablonu (JSON)', - 'headers' => 'Başlıklar', - 'header_name' => 'Başlık adı', - 'header_value' => 'Değer', - 'add_header' => 'Başlık ekle', - 'polling_section' => 'Liste ve yinelenenleri kaldırma (isteğe bağlı)', - 'polling_hint' => 'Yanıt bir liste olduğunda, her öğe iş akışını ayrı ayrı çalıştırır. Tek bir nesne bir kez çalışır.', - 'items_path' => 'Öğe yolu', - 'items_path_hint' => 'Yanıt zaten bir diziyse boş bırakın. İç içe bir dizi için noktalı bir yol (örn. data.items) veya kimliğe göre anahtarlanan bir nesne için * kullanın.', - 'item_key_path' => 'Öğe anahtar yolu', - 'item_key_path_hint' => 'Benzersiz bir kimliğe giden JSON yolu (örn. id). Zaten görülen öğeler atlanır; böylece tarihsiz bir besleme bile yalnızca yeni girişleri iletir.', - 'item_date_path' => 'Öğe tarih yolu', - 'item_date_path_hint' => 'Öğe zaman damgasına giden JSON yolu (örn. published_at). Varsa anahtar yolu yerine tercih edilir. İlk yoklama temeli kaydeder ve hiçbir şey iletmez; böylece mevcut bir besleme ilk gün taşmaz.', - ], - ], - - 'delete' => [ - 'title' => 'Otomasyonu sil', - 'description' => 'Bu otomasyonu silmek istediğinizden emin misiniz? Tüm çalıştırmalar ve tetikleyici öğeleri de kaldırılacak. Bu işlem geri alınamaz.', - 'confirm' => 'Sil', - 'cancel' => 'İptal', - ], - - 'flash' => [ - 'deleted' => 'Otomasyon başarıyla silindi!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Bu otomasyon için yapılandırılmış etkin sosyal hesap yok.', - 'must_have_one_trigger' => 'Otomasyonda tam olarak bir tetikleyici düğümü olmalıdır.', - 'trigger_must_be_connected' => 'Tetikleyici düğümü en az bir düğüme bağlı olmalıdır.', - 'graph_contains_cycle' => 'Otomasyon grafiği bir döngü içeriyor.', - 'only_failed_can_retry' => 'Yalnızca başarısız çalıştırmalar yeniden denenebilir.', - 'no_generated_post' => 'Çalıştırmada oluşturulmuş gönderi bulunamadı.', - 'url_not_allowed' => 'İstek URL\'si özel veya erişilemez bir adrese işaret ediyor ve engellendi.', - 'node_no_longer_exists' => ':node_id düğümü artık otomasyonda yok.', - 'no_trigger_connection' => 'Tetikleyici düğümüne bağlı düğüm yok.', - 'fetch_rss_missing_url' => 'RSS Getir düğümünde besleme URL\'si eksik.', - 'fetch_rss_request_failed' => 'RSS besleme isteği başarısız oldu.', - 'fetch_rss_malformed' => 'RSS beslemesi bozuk.', - 'http_missing_url' => 'HTTP istek düğümünde URL eksik.', - 'http_request_exception' => 'HTTP isteği bir istisna oluşturdu.', - 'http_request_failed' => 'HTTP isteği başarısız oldu.', - 'http_items_path_not_array' => 'Öğe yolu bir listeye çözümlenmedi.', - 'generate_image_format_required' => 'AI oluşturma yalnızca görsel üretir. Görsel formatı seçin (video değil).', - ], -]; diff --git a/lang/tr/common.php b/lang/tr/common.php index 615d5ba4..aab1e029 100644 --- a/lang/tr/common.php +++ b/lang/tr/common.php @@ -6,8 +6,6 @@ 'back' => 'Geri', - 'beta' => 'Beta', - 'confirm_modal' => [ 'cannot_be_undone' => 'Bu işlem geri alınamaz.', 'type' => 'Yazın:', diff --git a/lang/tr/sidebar.php b/lang/tr/sidebar.php index 4084d3ff..4142f3c2 100644 --- a/lang/tr/sidebar.php +++ b/lang/tr/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Diğerleri', ], 'analytics' => 'Analitik', - 'automations' => 'Otomasyonlar', 'onboarding' => 'Başlarken', 'onboarding_hint' => 'Kurulumu bitir', 'posts' => [ diff --git a/lang/uk/automations.php b/lang/uk/automations.php deleted file mode 100644 index f6c2f9c3..00000000 --- a/lang/uk/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - 'Редактор автоматизацій найкраще працює на великому екрані. Відкрийте його на комп’ютері, щоб побудувати workflow.', - 'title' => 'Автоматизації', - 'default_name' => 'Нова автоматизація', - - 'actions' => [ - 'new' => 'Нова автоматизація', - 'edit' => 'Редагувати', - 'save' => 'Зберегти', - 'activate' => 'Активувати', - 'pause' => 'Призупинити', - 'delete' => 'Видалити', - 'retry' => 'Повторити', - 'guide' => 'Дізнатися, як це працює', - ], - - 'tabs' => [ - 'build' => 'Конструктор', - 'variables' => 'Змінні', - 'test' => 'Тест', - ], - - 'nav' => [ - 'workflow' => 'Процес', - 'invocations' => 'Запуски', - 'metrics' => 'Метрики', - 'settings' => 'Налаштування', - ], - - 'settings' => [ - 'general' => 'Загальні', - 'general_description' => 'Перейменуйте цю автоматизацію.', - 'name_label' => 'Назва', - 'name_saved' => 'Автоматизацію перейменовано.', - 'status_title' => 'Статус', - 'status_description' => 'Активуйте, щоб запустити, або призупиніть, щоб зупинити.', - 'activated_at' => 'Активовано :date', - 'paused_at' => 'Призупинено :date', - 'created_at' => 'Створено :date', - 'danger_title' => 'Небезпечна зона', - 'danger_description' => 'Незворотні дії.', - 'delete_title' => 'Видалити цю автоматизацію', - 'delete_description' => 'Назавжди видаляє автоматизацію та історію її запусків.', - ], - - 'status_run' => [ - 'pending' => 'Очікує', - 'running' => 'Виконується', - 'waiting' => 'Очікування', - 'completed' => 'Завершено', - 'failed' => 'Помилка', - 'cancelled' => 'Скасовано', - ], - - 'node_type' => [ - 'trigger' => 'Тригер', - 'generate' => 'Генерація контенту', - 'delay' => 'Затримка', - 'condition' => 'Умова', - 'publish' => 'Публікація', - 'end' => 'Кінець', - 'fetch_rss' => 'Отримати RSS', - 'http_request' => 'HTTP-запит', - ], - - 'invocations' => [ - 'empty' => 'Запусків ще немає.', - 'refresh' => 'Оновити', - 'search_placeholder' => 'Пошук за ID запуску…', - 'copied' => 'ID запуску скопійовано.', - 'loading' => 'Завантаження кроків…', - 'no_steps' => 'Кроки не записані.', - 'load_error' => 'Не вдалося завантажити кроки.', - 'steps' => '{0}Немає кроків|{1}:count крок|[2,*]:count кроки', - 'filter' => [ - 'all' => 'Усі статуси', - ], - 'columns' => [ - 'timestamp' => 'Час', - 'run' => 'Запуск', - 'status' => 'Статус', - 'message' => 'Останнє повідомлення', - 'duration' => 'Тривалість', - ], - 'summary' => [ - 'completed' => 'Процес завершено', - 'failed' => 'Процес завершився з помилкою', - 'running' => 'Процес виконується', - 'cancelled' => 'Процес скасовано', - 'pending' => 'Процес очікує', - ], - ], - - 'metrics' => [ - 'overview' => 'Огляд', - 'runs_over_time' => 'Запуски з часом', - 'posts_by_platform' => 'Пости за платформами', - 'no_posts' => 'За цей період не опубліковано жодного поста.', - 'cards' => [ - 'runs' => 'Усього запусків', - 'completed' => 'Завершено', - 'failed' => 'З помилкою', - 'in_progress' => 'В процесі', - 'success_rate' => 'Частка успішних', - 'avg_duration' => 'Сер. тривалість', - 'posts_created' => 'Створено постів', - ], - 'legend' => [ - 'started' => 'Розпочато', - 'completed' => 'Завершено', - 'failed' => 'З помилкою', - ], - ], - - 'categories' => [ - 'sources' => 'Джерела', - 'content' => 'Контент', - 'flow' => 'Логіка', - 'output' => 'Вивід', - ], - - 'variables' => [ - 'title' => 'Змінні процесу', - 'hint' => 'Багаторазові значення, доступні скрізь через {{ variables.KEY }}. Зберігаються в зашифрованому вигляді.', - 'empty' => 'Змінних ще немає.', - 'key' => 'Ключ', - 'value' => 'Значення', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => 'Значення', - 'add' => 'Нова змінна', - ], - - 'expr' => [ - 'trigger_event' => 'Назва події тригера', - 'trigger_fired_at' => 'Коли спрацював тригер', - 'trigger_post_id' => 'ID поста-тригера', - 'trigger_post_content' => 'Вміст поста-тригера', - 'trigger_post_status' => 'Статус поста-тригера', - 'trigger_post_scheduled_at' => 'Коли пост заплановано', - 'trigger_post_published_at' => 'Коли пост опубліковано', - 'fetched_title' => 'Заголовок отриманого елемента', - 'fetched_link' => 'Посилання отриманого елемента', - 'fetched_date' => 'Дата публікації отриманого елемента', - 'fetched_content' => 'Повний вміст отриманого елемента', - 'fetched_description' => 'Короткий опис отриманого елемента', - 'fetched_author' => 'Автор отриманого елемента', - 'fetched_image' => 'URL зображення отриманого елемента', - 'fetched_categories' => 'Категорії отриманого елемента', - 'fetched_enclosure' => 'Медіа отриманого елемента (аудіо/відео/файл)', - 'fetched_pubdate' => 'Дата публікації отриманого елемента', - 'fetched_http' => 'Отриманий HTTP-елемент (додайте поле)', - 'generated_content' => 'AI-згенерований вміст поста', - 'generated_post_url' => 'URL AI-згенерованого поста', - 'variable' => 'Змінна процесу', - 'now' => 'Поточна дата та час', - ], - - 'test' => [ - 'description' => 'Виконує автоматизацію від початку до кінця, використовуючи синтезовані дані тригера. Корисно для перевірки кожного вузла без очікування реального розкладу або стрічки.', - 'starting' => 'Запуск тестового прогону…', - 'in_progress' => 'В процесі', - 'completed' => 'Завершено', - 'failed' => 'Помилка', - 'waiting' => 'Очікування', - 'close' => 'Закрити', - 'no_node_runs' => 'Очікування запуску першого вузла…', - 'node_input' => 'Вхід', - 'node_output' => 'Вихід', - 'node_error' => 'Помилка', - 'no_new_items' => 'Немає нових елементів — наступні вузли не запускалися.', - 'error_starting' => 'Не вдалося запустити тестовий прогін.', - 'with_real_data' => 'З реальними даними', - 'run' => 'Запустити тест', - 'idle_hint' => 'Натисніть «Запустити тест», щоб виконати автоматизацію від початку до кінця.', - 'real_data_hint' => 'Цей тест опублікує пости, оновить мітки опитування та викличе зовнішні побічні ефекти.', - 'dry_badge' => 'Тестовий прогін', - ], - - 'status' => [ - 'draft' => 'Чернетка', - 'active' => 'Активна', - 'paused' => 'Призупинена', - ], - - 'index' => [ - 'empty_title' => 'Автоматизацій ще немає', - 'empty_description' => 'Створіть першу автоматизацію, щоб публікувати на автопілоті.', - 'columns' => [ - 'name' => 'Назва', - 'status' => 'Статус', - 'created' => 'Створено', - ], - ], - - 'form' => [ - 'activate_error_fallback' => 'Не вдалося активувати автоматизацію.', - 'pause_error_fallback' => 'Не вдалося призупинити автоматизацію.', - 'save_error_fallback' => 'Не вдалося зберегти автоматизацію.', - 'save_success' => 'Автоматизацію збережено.', - 'empty_canvas_title' => 'Почніть створювати автоматизацію', - 'empty_canvas_description' => 'Перетягніть вузол з лівої панелі, щоб почати.', - 'name_placeholder' => 'Автоматизація без назви', - ], - - 'nodes' => [ - 'trigger' => 'Тригер', - 'generate' => 'Генерація', - 'delay' => 'Затримка', - 'condition' => 'Умова', - 'publish' => 'Публікація', - 'end' => 'Кінець', - 'end_summary' => 'Зупиняє автоматизацію тут', - 'fetch_rss' => 'Отримати RSS', - 'http_request' => 'HTTP-запит', - 'handles' => [ - 'items' => 'є елементи', - 'no_items' => 'немає елементів', - ], - ], - - 'config' => [ - 'select_placeholder' => 'Виберіть…', - 'invalid_json' => 'Це ще не коректний JSON.', - 'expand_editor' => 'Розгорнути редактор', - 'minimize_editor' => 'Згорнути', - - 'trigger' => [ - 'type' => 'Тип тригера', - 'types' => [ - 'schedule' => 'Розклад', - 'post_published' => 'Коли пост опубліковано', - 'post_scheduled' => 'Коли пост заплановано', - ], - 'post_published_hint' => 'Запускається щоразу, коли будь-який пост у цьому робочому просторі опубліковано. Опублікований пост доступний у {{ trigger.post }} для наступних вузлів.', - 'post_scheduled_hint' => 'Запускається щоразу, коли будь-який пост у цьому робочому просторі заплановано. Запланований пост доступний у {{ trigger.post }}.', - - 'schedule' => [ - 'field' => 'Інтервал спрацювання', - 'fields' => [ - 'minutes' => 'Хвилини', - 'hours' => 'Години', - 'days' => 'Дні', - 'weeks' => 'Тижні', - 'months' => 'Місяці', - ], - 'minutes_interval' => 'Хвилин між спрацюваннями', - 'hours_interval' => 'Годин між спрацюваннями', - 'days_interval' => 'Днів між спрацюваннями', - 'hour' => 'Спрацювання о годині', - 'minute' => 'Спрацювання на хвилині', - 'weekdays' => 'Спрацювання у будні', - 'day_of_month' => 'День місяця', - 'weekday_names' => [ - 'sun' => 'Нд', - 'mon' => 'Пн', - 'tue' => 'Вт', - 'wed' => 'Ср', - 'thu' => 'Чт', - 'fri' => 'Пт', - 'sat' => 'Сб', - ], - 'summary' => [ - 'every_n_minutes' => 'Запускається щохвилини|Запускається кожні :count хвилини|Запускається кожні :count хвилин', - 'every_n_hours' => 'Запускається щогодини на :minute-й хвилині|Запускається кожні :count години на :minute-й хвилині|Запускається кожні :count годин на :minute-й хвилині', - 'every_n_days' => 'Запускається щодня о :time|Запускається кожні :count дні о :time|Запускається кожні :count днів о :time', - 'weekly' => 'Запускається у :days о :time', - 'monthly' => 'Запускається :day числа кожного місяця о :time', - ], - ], - ], - 'generate' => [ - 'social_accounts' => 'Соціальні акаунти', - 'social_accounts_empty' => 'Немає підключених соціальних акаунтів. Спочатку підключіть один.', - 'target_slide_count' => 'Слайдів для генерації', - 'prompt_template' => 'Шаблон промпту', - 'prompt_template_hint' => 'Введіть {{, щоб вставити дані з попередніх кроків.', - 'image_count' => 'Зображень для генерації', - 'image_count_hint' => '0 = лише текст (без зображення). 1 = одне зображення. 2+ = карусель.', - 'use_brand_voice' => 'Використовувати голос бренду', - 'use_brand_voice_hint' => 'Застосовуйте опис і голос вашого бренду. Вимкніть для точної курації сторонніх джерел (новини, RSS).', - 'use_brand_visuals' => 'Використовувати візуал бренду', - 'use_brand_visuals_hint' => 'Спрямовуйте AI-зображення кольорами та айдентикою бренду. Вимкніть для нейтральних зображень лише за темою поста.', - 'style' => 'Стиль', - 'account_summary' => ':count акаунт · :format|:count акаунти · :format|:count акаунтів · :format', - 'formats' => [ - 'single' => 'один', - 'carousel' => 'карусель', - ], - ], - 'delay' => [ - 'duration' => 'Тривалість', - 'unit' => 'Одиниця', - 'units' => [ - 'minutes' => 'Хвилини', - 'hours' => 'Години', - 'days' => 'Дні', - ], - ], - 'condition' => [ - 'field' => 'Поле', - 'operator' => 'Оператор', - 'operators' => [ - 'contains' => 'містить', - 'not_contains' => 'не містить', - 'equals' => 'дорівнює', - 'not_equals' => 'не дорівнює', - 'matches' => 'відповідає (regex)', - 'greater_than' => 'більше ніж', - 'less_than' => 'менше ніж', - ], - 'value' => 'Значення', - ], - 'publish' => [ - 'mode' => 'Режим', - 'modes' => [ - 'now' => 'Опублікувати зараз', - 'scheduled' => 'Запланувати', - 'draft' => 'Зберегти як чернетку', - ], - 'scheduled_offset' => 'Зміщення від тригера (хвилини)', - 'offset_summary' => ':mode · +:offset хв', - ], - 'end' => [ - 'reason' => 'Причина (необов’язково)', - 'reason_placeholder' => 'напр. Відфільтровано умовою', - ], - 'fetch_rss' => [ - 'feed_url' => 'URL стрічки', - 'feed_url_hint' => 'При першому запуску мітку встановлюють на «зараз», щоб історичні елементи не заповнили наступні вузли. Наступні запуски бачать лише елементи новіші за попереднє опитування.', - 'inspect' => 'Перевірити стрічку', - 'inspecting' => 'Перевірка…', - 'inspect_hint' => 'Отримайте зразок, щоб дізнатися доступні поля для наступних вузлів.', - 'inspect_error' => 'Не вдалося прочитати цю стрічку. Перевірте URL і спробуйте ще раз.', - 'discovered_fields' => 'Доступні поля', - 'discovered_empty' => 'У останньому елементі поля не знайдено.', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => 'Метод', - 'auth_type' => 'Автентифікація', - 'auth' => [ - 'none' => 'Немає (публічний)', - 'bearer' => 'Bearer-токен', - 'basic' => 'Basic-автентифікація', - 'api_key' => 'Заголовок з API-ключем', - ], - 'bearer_token' => 'Bearer-токен', - 'basic_username' => 'Ім’я користувача', - 'basic_password' => 'Пароль', - 'api_key_header' => 'Назва заголовка', - 'api_key_value' => 'API-ключ', - 'body_template' => 'Шаблон тіла (JSON)', - 'headers' => 'Заголовки', - 'header_name' => 'Назва заголовка', - 'header_value' => 'Значення', - 'add_header' => 'Додати заголовок', - 'polling_section' => 'Список і дедуплікація (необов’язково)', - 'polling_hint' => 'Якщо відповідь — список, кожен елемент запускає процес окремо. Один об’єкт запускає його один раз.', - 'items_path' => 'Шлях до елементів', - 'items_path_hint' => 'Залиште порожнім, якщо відповідь уже масив. Використовуйте шлях через крапку (напр. data.items) для вкладеного масиву або * для об’єкта з ключами за id.', - 'item_key_path' => 'Шлях до ключа елемента', - 'item_key_path_hint' => 'JSON-шлях до унікального id (напр. id). Вже бачені елементи пропускаються, тож стрічка без дат все одно передає лише нові записи.', - 'item_date_path' => 'Шлях до дати елемента', - 'item_date_path_hint' => 'JSON-шлях до мітки часу елемента (напр. published_at). Переважніший за шлях ключа, коли доступний. Перше опитування фіксує базову мітку і нічого не передає, тож існуюча стрічка не заповнить систему в перший день.', - ], - ], - - 'delete' => [ - 'title' => 'Видалити автоматизацію', - 'description' => 'Ви впевнені, що хочете видалити цю автоматизацію? Усі запуски та елементи тригерів також буде видалено. Цю дію не можна скасувати.', - 'confirm' => 'Видалити', - 'cancel' => 'Скасувати', - ], - - 'flash' => [ - 'deleted' => 'Автоматизацію успішно видалено!', - ], - - 'errors' => [ - 'no_active_social_accounts' => 'Для цієї автоматизації не налаштовано активних соціальних акаунтів.', - 'must_have_one_trigger' => 'Автоматизація має містити рівно один вузол-тригер.', - 'trigger_must_be_connected' => 'Вузол-тригер має бути з’єднаний хоча б з одним вузлом.', - 'graph_contains_cycle' => 'Граф автоматизації містить цикл.', - 'only_failed_can_retry' => 'Повторити можна лише запуски з помилкою.', - 'no_generated_post' => 'У запуску не знайдено згенерованого поста.', - 'url_not_allowed' => 'URL запиту вказує на приватну або недоступну адресу і було заблоковано.', - 'node_no_longer_exists' => 'Вузол :node_id більше не існує в автоматизації.', - 'no_trigger_connection' => 'До вузла-тригера не підключено жодного вузла.', - 'fetch_rss_missing_url' => 'У вузлі «Отримати RSS» відсутній URL стрічки.', - 'fetch_rss_request_failed' => 'Запит RSS-стрічки не вдався.', - 'fetch_rss_malformed' => 'RSS-стрічка має некоректний формат.', - 'http_missing_url' => 'У вузлі HTTP-запиту відсутній URL.', - 'http_request_exception' => 'HTTP-запит викинув виняток.', - 'http_request_failed' => 'HTTP-запит не вдався.', - 'http_items_path_not_array' => 'Шлях до елементів не повернув список.', - 'generate_image_format_required' => 'AI генерує лише зображення. Виберіть формат зображення (не відео).', - ], -]; diff --git a/lang/uk/common.php b/lang/uk/common.php index 6887d706..6767eb3c 100644 --- a/lang/uk/common.php +++ b/lang/uk/common.php @@ -6,8 +6,6 @@ 'back' => 'Назад', - 'beta' => 'Бета', - 'confirm_modal' => [ 'cannot_be_undone' => 'Цю дію не можна скасувати.', 'type' => 'Введіть', diff --git a/lang/uk/sidebar.php b/lang/uk/sidebar.php index a668212d..6a91ddcd 100644 --- a/lang/uk/sidebar.php +++ b/lang/uk/sidebar.php @@ -26,7 +26,6 @@ 'others' => 'Інше', ], 'analytics' => 'Аналітика', - 'automations' => 'Автоматизації', 'onboarding' => 'Початок роботи', 'onboarding_hint' => 'Завершіть налаштування', 'posts' => [ diff --git a/lang/zh/automations.php b/lang/zh/automations.php deleted file mode 100644 index 3f41f389..00000000 --- a/lang/zh/automations.php +++ /dev/null @@ -1,402 +0,0 @@ - '自动化编辑器在更大的屏幕上体验最佳。请在电脑上打开以搭建你的工作流。', - 'title' => '自动化', - 'default_name' => '新建自动化', - - 'actions' => [ - 'new' => '新建自动化', - 'edit' => '编辑', - 'save' => '保存', - 'activate' => '启用', - 'pause' => '暂停', - 'delete' => '删除', - 'retry' => '重试', - 'guide' => '了解运作方式', - ], - - 'tabs' => [ - 'build' => '搭建', - 'variables' => '变量', - 'test' => '测试', - ], - - 'nav' => [ - 'workflow' => '工作流', - 'invocations' => '执行记录', - 'metrics' => '指标', - 'settings' => '设置', - ], - - 'settings' => [ - 'general' => '常规', - 'general_description' => '重命名此自动化。', - 'name_label' => '名称', - 'name_saved' => '自动化已重命名。', - 'status_title' => '状态', - 'status_description' => '启用即开始运行,暂停即停止。', - 'activated_at' => '于 :date 启用', - 'paused_at' => '于 :date 暂停', - 'created_at' => '于 :date 创建', - 'danger_title' => '危险区域', - 'danger_description' => '不可逆的操作。', - 'delete_title' => '删除此自动化', - 'delete_description' => '永久删除该自动化及其运行历史。', - ], - - 'status_run' => [ - 'pending' => '等待中', - 'running' => '运行中', - 'waiting' => '等待中', - 'completed' => '已完成', - 'failed' => '已失败', - 'cancelled' => '已取消', - ], - - 'node_type' => [ - 'trigger' => '触发器', - 'generate' => '生成内容', - 'delay' => '延迟', - 'condition' => '条件', - 'publish' => '发布', - 'end' => '结束', - 'fetch_rss' => '抓取 RSS', - 'http_request' => 'HTTP 请求', - ], - - 'invocations' => [ - 'empty' => '暂无执行记录。', - 'refresh' => '刷新', - 'search_placeholder' => '按运行 ID 搜索…', - 'copied' => '运行 ID 已复制。', - 'loading' => '正在加载步骤…', - 'no_steps' => '未记录任何步骤。', - 'load_error' => '无法加载步骤。', - 'steps' => '{0}无步骤|{1}:count 个步骤|[2,*]:count 个步骤', - 'filter' => [ - 'all' => '所有状态', - ], - 'columns' => [ - 'timestamp' => '时间戳', - 'run' => '运行', - 'status' => '状态', - 'message' => '最后一条消息', - 'duration' => '时长', - ], - 'summary' => [ - 'completed' => '工作流已完成', - 'failed' => '工作流已失败', - 'running' => '工作流运行中', - 'cancelled' => '工作流已取消', - 'pending' => '工作流等待中', - ], - ], - - 'metrics' => [ - 'overview' => '概览', - 'runs_over_time' => '运行次数趋势', - 'posts_by_platform' => '各平台帖子数', - 'no_posts' => '此时间段内未发布任何帖子。', - 'cards' => [ - 'runs' => '总运行次数', - 'completed' => '已完成', - 'failed' => '已失败', - 'in_progress' => '进行中', - 'success_rate' => '成功率', - 'avg_duration' => '平均时长', - 'posts_created' => '已创建帖子数', - ], - 'legend' => [ - 'started' => '已开始', - 'completed' => '已完成', - 'failed' => '已失败', - ], - ], - - 'categories' => [ - 'sources' => '数据源', - 'content' => '内容', - 'flow' => '流程', - 'output' => '输出', - ], - - 'variables' => [ - 'title' => '工作流变量', - 'hint' => '可在任意位置通过 {{ variables.KEY }} 引用的可复用值。加密存储。', - 'empty' => '暂无变量。', - 'key' => '键', - 'value' => '值', - 'key_placeholder' => 'API_KEY', - 'value_placeholder' => '值', - 'add' => '新建变量', - ], - - 'expr' => [ - 'trigger_event' => '触发事件名称', - 'trigger_fired_at' => '触发器触发时间', - 'trigger_post_id' => '触发帖子 ID', - 'trigger_post_content' => '触发帖子内容', - 'trigger_post_status' => '触发帖子状态', - 'trigger_post_scheduled_at' => '帖子的排期时间', - 'trigger_post_published_at' => '帖子的发布时间', - 'fetched_title' => '抓取条目的标题', - 'fetched_link' => '抓取条目的链接', - 'fetched_date' => '抓取条目的发布日期', - 'fetched_content' => '抓取条目的完整内容', - 'fetched_description' => '抓取条目的摘要', - 'fetched_author' => '抓取条目的作者', - 'fetched_image' => '抓取条目的图片 URL', - 'fetched_categories' => '抓取条目的分类', - 'fetched_enclosure' => '抓取条目的媒体(音频/视频/文件)', - 'fetched_pubdate' => '抓取条目的发布日期', - 'fetched_http' => '抓取的 HTTP 条目(追加一个字段)', - 'generated_content' => 'AI 生成的帖子内容', - 'generated_post_url' => 'AI 生成的帖子 URL', - 'variable' => '工作流变量', - 'now' => '当前日期和时间', - ], - - 'test' => [ - 'description' => '使用合成的触发载荷端到端地运行该自动化。可用于验证每个节点,无需等待真实的排期或订阅源。', - 'starting' => '正在启动测试运行…', - 'in_progress' => '进行中', - 'completed' => '已完成', - 'failed' => '已失败', - 'waiting' => '等待中', - 'close' => '关闭', - 'no_node_runs' => '正在等待第一个节点开始…', - 'node_input' => '输入', - 'node_output' => '输出', - 'node_error' => '错误', - 'no_new_items' => '没有新条目——后续节点未运行。', - 'error_starting' => '无法启动测试运行。', - 'with_real_data' => '使用真实数据', - 'run' => '运行测试', - 'idle_hint' => '点击“运行测试”即可端到端执行该自动化。', - 'real_data_hint' => '此测试将会发布帖子、推进轮询水位线,并触发外部副作用。', - 'dry_badge' => '试运行', - ], - - 'status' => [ - 'draft' => '草稿', - 'active' => '已启用', - 'paused' => '已暂停', - ], - - 'index' => [ - 'empty_title' => '暂无自动化', - 'empty_description' => '创建你的第一个自动化,让发帖自动运行。', - 'columns' => [ - 'name' => '名称', - 'status' => '状态', - 'created' => '创建时间', - ], - ], - - 'form' => [ - 'activate_error_fallback' => '无法启用自动化。', - 'pause_error_fallback' => '无法暂停自动化。', - 'save_error_fallback' => '无法保存自动化。', - 'save_success' => '自动化已保存。', - 'empty_canvas_title' => '开始搭建你的自动化', - 'empty_canvas_description' => '从左侧面板拖入一个节点即可开始。', - 'name_placeholder' => '未命名自动化', - ], - - 'nodes' => [ - 'trigger' => '触发器', - 'generate' => '生成', - 'delay' => '延迟', - 'condition' => '条件', - 'publish' => '发布', - 'end' => '结束', - 'end_summary' => '在此停止自动化', - 'fetch_rss' => '抓取 RSS', - 'http_request' => 'HTTP 请求', - 'handles' => [ - 'items' => '有条目', - 'no_items' => '无条目', - ], - ], - - 'config' => [ - 'select_placeholder' => '选择…', - 'invalid_json' => '这还不是有效的 JSON。', - 'expand_editor' => '展开编辑器', - 'minimize_editor' => '最小化', - - 'trigger' => [ - 'type' => '触发类型', - 'types' => [ - 'schedule' => '定时', - 'post_published' => '当帖子被发布时', - 'post_scheduled' => '当帖子被排期时', - ], - 'post_published_hint' => '每当此工作区中的任意帖子被发布时运行。已发布的帖子将在 {{ trigger.post }} 处供后续节点使用。', - 'post_scheduled_hint' => '每当此工作区中的任意帖子被排期时运行。已排期的帖子可在 {{ trigger.post }} 处获取。', - - 'schedule' => [ - 'field' => '触发间隔', - 'fields' => [ - 'minutes' => '分钟', - 'hours' => '小时', - 'days' => '天', - 'weeks' => '周', - 'months' => '月', - ], - 'minutes_interval' => '每次触发间隔的分钟数', - 'hours_interval' => '每次触发间隔的小时数', - 'days_interval' => '每次触发间隔的天数', - 'hour' => '触发的小时', - 'minute' => '触发的分钟', - 'weekdays' => '在这些工作日触发', - 'day_of_month' => '每月的第几天', - 'weekday_names' => [ - 'sun' => '周日', - 'mon' => '周一', - 'tue' => '周二', - 'wed' => '周三', - 'thu' => '周四', - 'fri' => '周五', - 'sat' => '周六', - ], - 'summary' => [ - 'every_n_minutes' => '每分钟运行一次|每 :count 分钟运行一次', - 'every_n_hours' => '每小时在第 :minute 分钟运行|每 :count 小时在第 :minute 分钟运行', - 'every_n_days' => '每天在 :time 运行|每 :count 天在 :time 运行', - 'weekly' => '每 :days 在 :time 运行', - 'monthly' => '每月 :day 日在 :time 运行', - ], - ], - ], - 'generate' => [ - 'social_accounts' => '社交账号', - 'social_accounts_empty' => '没有已连接的社交账号。请先连接一个。', - 'target_slide_count' => '要生成的幻灯片数量', - 'prompt_template' => '提示词模板', - 'prompt_template_hint' => '输入 {{ 即可插入前面步骤的数据。', - 'image_count' => '要生成的图片数量', - 'image_count_hint' => '0 = 纯文字帖子(无图片)。1 = 单张图片。2 及以上 = 轮播。', - 'use_brand_voice' => '使用品牌语气', - 'use_brand_voice_hint' => '应用你的品牌描述和语气。若要忠实呈现第三方来源(新闻、RSS),请关闭。', - 'use_brand_visuals' => '使用品牌视觉', - 'use_brand_visuals_hint' => '用你的品牌色彩和形象来引导 AI 图片。若要仅根据帖子主题生成中性图像,请关闭。', - 'style' => '风格', - 'account_summary' => ':count 个账号 · :format|:count 个账号 · :format', - 'formats' => [ - 'single' => '单图', - 'carousel' => '轮播', - ], - ], - 'delay' => [ - 'duration' => '时长', - 'unit' => '单位', - 'units' => [ - 'minutes' => '分钟', - 'hours' => '小时', - 'days' => '天', - ], - ], - 'condition' => [ - 'field' => '字段', - 'operator' => '运算符', - 'operators' => [ - 'contains' => '包含', - 'not_contains' => '不包含', - 'equals' => '等于', - 'not_equals' => '不等于', - 'matches' => '匹配(正则)', - 'greater_than' => '大于', - 'less_than' => '小于', - ], - 'value' => '值', - ], - 'publish' => [ - 'mode' => '模式', - 'modes' => [ - 'now' => '立即发布', - 'scheduled' => '排期', - 'draft' => '存为草稿', - ], - 'scheduled_offset' => '相对触发的延迟(分钟)', - 'offset_summary' => ':mode · +:offset 分钟', - ], - 'end' => [ - 'reason' => '原因(可选)', - 'reason_placeholder' => '例如 被条件过滤掉', - ], - 'fetch_rss' => [ - 'feed_url' => '订阅源 URL', - 'feed_url_hint' => '首次运行时,水位线会设为“当前”,以免历史条目涌入后续节点。之后的运行只会看到比上次轮询更新的条目。', - 'inspect' => '检查订阅源', - 'inspecting' => '检查中…', - 'inspect_hint' => '抓取一份样本,以发现可在后续节点中使用的字段。', - 'inspect_error' => '无法读取此订阅源。请检查 URL 后重试。', - 'discovered_fields' => '可用字段', - 'discovered_empty' => '在最新条目中未找到任何字段。', - ], - 'http_request' => [ - 'url' => 'URL', - 'method' => '方法', - 'auth_type' => '身份验证', - 'auth' => [ - 'none' => '无(公开)', - 'bearer' => 'Bearer 令牌', - 'basic' => '基本认证', - 'api_key' => 'API 密钥请求头', - ], - 'bearer_token' => 'Bearer 令牌', - 'basic_username' => '用户名', - 'basic_password' => '密码', - 'api_key_header' => '请求头名称', - 'api_key_value' => 'API 密钥', - 'body_template' => '请求体模板(JSON)', - 'headers' => '请求头', - 'header_name' => '请求头名称', - 'header_value' => '值', - 'add_header' => '添加请求头', - 'polling_section' => '列表与去重(可选)', - 'polling_hint' => '当响应为列表时,每个条目会分别运行一次工作流。单个对象则运行一次。', - 'items_path' => '条目路径', - 'items_path_hint' => '如果响应本身已是数组,则留空。若为嵌套数组,请使用点分路径(例如 data.items);若为以 id 为键的对象,请使用 *。', - 'item_key_path' => '条目键路径', - 'item_key_path_hint' => '指向唯一 id 的 JSON 路径(例如 id)。已见过的条目会被跳过,因此即使订阅源没有日期,也只转发新条目。', - 'item_date_path' => '条目日期路径', - 'item_date_path_hint' => '指向条目时间戳的 JSON 路径(例如 published_at)。可用时优先于键路径。首次轮询会记录基准并不转发任何内容,因此现有订阅源第一天绝不会涌入。', - ], - ], - - 'delete' => [ - 'title' => '删除自动化', - 'description' => '确定要删除此自动化吗?所有运行记录和触发条目也将被移除。此操作无法撤销。', - 'confirm' => '删除', - 'cancel' => '取消', - ], - - 'flash' => [ - 'deleted' => '自动化删除成功!', - ], - - 'errors' => [ - 'no_active_social_accounts' => '此自动化未配置任何已启用的社交账号。', - 'must_have_one_trigger' => '自动化必须且只能有一个触发器节点。', - 'trigger_must_be_connected' => '触发器节点必须至少连接到一个节点。', - 'graph_contains_cycle' => '自动化流程图中包含环路。', - 'only_failed_can_retry' => '只有失败的运行才能重试。', - 'no_generated_post' => '在该运行中未找到已生成的帖子。', - 'url_not_allowed' => '请求 URL 指向私有或无法访问的地址,已被拦截。', - 'node_no_longer_exists' => '节点 :node_id 已不存在于该自动化中。', - 'no_trigger_connection' => '没有节点连接到触发器节点。', - 'fetch_rss_missing_url' => '抓取 RSS 节点缺少订阅源 URL。', - 'fetch_rss_request_failed' => 'RSS 订阅源请求失败。', - 'fetch_rss_malformed' => 'RSS 订阅源格式有误。', - 'http_missing_url' => 'HTTP 请求节点缺少 URL。', - 'http_request_exception' => 'HTTP 请求抛出了异常。', - 'http_request_failed' => 'HTTP 请求失败。', - 'http_items_path_not_array' => '条目路径未解析为列表。', - 'generate_image_format_required' => 'AI 生成仅创建图片。请选择图片格式(不支持视频)。', - ], -]; diff --git a/lang/zh/common.php b/lang/zh/common.php index 7f7ec022..f570e914 100644 --- a/lang/zh/common.php +++ b/lang/zh/common.php @@ -6,8 +6,6 @@ 'back' => '返回', - 'beta' => '测试版', - 'confirm_modal' => [ 'cannot_be_undone' => '此操作无法撤销。', 'type' => '输入', diff --git a/lang/zh/sidebar.php b/lang/zh/sidebar.php index 347bea9e..0cfb6816 100644 --- a/lang/zh/sidebar.php +++ b/lang/zh/sidebar.php @@ -26,7 +26,6 @@ 'others' => '其他', ], 'analytics' => '分析', - 'automations' => '自动化', 'onboarding' => '开始使用', 'onboarding_hint' => '完成设置', 'posts' => [ diff --git a/package-lock.json b/package-lock.json index 47682541..0df5a2b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,22 +5,13 @@ "packages": { "": { "dependencies": { - "@codemirror/commands": "^6.10.3", - "@codemirror/lang-json": "^6.0.2", - "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.17", "@inertiajs/vue3": "^3.6.1", "@tabler/icons-vue": "^3.36.1", "@tailwindcss/typography": "^0.5.19", - "@vue-flow/background": "^1.3.2", - "@vue-flow/controls": "^1.1.3", - "@vue-flow/core": "^1.48.2", - "@vue-flow/minimap": "^1.5.4", "@vueuse/core": "^12.8.2", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "codemirror": "^6.0.2", "dayjs": "^1.11.19", "embla-carousel-vue": "^8.6.0", "highlight.js": "^11.11.1", @@ -43,8 +34,6 @@ "@laravel/vite-plugin-wayfinder": "^0.1.3", "@tailwindcss/vite": "^4.1.11", "@types/node": "^22.13.5", - "@unovis/ts": "^1.6.4", - "@unovis/vue": "^1.6.4", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-typescript": "^14.3.0", "chokidar": "^5.0.0", @@ -74,62 +63,6 @@ "lightningcss-win32-x64-msvc": "^1.29.1" } }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -163,50 +96,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -220,97 +109,6 @@ "node": ">=6.9.0" } }, - "node_modules/@codemirror/autocomplete": { - "version": "6.20.3", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", - "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/commands": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", - "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.6.0", - "@codemirror/view": "^6.27.0", - "@lezer/common": "^1.1.0" - } - }, - "node_modules/@codemirror/lang-json": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", - "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/json": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", - "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/lint": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", - "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.42.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/search": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", - "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.37.0", - "crelt": "^1.0.5" - } - }, - "node_modules/@codemirror/state": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", - "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", - "license": "MIT", - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.43.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.1.tgz", - "integrity": "sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.6.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -342,110 +140,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/css": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", - "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.13.5", - "@emotion/serialize": "^1.3.3", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "dev": true, - "license": "MIT" - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -837,13 +531,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/@laravel/echo-vue": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@laravel/echo-vue/-/echo-vue-2.3.0.tgz", @@ -866,118 +553,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@lezer/common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", - "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", - "license": "MIT" - }, - "node_modules/@lezer/highlight": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", - "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.3.0" - } - }, - "node_modules/@lezer/json": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", - "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", - "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", - "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", - "dev": true, - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", - "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@mapbox/mapbox-gl-supported": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", - "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", - "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@mapbox/tiny-sdf": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz", - "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", - "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/point-geometry": "~0.1.0" - } - }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", - "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", - "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", - "license": "MIT" - }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2036,331 +1611,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/d3": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", - "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/d3-axis": "*", - "@types/d3-brush": "*", - "@types/d3-chord": "*", - "@types/d3-color": "*", - "@types/d3-contour": "*", - "@types/d3-delaunay": "*", - "@types/d3-dispatch": "*", - "@types/d3-drag": "*", - "@types/d3-dsv": "*", - "@types/d3-ease": "*", - "@types/d3-fetch": "*", - "@types/d3-force": "*", - "@types/d3-format": "*", - "@types/d3-geo": "*", - "@types/d3-hierarchy": "*", - "@types/d3-interpolate": "*", - "@types/d3-path": "*", - "@types/d3-polygon": "*", - "@types/d3-quadtree": "*", - "@types/d3-random": "*", - "@types/d3-scale": "*", - "@types/d3-scale-chromatic": "*", - "@types/d3-selection": "*", - "@types/d3-shape": "*", - "@types/d3-time": "*", - "@types/d3-time-format": "*", - "@types/d3-timer": "*", - "@types/d3-transition": "*", - "@types/d3-zoom": "*" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-axis": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", - "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-brush": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", - "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-chord": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", - "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-collection": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.13.tgz", - "integrity": "sha512-v0Rgw3IZebRyamcwVmtTDCZ8OmQcj4siaYjNc7wGMZT7PmdSHawGsCOQMxyLvZ7lWjfohYLK0oXtilMOMgfY8A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-contour": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", - "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-array": "*", - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-dispatch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", - "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-drag": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", - "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-dsv": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", - "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-fetch": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", - "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-dsv": "*" - } - }, - "node_modules/@types/d3-force": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", - "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-format": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", - "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/d3-hierarchy": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", - "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-polygon": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", - "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-quadtree": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", - "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-random": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", - "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-sankey": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/d3-sankey/-/d3-sankey-0.12.5.tgz", - "integrity": "sha512-/3RZSew0cLAtzGQ+C89hq/Rp3H20QJuVRSqFy6RKLe7E0B8kd2iOS1oBsodrgds4PcNVpqWhdUEng/SHvBcJ6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-shape": "^1" - } - }, - "node_modules/@types/d3-sankey/node_modules/@types/d3-path": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.11.tgz", - "integrity": "sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-sankey/node_modules/@types/d3-shape": { - "version": "1.3.12", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.12.tgz", - "integrity": "sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-path": "^1" - } - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-selection": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-time-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", - "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/d3-transition": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", - "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-selection": "*" - } - }, - "node_modules/@types/d3-zoom": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", - "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/d3-interpolate": "*", - "@types/d3-selection": "*" - } - }, - "node_modules/@types/dagre": { - "version": "0.7.54", - "resolved": "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.54.tgz", - "integrity": "sha512-QjcRY+adGbYvBFS7cwv5txhVIwX1XXIUswWl+kSQTbI6NjgZydrZkEKX/etzVd7i+bCsCb40Z/xlBY5eoFuvWQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2368,13 +1618,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2389,35 +1632,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/leaflet": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.6.tgz", - "integrity": "sha512-Emkz3V08QnlelSbpT46OEAx+TBZYTOX2r1yM7W+hWg5+djHtQ1GbEXBDRLaqQDOYcDI51Ss0ayoqoKD4CtLUDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", - "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", - "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, "node_modules/@types/node": { "version": "22.19.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.6.tgz", @@ -2427,101 +1641,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", - "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/supercluster": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-5.0.3.tgz", - "integrity": "sha512-XMSqQEr7YDuNtFwSgaHHOjsbi0ZGL62V9Js4CW45RBuRYlNWSW/KDqN+RFFE7HdHcGhJPtN0klKvw06r9Kg7rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/three": { - "version": "0.135.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.135.0.tgz", - "integrity": "sha512-l7WLhIHjhHMtlpyTSltPPAKLpiMwgMD1hXHj59AVUpYRoZP7Fd9NNOSRSvZBCPLpTHPYojgQvSJCoza9zoL7bg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/throttle-debounce": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz", - "integrity": "sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/topojson": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@types/topojson/-/topojson-3.2.6.tgz", - "integrity": "sha512-ppfdlxjxofWJ66XdLgIlER/85RvpGyfOf8jrWf+3kVIjEatFxEZYD/Ea83jO672Xu1HRzd/ghwlbcZIUNHTskw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/topojson-client": "*", - "@types/topojson-server": "*", - "@types/topojson-simplify": "*", - "@types/topojson-specification": "*" - } - }, - "node_modules/@types/topojson-client": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@types/topojson-client/-/topojson-client-3.1.5.tgz", - "integrity": "sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/topojson-specification": "*" - } - }, - "node_modules/@types/topojson-server": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/topojson-server/-/topojson-server-3.0.4.tgz", - "integrity": "sha512-5+ieK8ePfP+K2VH6Vgs1VCt+fO1U8XZHj0UsF+NktaF0DavAo1q3IvCBXgokk/xmtvoPltSUs6vxuR/zMdOE1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/topojson-specification": "*" - } - }, - "node_modules/@types/topojson-simplify": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/topojson-simplify/-/topojson-simplify-3.0.3.tgz", - "integrity": "sha512-sBO5UZ0O2dB0bNwo0vut2yLHhj3neUGi9uL7/ROdm8Gs6dtt4jcB9OGDKr+M2isZwQM2RuzVmifnMZpxj4IGNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/topojson-specification": "*" - } - }, - "node_modules/@types/topojson-specification": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/topojson-specification/-/topojson-specification-1.0.5.tgz", - "integrity": "sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -2778,129 +1897,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@unovis/dagre-layout": { - "version": "0.8.8-2", - "resolved": "https://registry.npmjs.org/@unovis/dagre-layout/-/dagre-layout-0.8.8-2.tgz", - "integrity": "sha512-ZfDvfcYtzzhZhgKZty8XDi+zQIotfRqfNVF5M3dFQ9d9C5MTaRdbeBnPUkNrmlLJGgQ42HMOE2ajZLfm2VlRhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@unovis/graphlibrary": "^2.2.0-2", - "lodash-es": "^4.17.21" - } - }, - "node_modules/@unovis/graphlibrary": { - "version": "2.2.0-2", - "resolved": "https://registry.npmjs.org/@unovis/graphlibrary/-/graphlibrary-2.2.0-2.tgz", - "integrity": "sha512-HeEzpd/vDyWiIJt0rnh+2ICXUIuF2N0+Z9OJJiKg0DB+eFUcD+bk+9QPhYHwkFwfxdjDA9fHi1DZ/O/bbV58Nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21" - } - }, - "node_modules/@unovis/ts": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@unovis/ts/-/ts-1.6.4.tgz", - "integrity": "sha512-LH8AqYuiVxMcm/SP/VsBKfBa6tu37CJapcn8qeRATZvtYuh8RBDnXr3ejwJyEUvIYJzbPuHOEQo9WIDre9CK1Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@emotion/css": "^11.7.1", - "@juggle/resize-observer": "^3.3.1", - "@types/d3": "^7.4.0", - "@types/d3-array": "~3.2.2", - "@types/d3-axis": "~3.0.6", - "@types/d3-brush": "~3.0.6", - "@types/d3-chord": "~3.0.6", - "@types/d3-collection": "^1.0.10", - "@types/d3-color": "~3.1.3", - "@types/d3-drag": "~3.0.7", - "@types/d3-ease": "~3.0.2", - "@types/d3-force": "~3.0.10", - "@types/d3-geo": "~3.1.0", - "@types/d3-hierarchy": "~3.1.7", - "@types/d3-interpolate": "~3.0.4", - "@types/d3-path": "~3.1.1", - "@types/d3-sankey": "^0.12.4", - "@types/d3-scale": "~4.0.9", - "@types/d3-selection": "~3.0.0", - "@types/d3-shape": "~3.1.7", - "@types/d3-timer": "~3.0.2", - "@types/d3-transition": "~3.0.9", - "@types/d3-zoom": "~3.0.8", - "@types/dagre": "^0.7.50", - "@types/geojson": "^7946.0.8", - "@types/leaflet": "1.7.6", - "@types/supercluster": "^5.0.2", - "@types/three": "^0.135.0", - "@types/throttle-debounce": "^5.0.0", - "@types/topojson": "^3.2.3", - "@types/topojson-client": "^3.0.0", - "@types/topojson-specification": "^1.0.2", - "@unovis/dagre-layout": "0.8.8-2", - "@unovis/graphlibrary": "2.2.0-2", - "d3": "^7.2.1", - "d3-array": "~3", - "d3-axis": "~3", - "d3-brush": "~3", - "d3-chord": "~3", - "d3-collection": "^1.0.7", - "d3-color": "~3", - "d3-drag": "~3", - "d3-ease": "~3", - "d3-force": "~3", - "d3-geo": "~3", - "d3-geo-projection": "^4.0.0", - "d3-hierarchy": "~3", - "d3-interpolate": "~3", - "d3-interpolate-path": "^2.2.3", - "d3-path": "~3", - "d3-sankey": "^0.12.3", - "d3-scale": "~4", - "d3-selection": "~3", - "d3-shape": "~3", - "d3-timer": "~3", - "d3-transition": "~3", - "d3-zoom": "~3", - "elkjs": "^0.10.0", - "geojson": "^0.5.0", - "leaflet": "1.7.1", - "maplibre-gl": "^2.1.9", - "striptags": "^3.2.0", - "supercluster": "^7.1.5", - "three": "^0.135.0", - "throttle-debounce": "^5.0.0", - "topojson-client": "^3.1.0", - "tslib": "^2.3.1", - "typescript": "~4.2.4" - } - }, - "node_modules/@unovis/ts/node_modules/typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/@unovis/vue": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@unovis/vue/-/vue-1.6.4.tgz", - "integrity": "sha512-Gt5LwmwiMoB0/f1eJL29sfKD9jzlqgTHxc+lW4rMFqIG/PpHLpWL2jTJYVVFfWFd1MjTJWXYiDA06hCEjFcrAg==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "@unovis/ts": "1.6.4", - "vue": "^3" - } - }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", @@ -3216,150 +2212,6 @@ "vscode-uri": "^3.0.8" } }, - "node_modules/@vue-flow/background": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@vue-flow/background/-/background-1.3.2.tgz", - "integrity": "sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==", - "license": "MIT", - "peerDependencies": { - "@vue-flow/core": "^1.23.0", - "vue": "^3.3.0" - } - }, - "node_modules/@vue-flow/controls": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@vue-flow/controls/-/controls-1.1.3.tgz", - "integrity": "sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==", - "license": "MIT", - "peerDependencies": { - "@vue-flow/core": "^1.23.0", - "vue": "^3.3.0" - } - }, - "node_modules/@vue-flow/core": { - "version": "1.48.2", - "resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.48.2.tgz", - "integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==", - "license": "MIT", - "dependencies": { - "@vueuse/core": "^10.5.0", - "d3-drag": "^3.0.0", - "d3-interpolate": "^3.0.1", - "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0" - }, - "peerDependencies": { - "vue": "^3.3.0" - } - }, - "node_modules/@vue-flow/core/node_modules/@types/web-bluetooth": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", - "license": "MIT" - }, - "node_modules/@vue-flow/core/node_modules/@vueuse/core": { - "version": "10.11.1", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", - "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", - "license": "MIT", - "dependencies": { - "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.11.1", - "@vueuse/shared": "10.11.1", - "vue-demi": ">=0.14.8" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vue-flow/core/node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@vue-flow/core/node_modules/@vueuse/metadata": { - "version": "10.11.1", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz", - "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vue-flow/core/node_modules/@vueuse/shared": { - "version": "10.11.1", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz", - "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", - "license": "MIT", - "dependencies": { - "vue-demi": ">=0.14.8" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vue-flow/core/node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/@vue-flow/minimap": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@vue-flow/minimap/-/minimap-1.5.4.tgz", - "integrity": "sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==", - "license": "MIT", - "dependencies": { - "d3-selection": "^3.0.0", - "d3-zoom": "^3.0.0" - }, - "peerDependencies": { - "@vue-flow/core": "^1.23.0", - "vue": "^3.3.0" - } - }, "node_modules/@vue/compiler-core": { "version": "3.5.26", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", @@ -3831,22 +2683,6 @@ "proxy-from-env": "^2.1.0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4025,21 +2861,6 @@ "node": ">=6" } }, - "node_modules/codemirror": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", - "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4072,16 +2893,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4127,13 +2938,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, "node_modules/core-js": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", @@ -4145,39 +2949,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", - "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4192,13 +2963,6 @@ "node": ">= 8" } }, - "node_modules/csscolorparser": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", - "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", - "dev": true, - "license": "MIT" - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4217,510 +2981,6 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "dev": true, - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "dev": true, - "license": "ISC", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo-projection": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", - "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", - "dev": true, - "license": "ISC", - "dependencies": { - "commander": "7", - "d3-array": "1 - 3", - "d3-geo": "1.12.0 - 3" - }, - "bin": { - "geo2svg": "bin/geo2svg.js", - "geograticule": "bin/geograticule.js", - "geoproject": "bin/geoproject.js", - "geoquantize": "bin/geoquantize.js", - "geostitch": "bin/geostitch.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate-path": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz", - "integrity": "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", - "dev": true, - "license": "ISC" - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dev": true, - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "license": "ISC", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -4854,16 +3114,6 @@ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", "license": "MIT" }, - "node_modules/delaunator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", - "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4918,20 +3168,6 @@ "node": ">= 0.4" } }, - "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/elkjs": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.10.2.tgz", - "integrity": "sha512-Yx3ORtbAFrXelYkAy2g0eYyVY8QG0XEmGdQXmy0eithKKjbWRfl3Xe884lfkszfBF6UKyIy4LwfcZ3AZc8oxFw==", - "dev": true, - "license": "EPL-2.0" - }, "node_modules/embla-carousel": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", @@ -5019,16 +3255,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -5814,13 +4040,6 @@ "node": ">=8" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true, - "license": "MIT" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5975,23 +4194,6 @@ "node": ">= 0.4" } }, - "node_modules/geojson": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", - "integrity": "sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/geojson-vt": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", - "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", - "dev": true, - "license": "ISC" - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6052,19 +4254,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -6096,13 +4285,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/gl-matrix": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", - "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", - "dev": true, - "license": "MIT" - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6116,34 +4298,6 @@ "node": ">=10.13.0" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -6316,40 +4470,6 @@ "node": ">= 6" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6387,13 +4507,6 @@ "node": ">=0.8.19" } }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -6409,16 +4522,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -6437,13 +4540,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -6851,13 +4947,6 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -6871,19 +4960,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -6891,13 +4967,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6925,13 +4994,6 @@ "json5": "lib/cli.js" } }, - "node_modules/kdbush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", - "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==", - "dev": true, - "license": "ISC" - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6942,16 +5004,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/laravel-echo": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.3.0.tgz", @@ -7019,13 +5071,6 @@ "vue": "^3.5.13" } }, - "node_modules/leaflet": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", - "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7289,13 +5334,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -7319,13 +5357,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", - "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7348,40 +5379,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/maplibre-gl": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.4.0.tgz", - "integrity": "sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==", - "dev": true, - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", - "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/mapbox-gl-supported": "^2.0.1", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.5", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", - "@mapbox/whoots-js": "^3.1.0", - "@types/geojson": "^7946.0.10", - "@types/mapbox__point-geometry": "^0.1.2", - "@types/mapbox__vector-tile": "^1.3.0", - "@types/pbf": "^3.0.2", - "csscolorparser": "~1.0.3", - "earcut": "^2.2.4", - "geojson-vt": "^3.2.1", - "gl-matrix": "^3.4.3", - "global-prefix": "^3.0.0", - "murmurhash-js": "^1.0.0", - "pbf": "^3.2.1", - "potpack": "^1.0.2", - "quickselect": "^2.0.0", - "supercluster": "^7.1.5", - "tinyqueue": "^2.0.3", - "vt-pbf": "^3.1.3" - } - }, "node_modules/maska": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/maska/-/maska-3.2.0.tgz", @@ -7481,13 +5478,6 @@ "dev": true, "license": "MIT" }, - "node_modules/murmurhash-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", - "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", - "dev": true, - "license": "MIT" - }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -7726,25 +5716,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -7778,30 +5749,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ieee754": "^1.1.12", - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, "node_modules/php-parser": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/php-parser/-/php-parser-3.2.2.tgz", @@ -7946,13 +5893,6 @@ "web-vitals": "^5.1.0" } }, - "node_modules/potpack": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", - "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", - "dev": true, - "license": "ISC" - }, "node_modules/preact": { "version": "10.29.0", "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.0.tgz", @@ -8117,13 +6057,6 @@ "node": ">=12.0.0" } }, - "node_modules/protocol-buffers-schema": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", - "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", - "dev": true, - "license": "MIT" - }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -8180,13 +6113,6 @@ ], "license": "MIT" }, - "node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", - "dev": true, - "license": "ISC" - }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -8349,16 +6275,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/resolve-protobuf-schema": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", - "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "protocol-buffers-schema": "^3.3.1" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -8370,13 +6286,6 @@ "node": ">=0.10.0" } }, - "node_modules/robust-predicates": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", - "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/rolldown": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", @@ -8440,13 +6349,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -8512,13 +6414,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -8723,16 +6618,6 @@ "node": ">=10.0.0" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8882,36 +6767,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/striptags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.2.0.tgz", - "integrity": "sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==", - "dev": true, - "license": "MIT" - }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "license": "MIT" - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/supercluster": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", - "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", - "dev": true, - "license": "ISC", - "dependencies": { - "kdbush": "^3.0.0" - } - }, "node_modules/supports-color": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", @@ -8968,23 +6823,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/three": { - "version": "0.135.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.135.0.tgz", - "integrity": "sha512-kuEpuuxRzLv0MDsXai9huCxOSQPZ4vje6y0gn80SRmQvgz6/+rI0NAvCRAw56zYaWKMGMfqKWsxF9Qa2Z9xymQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/throttle-debounce": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", - "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.22" - } - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -9030,13 +6868,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinyqueue": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", - "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", - "dev": true, - "license": "ISC" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9050,28 +6881,6 @@ "node": ">=8.0" } }, - "node_modules/topojson-client": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", - "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", - "dev": true, - "license": "ISC", - "dependencies": { - "commander": "2" - }, - "bin": { - "topo2geo": "bin/topo2geo", - "topomerge": "bin/topomerge", - "topoquantize": "bin/topoquantize" - } - }, - "node_modules/topojson-client/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -9562,18 +7371,6 @@ "dev": true, "license": "MIT" }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", - "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, "node_modules/vue": { "version": "3.5.26", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz", @@ -9684,12 +7481,6 @@ "typescript": ">=5.0.0" } }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "license": "MIT" - }, "node_modules/web-vitals": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz", diff --git a/package.json b/package.json index a3c625a8..e54d3f10 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,6 @@ "@laravel/vite-plugin-wayfinder": "^0.1.3", "@tailwindcss/vite": "^4.1.11", "@types/node": "^22.13.5", - "@unovis/ts": "^1.6.4", - "@unovis/vue": "^1.6.4", "@vitejs/plugin-vue": "^6.0.0", "@vue/eslint-config-typescript": "^14.3.0", "chokidar": "^5.0.0", @@ -39,22 +37,13 @@ "vue-tsc": "^2.2.4" }, "dependencies": { - "@codemirror/commands": "^6.10.3", - "@codemirror/lang-json": "^6.0.2", - "@codemirror/state": "^6.5.4", - "@codemirror/view": "^6.39.17", "@inertiajs/vue3": "^3.6.1", "@tabler/icons-vue": "^3.36.1", "@tailwindcss/typography": "^0.5.19", - "@vue-flow/background": "^1.3.2", - "@vue-flow/controls": "^1.1.3", - "@vue-flow/core": "^1.48.2", - "@vue-flow/minimap": "^1.5.4", "@vueuse/core": "^12.8.2", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "codemirror": "^6.0.2", "dayjs": "^1.11.19", "embla-carousel-vue": "^8.6.0", "highlight.js": "^11.11.1", diff --git a/resources/css/app.css b/resources/css/app.css index eb623584..b0620446 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,7 +1,6 @@ @import 'tailwindcss'; @import 'tw-animate-css'; @import 'vue-sonner/style.css'; -@import './automations.css'; @import './json-viewer.css'; @plugin '@tailwindcss/typography'; @source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; diff --git a/resources/css/automations.css b/resources/css/automations.css deleted file mode 100644 index 469d7dc7..00000000 --- a/resources/css/automations.css +++ /dev/null @@ -1,103 +0,0 @@ -.automation-node { - position: relative; - min-width: 230px; - max-width: 260px; - background: var(--card); - border: 2px solid var(--foreground); - border-radius: 14px; - box-shadow: 3px 3px 0 var(--foreground); - transition: transform 120ms ease, box-shadow 120ms ease; -} - -.automation-node:hover { - transform: translate(-1px, -1px); - box-shadow: 4px 4px 0 var(--foreground); -} - -.automation-node--wide { - min-width: 260px; - max-width: 260px; -} - -.automation-node.is-selected { - transform: translate(-2px, -2px); - box-shadow: 5px 5px 0 #7c3aed; -} - -.automation-node__header { - display: flex; - align-items: center; - gap: 0.625rem; - padding: 0.625rem 0.875rem; - border-bottom: 2px solid var(--foreground); - border-top-left-radius: 12px; - border-top-right-radius: 12px; -} - -.automation-node__icon-tile { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border: 2px solid var(--foreground); - border-radius: 8px; - flex-shrink: 0; - transform: rotate(-3deg); -} - -.automation-node__icon-tile--violet { background: #ede9fe; color: #5b21b6; } -.automation-node__icon-tile--blue { background: #dbeafe; color: #1d4ed8; } -.automation-node__icon-tile--amber { background: #fef3c7; color: #92400e; } -.automation-node__icon-tile--rose { background: #ffe4e6; color: #be123c; } -.automation-node__icon-tile--emerald { background: #d1fae5; color: #047857; } -.automation-node__icon-tile--slate { background: #e2e8f0; color: #334155; } -.automation-node__icon-tile--zinc { background: #e4e4e7; color: #27272a; } -.automation-node__icon-tile--cyan { background: #cffafe; color: #155e75; } - -.automation-node--accent-violet .automation-node__header { background: #f5f3ff; } -.automation-node--accent-blue .automation-node__header { background: #eff6ff; } -.automation-node--accent-amber .automation-node__header { background: #fffbeb; } -.automation-node--accent-rose .automation-node__header { background: #fff1f2; } -.automation-node--accent-emerald .automation-node__header { background: #ecfdf5; } -.automation-node--accent-slate .automation-node__header { background: #f1f5f9; } -.automation-node--accent-zinc .automation-node__header { background: #f4f4f5; } -.automation-node--accent-cyan .automation-node__header { background: #ecfeff; } - -.automation-node__title { - min-width: 0; - font-weight: 700; - font-size: 0.875rem; - color: var(--foreground); - line-height: 1.2; - letter-spacing: -0.005em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.automation-node__summary { - padding: 0.625rem 0.875rem 0.75rem 0.875rem; - font-size: 0.75rem; - font-weight: 500; - color: color-mix(in srgb, var(--foreground) 70%, transparent); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - border-bottom-left-radius: 12px; - border-bottom-right-radius: 12px; -} - -.automation-node__branches { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.375rem 0.875rem 0.625rem 0.875rem; - font-size: 0.625rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.automation-node__branch--yes { color: #047857; } -.automation-node__branch--no { color: #be123c; } diff --git a/resources/js/components/AppSidebar.vue b/resources/js/components/AppSidebar.vue index 2ff7ce99..cdef840c 100644 --- a/resources/js/components/AppSidebar.vue +++ b/resources/js/components/AppSidebar.vue @@ -3,7 +3,6 @@ import { Link, usePage } from '@inertiajs/vue3'; import { IconAffiliate, IconAlertTriangle, - IconBolt, IconBrandDiscord, IconCalendar, IconChartBar, @@ -53,7 +52,6 @@ import WorkspaceMenuContent from '@/components/WorkspaceMenuContent.vue'; import { useWorkspaceRole } from '@/composables/useWorkspaceRole'; import { accounts, analytics, calendar } from '@/routes/app'; import { index as assets } from '@/routes/app/assets'; -import { index as automations } from '@/routes/app/automations'; import { portal } from '@/routes/app/billing'; import { index as labels } from '@/routes/app/labels'; import { index as mcp } from '@/routes/app/mcp'; @@ -83,7 +81,6 @@ const { canCreatePost, canManageAccounts, canManageWebhooks, - canManageAutomations, canCreateWorkspace, } = useWorkspaceRole(); const { isMobile } = useSidebar(); @@ -99,16 +96,6 @@ const mainNavItems = computed(() => [ href: analytics.url(), icon: IconChartBar, }, - ...(canManageAutomations.value - ? [ - { - title: trans('sidebar.automations'), - href: automations.url(), - icon: IconBolt, - badge: trans('common.beta'), - }, - ] - : []), ]); const postsNavItems = computed(() => [ diff --git a/resources/js/components/ChannelConfigurator.vue b/resources/js/components/ChannelConfigurator.vue index ce3d00e8..09043eb7 100644 --- a/resources/js/components/ChannelConfigurator.vue +++ b/resources/js/components/ChannelConfigurator.vue @@ -23,12 +23,10 @@ const props = withDefaults(defineProps<{ media?: MediaItem[]; videoDurationSec?: number | null; disabled?: boolean; - previewOnly?: boolean; }>(), { media: () => [], videoDurationSec: null, disabled: false, - previewOnly: false, }); const emit = defineEmits<{ @@ -118,7 +116,6 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :media="media" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:content-type="emit('update:contentType', channel.id, $event)" @update:meta="emit('update:meta', channel.id, $event)" /> @@ -129,7 +126,6 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :media="media" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:content-type="emit('update:contentType', channel.id, $event)" @update:meta="emit('update:meta', channel.id, $event)" /> @@ -143,7 +139,6 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :content-type-error="channel.contentTypeError" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:content-type="emit('update:contentType', channel.id, $event)" @update:meta="emit('update:meta', channel.id, $event)" /> @@ -156,7 +151,6 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :boards-truncated="channel.boardsTruncated ?? false" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:content-type="emit('update:contentType', channel.id, $event)" @update:meta="emit('update:meta', channel.id, $event)" /> @@ -167,7 +161,6 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :media="media" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:meta="emit('update:meta', channel.id, $event)" /> props.channels.filter((channel) => isSel :social-account="channel.socialAccount" :meta="channel.meta" :disabled="disabled" - :preview-only="previewOnly" @update:meta="emit('update:meta', channel.id, $event)" /> diff --git a/resources/js/components/CodeEditor.vue b/resources/js/components/CodeEditor.vue deleted file mode 100644 index 57a225f9..00000000 --- a/resources/js/components/CodeEditor.vue +++ /dev/null @@ -1,333 +0,0 @@ - - - diff --git a/resources/js/components/NavMain.vue b/resources/js/components/NavMain.vue index ece3b176..cb2e3724 100644 --- a/resources/js/components/NavMain.vue +++ b/resources/js/components/NavMain.vue @@ -1,7 +1,6 @@ - - diff --git a/resources/js/components/automations/AutomationDetailLayout.vue b/resources/js/components/automations/AutomationDetailLayout.vue deleted file mode 100644 index 8b9ff418..00000000 --- a/resources/js/components/automations/AutomationDetailLayout.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/resources/js/components/automations/AutomationHeader.vue b/resources/js/components/automations/AutomationHeader.vue deleted file mode 100644 index 303e36fa..00000000 --- a/resources/js/components/automations/AutomationHeader.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/resources/js/components/automations/AutomationMobileBackHeader.vue b/resources/js/components/automations/AutomationMobileBackHeader.vue deleted file mode 100644 index c12c1af4..00000000 --- a/resources/js/components/automations/AutomationMobileBackHeader.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/resources/js/components/automations/AutomationRunsChart.vue b/resources/js/components/automations/AutomationRunsChart.vue deleted file mode 100644 index dd40d05a..00000000 --- a/resources/js/components/automations/AutomationRunsChart.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - diff --git a/resources/js/components/automations/AutomationTabsNav.vue b/resources/js/components/automations/AutomationTabsNav.vue deleted file mode 100644 index d55fc370..00000000 --- a/resources/js/components/automations/AutomationTabsNav.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/resources/js/components/automations/BuildPanel.vue b/resources/js/components/automations/BuildPanel.vue deleted file mode 100644 index 92098ffa..00000000 --- a/resources/js/components/automations/BuildPanel.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - diff --git a/resources/js/components/automations/EditorSidebar.vue b/resources/js/components/automations/EditorSidebar.vue deleted file mode 100644 index b1e32edb..00000000 --- a/resources/js/components/automations/EditorSidebar.vue +++ /dev/null @@ -1,72 +0,0 @@ - - - diff --git a/resources/js/components/automations/TestRunPanel.vue b/resources/js/components/automations/TestRunPanel.vue deleted file mode 100644 index 5dbaca7b..00000000 --- a/resources/js/components/automations/TestRunPanel.vue +++ /dev/null @@ -1,236 +0,0 @@ - - - diff --git a/resources/js/components/automations/VariablesPanel.vue b/resources/js/components/automations/VariablesPanel.vue deleted file mode 100644 index aec0cc31..00000000 --- a/resources/js/components/automations/VariablesPanel.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - diff --git a/resources/js/components/automations/config/ConditionNodeConfig.vue b/resources/js/components/automations/config/ConditionNodeConfig.vue deleted file mode 100644 index 76ee5fe7..00000000 --- a/resources/js/components/automations/config/ConditionNodeConfig.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - diff --git a/resources/js/components/automations/config/DelayNodeConfig.vue b/resources/js/components/automations/config/DelayNodeConfig.vue deleted file mode 100644 index 4d58fe79..00000000 --- a/resources/js/components/automations/config/DelayNodeConfig.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - diff --git a/resources/js/components/automations/config/EndNodeConfig.vue b/resources/js/components/automations/config/EndNodeConfig.vue deleted file mode 100644 index f277366c..00000000 --- a/resources/js/components/automations/config/EndNodeConfig.vue +++ /dev/null @@ -1,34 +0,0 @@ - - -