T3 Chat wide code block (always expand)

Utilize available horizontal space in T3 Chat to make reading code blocks with very long lines on wide screens easier.

目前為 2025-05-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         T3 Chat wide code block (always expand)
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  Utilize available horizontal space in T3 Chat to make reading code blocks with very long lines on wide screens easier.
// @author       https://github.com/dicksonhk, https://t3.chat
// @license      MIT
// @match        https://beta.t3.chat/*
// @match        https://t3.chat/*
// @grant        GM_addStyle // Grant GM_addStyle for the first part
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const ALWAYS_EXPAND_CODEBLOCK = true;
  const EXPANDED_CODEBLOCK_WIDTH = 'fit-content';
  const MAX_CODEBLOCK_WIDTH = '100%';

  // The original max-width for the wrapper, needs to match [role="log"]'s max-width
  const WRAPPER_MAX_WIDTH = '48rem';

  const _S_LOG_SPECIFIC = `[role="log"].max-w-3xl.px-4`;
  const _S_LOG_FALLBACK = `[role="log"]`;
  const S_LOGS = [_S_LOG_SPECIFIC, _S_LOG_FALLBACK];

  const _S_MSG_GP_SPECIFIC = `${_S_LOG_SPECIFIC} > * > .max-w-full`;
  const _S_MSG_GP_FALLBACK = `${_S_LOG_FALLBACK} :has(> [role="article"])`;
  const S_MSG_GP = [_S_MSG_GP_SPECIFIC, _S_MSG_GP_FALLBACK];

  // absolute and non-absolute items are styled differently
  const S_MSG_GP_ITEMS = S_MSG_GP.map((group) => `${group} > :not(.absolute)`);
  const S_MSG_GP_ABSOLUTE_ITEMS = S_MSG_GP.map((group) => `${group} > .absolute`);

  const CUSTOM_CSS = `
/* Remove max-width constraint wrapper full width and center children */
${[
  ...S_LOGS.map((log) => `${log} > *:not(:has(> .max-w-full))`),
  ...S_LOGS,
].join(',\n')} {
    max-width: 100%;
    align-items: center;
}
/* Apply full width to wrapper direct children */
${[
  ...S_LOGS.map((log) => `${log} > *:not(.absolute)`),
].join(',\n')} {
    width: 100%;
}
/* Apply code blocks width */
${[
  ...(ALWAYS_EXPAND_CODEBLOCK
    ? S_MSG_GP_ITEMS.map((item) => `${item} > :has(pre)`)
    : S_MSG_GP_ITEMS.map((item) => `${item} > :has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre)`)
  ),
].join(',\n')} {
    width: ${EXPANDED_CODEBLOCK_WIDTH};
    max-width: min(100%, ${MAX_CODEBLOCK_WIDTH});
    min-width: min(100%, 48rem);
    margin-left: auto;
    margin-right: auto;
}
/* Apply wrapper's original max-width to select children, except for text-wrap enabled code blocks */
/* TODO: handle non top-level code blocks */
${[
  ...S_LOGS.map((log) => `${log} > :not(:has(> .max-w-full)):not(.absolute)`),
  ...S_MSG_GP_ITEMS.map((item) => `${item} > :not(:has(pre))`),
  ...(ALWAYS_EXPAND_CODEBLOCK
    ? []
    : S_MSG_GP_ITEMS.map((item) => `${item} > :not(:has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre))`)
  ),
].join(',\n')} {
    max-width: ${WRAPPER_MAX_WIDTH};
}
/* Apply full width to absolute wrappers */
${[
  ...S_MSG_GP_ABSOLUTE_ITEMS.map((item) => `${item}.left-0`),
].join(',\n')} {
    right: 0;
}
${[
  ...S_MSG_GP_ABSOLUTE_ITEMS.map((item) => `${item}.right-0`),
].join(',\n')} {
    left: 0;
}
/* Apply wrapper's original max-width to absolutely positioned children */
${[
  ...S_MSG_GP_ABSOLUTE_ITEMS.flatMap((item) => [
    `${item}.left-0::before`,
    `${item}.right-0::after`,
  ]),
].join(',\n')} {
    content: '';
    width: max(0px, calc((100% - ${WRAPPER_MAX_WIDTH}) / 2 - .25rem));
}
/* Other layout fixes */
${[
  ...S_MSG_GP_ITEMS.map((item) => `${item}:not(.absolute)`),
].join(',\n')} {
    position: relative;
}
${[
  ...S_MSG_GP_ITEMS.map((item) => `${item}.flex-col`),
].join(',\n')} {
    align-items: center;
}
${[
  ...S_MSG_GP_ITEMS.map((item) => `${item}:not(.flex-col) > *`),
  // S_MSG_GP_ITEMS.flatMap((item) => [
  //   `${item}:not(.flex-col) > :not(:has(pre))`,
  //   `${item}:not(.flex-col) > :not(:has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre))`
  // ]),
].join(',\n')} {
    margin-left: auto;
    margin-right: auto;
}
`;

  injectCss(CUSTOM_CSS);

  // --- Function to add CSS, checking for GM_addStyle ---
  function injectCss(css) {
    // Check if GM_addStyle is available
    if (typeof GM_addStyle === 'function') {
      GM_addStyle(css);
      return;
    }

    // Inject using a style element
    const styleElement = document.createElement('style');
    styleElement.textContent = css;

    // Append to head if available
    if (document.head) {
      document.head.appendChild(styleElement);
      return;
    }

    // As a last resort, append to the document's root element
    // This is less ideal for performance but ensures the style is applied
    document.documentElement.appendChild(styleElement);
  }
})();