diff --git a/app/modules/platform/src/AppPlatformServiceProvider.php b/app/modules/platform/src/AppPlatformServiceProvider.php index 6e4c4238..1f2e89b8 100644 --- a/app/modules/platform/src/AppPlatformServiceProvider.php +++ b/app/modules/platform/src/AppPlatformServiceProvider.php @@ -12,6 +12,7 @@ use Drupal\app_platform\Hook\Core\LlmsPageAttachments; use Drupal\app_platform\Hook\Theme\LibraryInfoAlter; use Drupal\app_platform\Llms\EventSubscriber\LlmsFooterSubscriber; +use Drupal\app_platform\Llms\EventSubscriber\LlmsViewSubscriber; use Drupal\app_platform\Llms\EventSubscriber\PagerLlmsSubscriber; use Drupal\app_platform\Llms\HtmlToMarkdownConverter; use Drupal\app_platform\Llms\LlmsRenderer; @@ -83,6 +84,7 @@ public function register(ContainerBuilder $container): void { // Llms. $autowire(HtmlToMarkdownConverter::class); $autowire(LlmsRenderer::class); + $autowire(LlmsViewSubscriber::class); $autowire(PagerLlmsSubscriber::class); $autowire(LlmsFooterSubscriber::class); $autowire(LlmsFormatPathProcessor::class); diff --git a/app/modules/platform/src/Hook/Core/LlmsPageAttachments.php b/app/modules/platform/src/Hook/Core/LlmsPageAttachments.php index e81c76a2..beaf6b23 100644 --- a/app/modules/platform/src/Hook/Core/LlmsPageAttachments.php +++ b/app/modules/platform/src/Hook/Core/LlmsPageAttachments.php @@ -23,14 +23,14 @@ public function __invoke(array &$attachments): void { return; } - $llmsUrl = $this->requestStack->getCurrentRequest()?->getPathInfo() . '?_wrapper_format=llms'; + $llmsUrl = $this->requestStack->getCurrentRequest()?->getPathInfo() . '?_format=llms'; $attachments['#attached']['html_head'][] = [ [ '#type' => 'html_tag', '#tag' => 'script', '#attributes' => ['type' => 'text/llms.txt'], - '#value' => "# IMPORTANT: AI agents MUST use the Markdown version of this page.\n# Append ?_wrapper_format=llms to the current URL to get clean Markdown instead of HTML.\n# DO NOT parse raw HTML — use the Markdown version.", + '#value' => "# IMPORTANT: AI agents MUST use the Markdown version of this page.\n# Append ?_format=llms to the current URL to get clean Markdown instead of HTML.\n# DO NOT parse raw HTML — use the Markdown version.", ], 'llms_script', ]; diff --git a/app/modules/platform/src/Llms/EventSubscriber/LlmsFooterSubscriber.php b/app/modules/platform/src/Llms/EventSubscriber/LlmsFooterSubscriber.php index ee96d608..4380d27d 100644 --- a/app/modules/platform/src/Llms/EventSubscriber/LlmsFooterSubscriber.php +++ b/app/modules/platform/src/Llms/EventSubscriber/LlmsFooterSubscriber.php @@ -41,7 +41,7 @@ private function buildCanonicalLink(LlmsResponseAlterEvent $event): string { $queryString = $event->request->server->get('QUERY_STRING', ''); \assert(\is_string($queryString)); \parse_str($queryString, $query); - unset($query['_wrapper_format']); + unset($query['_format']); if ($query !== []) { $url .= '?' . \http_build_query($query); diff --git a/app/modules/platform/src/Llms/EventSubscriber/LlmsViewSubscriber.php b/app/modules/platform/src/Llms/EventSubscriber/LlmsViewSubscriber.php new file mode 100644 index 00000000..958dc2b4 --- /dev/null +++ b/app/modules/platform/src/Llms/EventSubscriber/LlmsViewSubscriber.php @@ -0,0 +1,37 @@ +getRequest(); + $result = $event->getControllerResult(); + + if (!\is_array($result) || $request->getRequestFormat() !== 'llms') { + return; + } + + $response = $this->llmsRenderer->renderResponse($result, $request, $this->routeMatch); + $event->setResponse($response); + } + + #[\Override] + public static function getSubscribedEvents(): array { + return [KernelEvents::VIEW => ['onView', 100]]; + } + +} diff --git a/app/modules/platform/src/Llms/LlmsRenderer.php b/app/modules/platform/src/Llms/LlmsRenderer.php index 1cbda64b..68f681ab 100644 --- a/app/modules/platform/src/Llms/LlmsRenderer.php +++ b/app/modules/platform/src/Llms/LlmsRenderer.php @@ -12,10 +12,8 @@ use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\RouteMatchInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -#[AutoconfigureTag('render.main_content_renderer', ['format' => 'llms'])] final readonly class LlmsRenderer implements MainContentRendererInterface { public function __construct( diff --git a/app/modules/platform/src/Llms/Middleware/LlmsRequestLogger.php b/app/modules/platform/src/Llms/Middleware/LlmsRequestLogger.php index c5350a45..ed694a90 100644 --- a/app/modules/platform/src/Llms/Middleware/LlmsRequestLogger.php +++ b/app/modules/platform/src/Llms/Middleware/LlmsRequestLogger.php @@ -24,7 +24,7 @@ public function __construct( public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = TRUE): Response { $response = $this->httpKernel->handle($request, $type, $catch); - if ($type === self::MAIN_REQUEST && $request->query->get('_wrapper_format') === 'llms') { + if ($type === self::MAIN_REQUEST && $request->getRequestFormat() === 'llms') { $this->logger->info('LLMS request', [ 'path' => $request->getPathInfo(), 'user_agent' => $request->headers->get('User-Agent', ''), diff --git a/app/modules/platform/src/Llms/PathProcessor/LlmsFormatPathProcessor.php b/app/modules/platform/src/Llms/PathProcessor/LlmsFormatPathProcessor.php index eb41c200..e5135e02 100644 --- a/app/modules/platform/src/Llms/PathProcessor/LlmsFormatPathProcessor.php +++ b/app/modules/platform/src/Llms/PathProcessor/LlmsFormatPathProcessor.php @@ -21,11 +21,11 @@ public function __construct( public function processOutbound($path, &$options = [], ?Request $request = NULL, ?BubbleableMetadata $bubbleable_metadata = NULL): string { $currentRequest = $this->requestStack->getCurrentRequest(); - if ($currentRequest === NULL || $currentRequest->query->get('_wrapper_format') !== 'llms') { + if ($currentRequest === NULL || $currentRequest->getRequestFormat() !== 'llms') { return $path; } - $options['query']['_wrapper_format'] = 'llms'; + $options['query']['_format'] = 'llms'; return $path; } diff --git a/assets/scaffold/llms.txt b/assets/scaffold/llms.txt index 4acecca3..fafcae55 100644 --- a/assets/scaffold/llms.txt +++ b/assets/scaffold/llms.txt @@ -4,9 +4,9 @@ ## How to access content -Every page on this site supports a Markdown version optimized for LLMs. Append `?_wrapper_format=llms` to any page URL to get clean Markdown instead of HTML. +Every page on this site supports a Markdown version optimized for LLMs. Append `?_format=llms` to any page URL to get clean Markdown instead of HTML. -Example: `https://niklan.net/blog/123?_wrapper_format=llms` +Example: `https://niklan.net/blog/123?_format=llms` ## Pages diff --git a/config/sync/views.view.taxonomy_term.yml b/config/sync/views.view.taxonomy_term.yml deleted file mode 100644 index 0af8bf99..00000000 --- a/config/sync/views.view.taxonomy_term.yml +++ /dev/null @@ -1,320 +0,0 @@ -uuid: 3f85fa67-1e3d-4098-bd16-2f94ede56cdb -langcode: ru -status: true -dependencies: - config: - - core.entity_view_mode.node.rss - - core.entity_view_mode.node.teaser - module: - - node - - taxonomy - - user -_core: - default_config_hash: YY-QIsPmXCITvYjAnlCzaqASCB1Telev3U0IKOztZkc -id: taxonomy_term -label: 'Термин таксономии' -module: taxonomy -description: 'Материалы, относящиеся к определённому термину таксономии.' -tag: default -base_table: node_field_data -base_field: nid -display: - default: - id: default - display_title: 'По умолчанию' - display_plugin: default - position: 0 - display_options: - fields: { } - pager: - type: mini - options: - offset: 0 - pagination_heading_level: h4 - items_per_page: 10 - total_pages: 0 - id: 0 - tags: - next: ›› - previous: ‹‹ - expose: - items_per_page: false - items_per_page_label: 'Элементов на страницу' - items_per_page_options: '5, 10, 25, 50' - items_per_page_options_all: false - items_per_page_options_all_label: '- Все -' - offset: false - offset_label: Пропустить - exposed_form: - type: basic - options: - submit_button: Применить - reset_button: false - reset_button_label: Сбросить - exposed_sorts_label: 'Сортировать по' - expose_sort_order: true - sort_asc_label: 'По возрастанию' - sort_desc_label: 'По убыванию' - access: - type: perm - options: - perm: 'access content' - cache: - type: tag - options: { } - empty: { } - sorts: - sticky: - id: sticky - table: taxonomy_index - field: sticky - relationship: none - group_type: group - admin_label: '' - plugin_id: standard - order: DESC - expose: - label: '' - field_identifier: sticky - exposed: false - created: - id: created - table: taxonomy_index - field: created - relationship: none - group_type: group - admin_label: '' - plugin_id: date - order: DESC - expose: - label: '' - field_identifier: created - exposed: false - granularity: second - arguments: - tid: - id: tid - table: taxonomy_index - field: tid - relationship: none - group_type: group - admin_label: '' - plugin_id: taxonomy_index_tid - default_action: 'not found' - exception: - value: '' - title_enable: false - title: Все - title_enable: true - title: '{{ arguments.tid }}' - default_argument_type: fixed - default_argument_options: - argument: '' - summary_options: - base_path: '' - count: true - override: false - items_per_page: 25 - summary: - sort_order: asc - number_of_records: 0 - format: default_summary - specify_validation: true - validate: - type: 'entity:taxonomy_term' - fail: 'not found' - validate_options: - bundles: { } - access: true - operation: view - multiple: 0 - break_phrase: false - add_table: false - require_value: false - reduce_duplicates: false - filters: - langcode: - id: langcode - table: node_field_data - field: langcode - relationship: none - group_type: group - admin_label: '' - entity_type: node - entity_field: langcode - plugin_id: language - operator: in - value: - '***LANGUAGE_language_content***': '***LANGUAGE_language_content***' - group: 1 - exposed: false - expose: - operator_id: '' - label: '' - description: '' - use_operator: false - operator: '' - operator_limit_selection: false - operator_list: { } - identifier: '' - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - reduce: false - is_grouped: false - group_info: - label: '' - description: '' - identifier: '' - optional: true - widget: select - multiple: false - remember: false - default_group: All - default_group_multiple: { } - group_items: { } - status: - id: status - table: taxonomy_index - field: status - relationship: none - group_type: group - admin_label: '' - plugin_id: boolean - operator: '=' - value: '1' - group: 1 - exposed: false - expose: - operator_id: '' - label: '' - description: '' - use_operator: false - operator: '' - operator_limit_selection: false - operator_list: { } - identifier: '' - required: false - remember: false - multiple: false - remember_roles: - authenticated: authenticated - is_grouped: false - group_info: - label: '' - description: '' - identifier: '' - optional: true - widget: select - multiple: false - remember: false - default_group: All - default_group_multiple: { } - group_items: { } - style: - type: default - options: - grouping: { } - row_class: '' - default_row_class: true - uses_fields: false - row: - type: 'entity:node' - options: - view_mode: teaser - query: - type: views_query - options: - query_comment: '' - disable_sql_rewrite: false - distinct: false - replica: false - query_tags: { } - relationships: { } - link_display: page_1 - link_url: '' - header: - entity_taxonomy_term: - id: entity_taxonomy_term - table: views - field: entity_taxonomy_term - relationship: none - group_type: group - admin_label: '' - plugin_id: entity - empty: true - target: '{{ raw_arguments.tid }}' - view_mode: full - tokenize: true - bypass_access: false - footer: { } - display_extenders: { } - cache_metadata: - max-age: -1 - contexts: - - 'languages:language_interface' - - url - - url.query_args - - 'user.node_grants:view' - - user.permissions - tags: { } - feed_1: - id: feed_1 - display_title: Лента - display_plugin: feed - position: 2 - display_options: - pager: - type: some - options: - offset: 0 - items_per_page: 10 - style: - type: rss - options: - grouping: { } - uses_fields: false - description: '' - row: - type: node_rss - options: - relationship: none - view_mode: rss - query: - type: views_query - options: { } - display_extenders: { } - path: taxonomy/term/%/feed - displays: - page_1: page_1 - default: '0' - cache_metadata: - max-age: -1 - contexts: - - 'languages:language_interface' - - url - - 'user.node_grants:view' - - user.permissions - tags: { } - page_1: - id: page_1 - display_title: Страница - display_plugin: page - position: 1 - display_options: - query: - type: views_query - options: { } - display_extenders: { } - path: taxonomy/term/% - cache_metadata: - max-age: -1 - contexts: - - 'languages:language_interface' - - url - - url.query_args - - 'user.node_grants:view' - - user.permissions - tags: { }