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 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name T3 Chat wide code block (always expand)
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.0
  5. // @description Utilize available horizontal space in T3 Chat to make reading code blocks with very long lines on wide screens easier.
  6. // @author https://github.com/dicksonhk, https://t3.chat
  7. // @license MIT
  8. // @match https://beta.t3.chat/*
  9. // @match https://t3.chat/*
  10. // @grant GM_addStyle // Grant GM_addStyle for the first part
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. const ALWAYS_EXPAND_CODEBLOCK = true;
  18. const EXPANDED_CODEBLOCK_WIDTH = 'fit-content';
  19. const MAX_CODEBLOCK_WIDTH = '100%';
  20.  
  21. // The original max-width for the wrapper, needs to match [role="log"]'s max-width
  22. const WRAPPER_MAX_WIDTH = '48rem';
  23.  
  24. const _S_LOG_SPECIFIC = `[role="log"].max-w-3xl.px-4`;
  25. const _S_LOG_FALLBACK = `[role="log"]`;
  26. const S_LOGS = [_S_LOG_SPECIFIC, _S_LOG_FALLBACK];
  27.  
  28. const _S_MSG_GP_SPECIFIC = `${_S_LOG_SPECIFIC} > * > .max-w-full`;
  29. const _S_MSG_GP_FALLBACK = `${_S_LOG_FALLBACK} :has(> [role="article"])`;
  30. const S_MSG_GP = [_S_MSG_GP_SPECIFIC, _S_MSG_GP_FALLBACK];
  31.  
  32. // absolute and non-absolute items are styled differently
  33. const S_MSG_GP_ITEMS = S_MSG_GP.map((group) => `${group} > :not(.absolute)`);
  34. const S_MSG_GP_ABSOLUTE_ITEMS = S_MSG_GP.map((group) => `${group} > .absolute`);
  35.  
  36. const CUSTOM_CSS = `
  37. /* Remove max-width constraint wrapper full width and center children */
  38. ${[
  39. ...S_LOGS.map((log) => `${log} > *:not(:has(> .max-w-full))`),
  40. ...S_LOGS,
  41. ].join(',\n')} {
  42. max-width: 100%;
  43. align-items: center;
  44. }
  45. /* Apply full width to wrapper direct children */
  46. ${[
  47. ...S_LOGS.map((log) => `${log} > *:not(.absolute)`),
  48. ].join(',\n')} {
  49. width: 100%;
  50. }
  51. /* Apply code blocks width */
  52. ${[
  53. ...(ALWAYS_EXPAND_CODEBLOCK
  54. ? S_MSG_GP_ITEMS.map((item) => `${item} > :has(pre)`)
  55. : S_MSG_GP_ITEMS.map((item) => `${item} > :has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre)`)
  56. ),
  57. ].join(',\n')} {
  58. width: ${EXPANDED_CODEBLOCK_WIDTH};
  59. max-width: min(100%, ${MAX_CODEBLOCK_WIDTH});
  60. min-width: min(100%, 48rem);
  61. margin-left: auto;
  62. margin-right: auto;
  63. }
  64. /* Apply wrapper's original max-width to select children, except for text-wrap enabled code blocks */
  65. /* TODO: handle non top-level code blocks */
  66. ${[
  67. ...S_LOGS.map((log) => `${log} > :not(:has(> .max-w-full)):not(.absolute)`),
  68. ...S_MSG_GP_ITEMS.map((item) => `${item} > :not(:has(pre))`),
  69. ...(ALWAYS_EXPAND_CODEBLOCK
  70. ? []
  71. : S_MSG_GP_ITEMS.map((item) => `${item} > :not(:has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre))`)
  72. ),
  73. ].join(',\n')} {
  74. max-width: ${WRAPPER_MAX_WIDTH};
  75. }
  76. /* Apply full width to absolute wrappers */
  77. ${[
  78. ...S_MSG_GP_ABSOLUTE_ITEMS.map((item) => `${item}.left-0`),
  79. ].join(',\n')} {
  80. right: 0;
  81. }
  82. ${[
  83. ...S_MSG_GP_ABSOLUTE_ITEMS.map((item) => `${item}.right-0`),
  84. ].join(',\n')} {
  85. left: 0;
  86. }
  87. /* Apply wrapper's original max-width to absolutely positioned children */
  88. ${[
  89. ...S_MSG_GP_ABSOLUTE_ITEMS.flatMap((item) => [
  90. `${item}.left-0::before`,
  91. `${item}.right-0::after`,
  92. ]),
  93. ].join(',\n')} {
  94. content: '';
  95. width: max(0px, calc((100% - ${WRAPPER_MAX_WIDTH}) / 2 - .25rem));
  96. }
  97. /* Other layout fixes */
  98. ${[
  99. ...S_MSG_GP_ITEMS.map((item) => `${item}:not(.absolute)`),
  100. ].join(',\n')} {
  101. position: relative;
  102. }
  103. ${[
  104. ...S_MSG_GP_ITEMS.map((item) => `${item}.flex-col`),
  105. ].join(',\n')} {
  106. align-items: center;
  107. }
  108. ${[
  109. ...S_MSG_GP_ITEMS.map((item) => `${item}:not(.flex-col) > *`),
  110. // S_MSG_GP_ITEMS.flatMap((item) => [
  111. // `${item}:not(.flex-col) > :not(:has(pre))`,
  112. // `${item}:not(.flex-col) > :not(:has(.\\[\\&_pre\\]\\:whitespace-pre-wrap > pre))`
  113. // ]),
  114. ].join(',\n')} {
  115. margin-left: auto;
  116. margin-right: auto;
  117. }
  118. `;
  119.  
  120. injectCss(CUSTOM_CSS);
  121.  
  122. // --- Function to add CSS, checking for GM_addStyle ---
  123. function injectCss(css) {
  124. // Check if GM_addStyle is available
  125. if (typeof GM_addStyle === 'function') {
  126. GM_addStyle(css);
  127. return;
  128. }
  129.  
  130. // Inject using a style element
  131. const styleElement = document.createElement('style');
  132. styleElement.textContent = css;
  133.  
  134. // Append to head if available
  135. if (document.head) {
  136. document.head.appendChild(styleElement);
  137. return;
  138. }
  139.  
  140. // As a last resort, append to the document's root element
  141. // This is less ideal for performance but ensures the style is applied
  142. document.documentElement.appendChild(styleElement);
  143. }
  144. })();