Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git
M pages/FilePage.php
118118
    echo '<div class="empty-state download-state">';
119119
    echo '<p>' . htmlspecialchars( $reason ) . '</p>';
120
    echo '<a href="' . $url . '" class="btn-download">Download Raw File</a>';
120
    echo '<a href="' . $url . '" class="btn-download">Download</a>';
121121
    echo '</div>';
122122
  }
M render/Highlighter.php
77
  private array $rules;
88
9
  public function __construct(string $filename, string $content, string $mediaType) {
9
  public function __construct( string $filename, string $content, string $mediaType ) {
1010
    $this->content = $content;
1111
12
    $this->lang = $this->detectLanguage($mediaType, $filename);
13
    $this->rules = LanguageDefinitions::get($this->lang) ?? [];
12
    $this->lang = $this->detectLanguage( $mediaType, $filename );
13
    $this->rules = LanguageDefinitions::get( $this->lang ) ?? [];
1414
  }
1515
1616
  public function render(): string {
17
    if (empty($this->rules)) {
18
      return htmlspecialchars($this->content);
17
    if( empty( $this->rules ) ) {
18
      return htmlspecialchars( $this->content );
1919
    }
2020
2121
    $patterns = [];
2222
23
    foreach ($this->rules as $name => $pattern) {
23
    foreach( $this->rules as $name => $pattern ) {
2424
      $delim = $pattern[0];
25
      $inner = substr($pattern, 1, strrpos($pattern, $delim) - 1);
26
      $inner = str_replace('~', '\~', $inner);
25
      $inner = substr( $pattern, 1, strrpos( $pattern, $delim ) - 1 );
26
      $inner = str_replace( '~', '\~', $inner );
2727
2828
      $patterns[] = "(?P<{$name}>{$inner})";
2929
    }
3030
31
    if (!in_array($this->lang, ['markdown', 'rmd'])) {
31
    if( !in_array( $this->lang, ['markdown', 'rmd'] ) ) {
3232
      $patterns[] = "(?P<punctuation>[\\{\\}\\(\\)\\[\\]\\;\\,])";
3333
    }
3434
3535
    $patterns[] = "(?P<any>[\s\S])";
36
    $combined = '~' . implode('|', $patterns) . '~msu';
36
    $combined = '~' . implode( '|', $patterns ) . '~msu';
3737
38
    return preg_replace_callback($combined, function ($matches) {
39
      foreach ($matches as $key => $value) {
40
        if (!is_numeric($key) && $value !== '') {
41
          if ($key === 'any') {
42
            return htmlspecialchars($value);
38
    $result = preg_replace_callback( $combined, function( $matches ) {
39
      foreach( $matches as $key => $value ) {
40
        if( !is_numeric( $key ) && $value !== '' ) {
41
          if( $key === 'any' ) {
42
            return htmlspecialchars( $value );
4343
          }
4444
45
          if ($key === 'string_interp') {
46
            return $this->renderInterpolatedString($value);
45
          if( $key === 'string_interp' ) {
46
            return $this->renderInterpolatedString( $value );
4747
          }
4848
49
          if ($key === 'math') {
50
            return $this->renderMath($value);
49
          if( $key === 'math' ) {
50
            return $this->renderMath( $value );
5151
          }
5252
53
          return '<span class="hl-' . $key . '">' . htmlspecialchars($value) . '</span>';
53
          return '<span class="hl-' . $key . '">' . htmlspecialchars( $value ) . '</span>';
5454
        }
5555
      }
5656
57
      return htmlspecialchars($matches[0]);
58
    }, $this->content);
57
      return htmlspecialchars( $matches[0] );
58
    }, $this->content );
59
60
    return $result ?? htmlspecialchars( $this->content );
5961
  }
6062
61
  private function renderInterpolatedString(string $content): string {
63
  private function renderInterpolatedString( string $content ): string {
6264
    $pattern = '/(\$\{[a-zA-Z0-9_]+\}|\$[a-zA-Z0-9_]+)/';
63
    $parts   = preg_split($pattern, $content, -1, PREG_SPLIT_DELIM_CAPTURE);
65
    $parts   = preg_split( $pattern, $content, -1, PREG_SPLIT_DELIM_CAPTURE );
6466
    $output  = '<span class="hl-string">';
6567
66
    foreach ($parts as $part) {
67
      if ($part === '') continue;
68
    foreach( $parts as $part ) {
69
      if( $part === '' ) continue;
6870
69
      if (str_starts_with($part, '${') && str_ends_with($part, '}')) {
70
        $inner = substr($part, 2, -1);
71
      if( str_starts_with( $part, '${' ) && str_ends_with( $part, '}' ) ) {
72
        $inner = substr( $part, 2, -1 );
7173
        $output .= '<span class="hl-interp-punct">${</span>';
72
        $output .= '<span class="hl-variable">' . htmlspecialchars($inner) . '</span>';
74
        $output .= '<span class="hl-variable">' . htmlspecialchars( $inner ) . '</span>';
7375
        $output .= '<span class="hl-interp-punct">}</span>';
74
      } elseif (str_starts_with($part, '$') && strlen($part) > 1) {
76
      } elseif( str_starts_with( $part, '$' ) && strlen( $part ) > 1 ) {
7577
         $output .= '<span class="hl-interp-punct">$</span>';
76
         $output .= '<span class="hl-variable">' . htmlspecialchars(substr($part, 1)) . '</span>';
78
         $output .= '<span class="hl-variable">' . htmlspecialchars( substr( $part, 1 ) ) . '</span>';
7779
      } else {
78
        $output .= htmlspecialchars($part);
80
        $output .= htmlspecialchars( $part );
7981
      }
8082
    }
8183
8284
    $output .= '</span>';
8385
8486
    return $output;
8587
  }
8688
87
  private function renderMath(string $content): string {
88
    $parts = preg_split('/(`[^`]+`)/', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
89
  private function renderMath( string $content ): string {
90
    $parts = preg_split( '/(`[^`]+`)/', $content, -1, PREG_SPLIT_DELIM_CAPTURE );
8991
    $output = '';
9092
91
    foreach ($parts as $part) {
92
      if ($part === '') continue;
93
    foreach( $parts as $part ) {
94
      if( $part === '' ) continue;
9395
94
      if (str_starts_with($part, '`') && str_ends_with($part, '`')) {
95
        $output .= '<span class="hl-function">' . htmlspecialchars($part) . '</span>';
96
      if( str_starts_with( $part, '`' ) && str_ends_with( $part, '`' ) ) {
97
        $output .= '<span class="hl-function">' . htmlspecialchars( $part ) . '</span>';
9698
      } else {
97
        $output .= '<span class="hl-math">' . htmlspecialchars($part) . '</span>';
99
        $output .= '<span class="hl-math">' . htmlspecialchars( $part ) . '</span>';
98100
      }
99101
    }
100102
101103
    return $output;
102104
  }
103105
104
  private function detectLanguage(string $mediaType, string $filename): string {
106
  private function detectLanguage( string $mediaType, string $filename ): string {
105107
    $lang = match( $mediaType ) {
106108
      'text/x-php', 'application/x-php', 'application/x-httpd-php' => 'php',
...
125127
      'application/typescript', 'text/typescript' => 'typescript',
126128
      'text/x-gradle' => 'gradle',
129
      'text/x-tex', 'application/x-tex' => 'tex',
127130
      default => null
128131
    };
...
158161
      'yaml', 'yml' => 'yaml',
159162
      'gradle' => 'gradle',
163
      'tex', 'sty', 'cls', 'ltx' => 'tex',
160164
      default => 'text'
161165
    };
M render/LanguageDefinitions.php
11
<?php
22
class LanguageDefinitions {
3
  public static function get(string $lang): array {
3
  public static function get( string $lang ): array {
44
    $int   = '(-?\b\d+(\.\d+)?\b)';
5
    $str   = '(".*?"|\'.*?\')';
5
    $str   = '("(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\')';
66
    $float = '(-?\d+(\.\d+)?([eE][+-]?\d+)?)';
77
88
    $rules = [
99
      'gradle' => [
1010
        'comment'       => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
11
        'string_interp' => '/(".*?"|""".*?""")/',
12
        'string'        => '/(\'.*?\'|\'\'\'.*?\'\'\'|\/.*?\/)/',
13
        'keyword'       => '/\b(def|task|apply|plugin|sourceCompatibility|targetCompatibility|repositories|dependencies|test|group|version|plugins|buildscript|allprojects|subprojects|project|ext|implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|mavenCentral|google|jcenter|classpath)\b/',
14
        'function'      => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\{)/',
11
        'string_interp' => '/("(?:\\\\.|[^"\\\\])*"|""".*?""")/',
12
        'string'        => '/(\'(?:\\\\.|[^\'\\\\])*\'|\'\'\'.*?\'\'\'|\/.*?\/)/',
13
        'keyword'       => '/\b(def|task|group|version|ext|return|if|else)\b/',
14
        'function'      => '/\b(apply|plugin|sourceCompatibility|targetCompatibility|repositories|dependencies|test|plugins|buildscript|allprojects|subprojects|project|implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|mavenCentral|google|jcenter|classpath)\b|\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\(|{)/',
1515
        'variable'      => '/(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{[^}]+\})/',
1616
        'boolean'       => '/\b(true|false|null)\b/',
1717
        'number'        => '/' . $int . '/',
18
      ],
19
      'tex' => [
20
        'comment'  => '/(%[^\r\n]*)/m',
21
        'math'     => '/(\$\$?.*?\$\$?)/s',
22
        'keyword'  => '/(\\\\(?:def|edef|gdef|xdef|let|futurelet|if|else|fi|ifnum|ifdim|ifodd|ifmmode|ifx|ifeof|iftrue|iffalse|ifcase|or|loop|repeat|newif|expandafter|noexpand|csname|endcsname|string|number|the|long|outer|global|par|advance|hsize|vsize|hoffset|voffset|displaywidth|parindent|baselineskip|leftskip|rightskip|hangindent|hangafter|parshape|pageno|nopagenumbers|folio|headline|footline|hbox|vbox|vtop|vcenter|rlap|llap|hskip|vskip|hfil|hfill|hfilneg|vfil|vfill|mskip|quad|qquad|enspace|thinspace|enskip|strut|phantom|vphantom|hphantom|smash|raise|lower|moveleft|moveright|halign|valign|noalign|openup|cr|crcr|omit|span|multispan|tabskip|settabs|matrix|pmatrix|bordermatrix|eqalign|displaylines|eqno|leqno|cases|left|right|over|atop|choose|brace|brack|root|of|buildrel|input|end|bye|item|itemitem|indent|noindent|narrower|rm|bf|tt|sl|it|font|char|magnification|magstep|magstephalf|day|month|year|jobname|romannumeral|uppercase|lowercase|footnote|topinsert|pageinsert|midinsert|endinsert|underbar|hfuzz|vfuzz|overfullrule|raggedright|raggedbottom|everypar|everymath|everydisplay|everycr))\b/',
23
        'function' => '/(\\\\[a-zA-Z@]+|\\\\[^a-zA-Z@])/',
24
        'variable' => '/(#[0-9])/',
1825
      ],
1926
      'php' => [
2027
        'tag'           => '/(<\?php|<\?|=\?>|\?>)/',
21
        'string_interp' => '/(".*?")/',
22
        'string'        => '/(\'.*?\')/',
28
        'string_interp' => '/("(?:\\\\.|[^"\\\\])*")/',
29
        'string'        => '/(\'(?:\\\\.|[^\'\\\\])*\')/',
2330
        'comment'       => '/(\/\/[^\r\n]*|#[^\r\n]*|\/\*.*?\*\/)/ms',
2431
        'keyword'       => '/\b(class|abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|new|or|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/',
2532
        'function'      => '/\b([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*(?=\()/',
2633
        'variable'      => '/(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/',
2734
        'number'        => '/' . $int . '/',
2835
        'boolean'       => '/\b(true|false|null)\b/i',
2936
      ],
3037
      'bash' => [
31
        'string_interp' => '/(".*?")/',
38
        'string_interp' => '/("(?:\\\\.|[^"\\\\])*")/',
3239
        'string'        => '/(\'.*?\')/',
3340
        'comment'       => '/(#[^\n]*)/',
...
4047
        'comment'  => '/((?i:rem)\b[^\n]*|::[^\n]*)/',
4148
        'string'   => '/("[^"]*")/',
42
        'keyword'  => '/(?i)\b(if|else|goto|for|in|do|call|exit|echo|pause|set|shift|start|cd|dir|copy|del|md|rd|cls|setlocal|endlocal|enabledelayedexpansion|defined|exist|not|errorlevel|setx|findstr|reg|nul|tokens|usebackq|equ|neq|lss|leq|gtr|geq)\b/',
49
        'keyword'  => '/(?i)\b(if|else|goto|for|in|do|exit|echo|pause|set|shift|start|cd|dir|copy|del|md|rd|cls|setlocal|endlocal|enabledelayedexpansion|defined|exist|not|errorlevel|setx|findstr|reg|nul|tokens|usebackq|equ|neq|lss|leq|gtr|geq)\b/',
50
        'function' => '/(?i)\b(call)\b/',
4351
        'variable' => '/(![\w-]+!|%[\w\(\)-]+%|%%[~a-zA-Z]+|%[~a-zA-Z0-9]+)/',
4452
        'label'    => '/(^\s*:[a-zA-Z0-9_-]+)/m',
4553
        'number'   => '/' . $int . '/',
4654
      ],
4755
      'c' => [
48
        'string'   => '/' . $str . '/',
49
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
50
        'keyword'  => '/\b(auto|break|case|const|continue|default|do|else|enum|extern|for|goto|if|register|return|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/',
51
        'type'     => '/\b(char|double|float|int|long|short|void)\b/',
52
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
53
        'number'   => '/' . $int . '/',
56
        'string'       => '/' . $str . '/',
57
        'comment'      => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
58
        'include'      => '/(^\s*#include[^\r\n]*)/m',
59
        'preprocessor' => '/(^\s*#(?!include\b)[^\r\n]*)/m',
60
        'keyword'      => '/\b(auto|break|case|const|continue|default|do|else|enum|extern|for|goto|if|noreturn|register|return|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/',
61
        'type'         => '/\b(char|double|float|int|long|short|void)\b/',
62
        'function'     => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
63
        'number'       => '/' . $int . '/',
5464
      ],
5565
      'cpp' => [
56
        'string'   => '/' . $str . '/',
57
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
58
        'keyword'  => '/\b(alignas|alignof|and|and_eq|asm|auto|bitand|bitor|break|case|catch|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|dynamic_cast|else|enum|explicit|export|extern|for|friend|goto|if|inline|mutable|namespace|new|noexcept|not|not_eq|nullptr|operator|or|or_eq|private|protected|public|register|reinterpret_cast|return|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|using|virtual|volatile|while|xor|xor_eq)\b/',
59
        'type'     => '/\b(bool|char|char16_t|char32_t|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/',
60
        'boolean'  => '/\b(true|false)\b/',
61
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
62
        'number'   => '/' . $int . '/',
66
        'string'       => '/' . $str . '/',
67
        'comment'      => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
68
        'include'      => '/(^\s*#include[^\r\n]*)/m',
69
        'preprocessor' => '/(^\s*#(?!include\b)[^\r\n]*)/m',
70
        'keyword'      => '/\b(alignas|alignof|and|and_eq|asm|auto|bitand|bitor|break|case|catch|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|dynamic_cast|else|enum|explicit|export|extern|for|friend|goto|if|inline|mutable|namespace|new|noexcept|noreturn|not|not_eq|nullptr|operator|or|or_eq|private|protected|public|register|reinterpret_cast|return|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|using|virtual|volatile|while|xor|xor_eq)\b/',
71
        'type'         => '/\b(bool|char|char16_t|char32_t|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/',
72
        'boolean'      => '/\b(true|false)\b/',
73
        'function'     => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
74
        'number'       => '/' . $int . '/',
6375
      ],
6476
      'java' => [
77
        'class'    => '/(@[a-zA-Z_][a-zA-Z0-9_]*)/',
6578
        'string'   => '/' . $str . '/',
6679
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
6780
        'keyword'  => '/\b(abstract|assert|break|case|catch|class|const|continue|default|do|else|enum|extends|final|finally|for|goto|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|void|volatile|while)\b/',
6881
        'type'     => '/\b(boolean|byte|char|double|float|int|long|short|void)\b/',
6982
        'boolean'  => '/\b(true|false|null)\b/',
7083
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
7184
        'number'   => '/' . $int . '/',
7285
      ],
7386
      'go' => [
74
        'string'   => '/(".*?"|`.*?`)/s',
87
        'string'   => '/("(?:\\\\.|[^"\\\\])*"|`.*?`)/s',
7588
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
7689
        'keyword'  => '/\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/',
7790
        'boolean'  => '/\b(true|false|nil|iota)\b/',
7891
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
7992
        'number'   => '/' . $int . '/',
8093
      ],
8194
      'rust' => [
82
        'string'   => '/(".*?"|\'.*?\')/',
95
        'string'   => '/' . $str . '/',
8396
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
8497
        'keyword'  => '/\b(as|break|const|continue|crate|else|enum|extern|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|Self|static|struct|super|trait|type|unsafe|use|where|while|async|await|dyn)\b/',
8598
        'boolean'  => '/\b(true|false)\b/',
8699
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
87100
        'number'   => '/' . $int . '/',
88101
      ],
89102
      'python' => [
90
        'string'   => '/(\'\'\'.*?\'\'\'|""".*?"""|".*?"|\'.*?\')/s',
103
        'string'   => '/(\'\'\'.*?\'\'\'|""".*?"""|"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\')/s',
91104
        'comment'  => '/(#[^\r\n]*)/m',
92105
        'keyword'  => '/\b(and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b/',
93106
        'boolean'  => '/\b(False|None|True)\b/',
94107
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
95108
        'number'   => '/' . $int . '/',
96109
      ],
97110
      'ruby' => [
98
        'string_interp' => '/(".*?")/',
99
        'string'        => '/(\'.*?\')/',
111
        'string_interp' => '/("(?:\\\\.|[^"\\\\])*")/',
112
        'string'        => '/(\'(?:\\\\.|[^\'\\\\])*\')/',
100113
        'comment'       => '/(#[^\r\n]*)/m',
101114
        'keyword'       => '/\b(alias|and|begin|break|case|class|def|defined|do|else|elsif|end|ensure|for|if|in|module|next|not|or|redo|rescue|retry|return|self|super|then|undef|unless|until|when|while|yield)\b/',
102115
        'boolean'       => '/\b(true|false|nil)\b/',
103116
        'function'      => '/\b([a-zA-Z_][a-zA-Z0-9_]*[?!]?)\s*(?=\()/',
104117
        'variable'      => '/(@[a-zA-Z_]\w*|\$[a-zA-Z_]\w*)/',
105118
        'number'        => '/' . $int . '/',
106119
      ],
107120
      'lua' => [
108
        'string'   => '/(".*?"|\'.*?\'|\[\[.*?\]\])/s',
121
        'string'   => '/("(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|\[\[.*?\]\])/s',
109122
        'comment'  => '/(--\[\[.*?\]\]|--[^\r\n]*)/ms',
110123
        'keyword'  => '/\b(and|break|do|else|elseif|end|for|function|if|in|local|not|or|repeat|return|then|until|while)\b/',
111124
        'boolean'  => '/\b(false|nil|true)\b/',
112125
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
113126
        'number'   => '/' . $int . '/',
114127
      ],
115128
      'javascript' => [
116
        'string'   => '/(".*?"|\'.*?\'|`.*?`)/s',
129
        'string'   => '/("(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|`(?:\\\\.|[^`\\\\])*`)/s',
117130
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
118131
        'keyword'  => '/\b(async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|finally|for|function|if|import|in|instanceof|new|return|super|switch|this|throw|try|typeof|var|void|while|with|yield|let|static|enum)\b/',
119132
        'boolean'  => '/\b(true|false|null|undefined)\b/',
120133
        'function' => '/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*(?=\()/',
121134
        'number'   => '/' . $int . '/',
122135
      ],
123136
      'typescript' => [
124
        'string'   => '/(".*?"|\'.*?\'|`.*?`)/s',
137
        'string'   => '/("(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\'|`(?:\\\\.|[^`\\\\])*`)/s',
125138
        'comment'  => '/(\/\/[^\r\n]*|\/\*.*?\*\/)/ms',
126139
        'keyword'  => '/\b(any|as|break|case|catch|class|const|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|if|implements|import|in|instanceof|interface|let|module|namespace|new|of|package|private|protected|public|require|return|static|super|switch|this|throw|try|type|typeof|var|void|while|with|yield)\b/',
...
150163
      ],
151164
      'json' => [
152
        'attribute' => '/(".*?")(?=\s*:)/',
153
        'string'    => '/(".*?")/',
165
        'attribute' => '/("(?:\\\\.|[^"\\\\])*")(?=\s*:)/',
166
        'string'    => '/("(?:\\\\.|[^"\\\\])*")/',
154167
        'boolean'   => '/\b(true|false|null)\b/',
155168
        'number'    => '/\b(-?\d+(\.\d+)?([eE][+-]?\d+)?)\b/',
156169
      ],
157170
      'sql' => [
158
        'string'  => '/(\'.*?\')/',
159
        'comment' => '/(--[^\r\n]*|\/\*.*?\*\/)/ms',
160
        'keyword' => '/(?i)\b(SELECT|FROM|WHERE|INSERT|INTO|UPDATE|DELETE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|BY|ORDER|HAVING|LIMIT|OFFSET|CREATE|TABLE|DROP|ALTER|INDEX|KEY|PRIMARY|FOREIGN|CONSTRAINT|DEFAULT|NOT|AND|OR|IN|VALUES|SET|AS|DISTINCT|UNION|ALL|CASE|WHEN|THEN|ELSE|END)\b/',
161
        'boolean' => '/(?i)\b(NULL|TRUE|FALSE)\b/',
162
        'number'  => '/' . $int . '/',
171
        'string'   => '/(\'.*?\')/',
172
        'comment'  => '/(--[^\r\n]*|\/\*.*?\*\/)/ms',
173
        'keyword'  => '/(?i)\b(SELECT|FROM|WHERE|INSERT|INTO|UPDATE|DELETE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|BY|ORDER|HAVING|LIMIT|OFFSET|CREATE|TABLE|DROP|ALTER|INDEX|KEY|PRIMARY|FOREIGN|CONSTRAINT|DEFAULT|NOT|AND|OR|IN|VALUES|SET|AS|DISTINCT|UNION|ALL|CASE|WHEN|THEN|ELSE|END)\b/',
174
        'boolean'  => '/(?i)\b(NULL|TRUE|FALSE)\b/',
175
        'function' => '/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/',
176
        'number'   => '/' . $int . '/',
163177
      ],
164178
      'yaml' => [
...
200214
    ];
201215
202
    return $rules[strtolower($lang)] ?? [];
216
    return $rules[strtolower( $lang )] ?? [];
203217
  }
204218
}
M repo.css
213213
  font-size: 0.875rem;
214214
  line-height: 1.6;
215
  white-space: pre;
216
}
217
218
.refs-list {
219
  display: grid;
220
  gap: 10px;
221
}
222
223
.ref-item {
224
  background: #161b22;
225
  border: 1px solid #30363d;
226
  border-radius: 6px;
227
  padding: 12px 16px;
228
  display: flex;
229
  align-items: center;
230
  gap: 12px;
231
}
232
233
.ref-type {
234
  background: #238636;
235
  color: white;
236
  padding: 2px 8px;
237
  border-radius: 12px;
238
  font-size: 0.75rem;
239
  font-weight: 600;
240
  text-transform: uppercase;
241
}
242
243
.ref-type.tag {
244
  background: #8957e5;
245
}
246
247
.ref-name {
248
  font-weight: 600;
249
  color: #f0f6fc;
250
}
251
252
.empty-state {
253
  text-align: center;
254
  padding: 60px 20px;
255
  color: #8b949e;
256
}
257
258
.commit-details {
259
  background: #161b22;
260
  border: 1px solid #30363d;
261
  border-radius: 6px;
262
  padding: 20px;
263
  margin-bottom: 20px;
264
}
265
266
.commit-header {
267
  margin-bottom: 20px;
268
}
269
270
.commit-title {
271
  font-size: 1.25rem;
272
  color: #f0f6fc;
273
  margin-bottom: 10px;
274
}
275
276
.commit-info {
277
  display: grid;
278
  gap: 8px;
279
  font-size: 0.875rem;
280
}
281
282
.commit-info-row {
283
  display: flex;
284
  gap: 10px;
285
}
286
287
.commit-info-label {
288
  color: #8b949e;
289
  width: 80px;
290
  flex-shrink: 0;
291
}
292
293
.commit-info-value {
294
  color: #c9d1d9;
295
  font-family: monospace;
296
}
297
298
.parent-link {
299
  color: #58a6ff;
300
  text-decoration: none;
301
}
302
303
.parent-link:hover {
304
  text-decoration: underline;
305
}
306
307
.repo-grid {
308
  display: grid;
309
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
310
  gap: 16px;
311
  margin-top: 20px;
312
}
313
314
.repo-card {
315
  background: #161b22;
316
  border: 1px solid #30363d;
317
  border-radius: 8px;
318
  padding: 20px;
319
  text-decoration: none;
320
  color: inherit;
321
  transition: border-color 0.2s, transform 0.1s;
322
}
323
324
.repo-card:hover {
325
  border-color: #58a6ff;
326
  transform: translateY(-2px);
327
}
328
329
.repo-card h3 {
330
  color: #58a6ff;
331
  margin-bottom: 8px;
332
  font-size: 1.1rem;
333
}
334
335
.repo-card p {
336
  color: #8b949e;
337
  font-size: 0.875rem;
338
  margin: 0;
339
}
340
341
.current-repo {
342
  background: #21262d;
343
  border: 1px solid #58a6ff;
344
  padding: 8px 16px;
345
  border-radius: 6px;
346
  font-size: 0.875rem;
347
  color: #f0f6fc;
348
}
349
350
.current-repo strong {
351
  color: #58a6ff;
352
}
353
354
.branch-badge {
355
  background: #238636;
356
  color: white;
357
  padding: 2px 8px;
358
  border-radius: 12px;
359
  font-size: 0.75rem;
360
  font-weight: 600;
361
  margin-left: 10px;
362
}
363
364
.commit-row {
365
  display: flex;
366
  padding: 10px 0;
367
  border-bottom: 1px solid #30363d;
368
  gap: 15px;
369
  align-items: baseline;
370
}
371
372
.commit-row:last-child {
373
  border-bottom: none;
374
}
375
376
.commit-row .sha {
377
  font-family: monospace;
378
  color: #58a6ff;
379
  text-decoration: none;
380
}
381
382
.commit-row .message {
383
  flex: 1;
384
  font-weight: 500;
385
}
386
387
.commit-row .meta {
388
  font-size: 0.85em;
389
  color: #8b949e;
390
  white-space: nowrap;
391
}
392
393
.blob-content-image {
394
  text-align: center;
395
  padding: 20px;
396
  background: #0d1117;
397
}
398
399
.blob-content-image img {
400
  max-width: 100%;
401
  border: 1px solid #30363d;
402
}
403
404
.blob-content-video {
405
  text-align: center;
406
  padding: 20px;
407
  background: #000;
408
}
409
410
.blob-content-video video {
411
  max-width: 100%;
412
  max-height: 80vh;
413
}
414
415
.blob-content-audio {
416
  text-align: center;
417
  padding: 40px;
418
  background: #161b22;
419
}
420
421
.blob-content-audio audio {
422
  width: 100%;
423
  max-width: 600px;
424
}
425
426
.download-state {
427
  text-align: center;
428
  padding: 40px;
429
  border: 1px solid #30363d;
430
  border-radius: 6px;
431
  margin-top: 10px;
432
}
433
434
.download-state p {
435
  margin-bottom: 20px;
436
  color: #8b949e;
437
}
438
439
.btn-download {
440
  display: inline-block;
441
  padding: 6px 16px;
442
  background: #238636;
443
  color: white;
444
  text-decoration: none;
445
  border-radius: 6px;
446
  font-weight: 600;
447
}
448
449
.repo-info-banner {
450
  margin-top: 15px;
451
}
452
453
.file-icon-container {
454
  width: 20px;
455
  text-align: center;
456
  margin-right: 5px;
457
  color: #8b949e;
458
}
459
460
.file-size {
461
  color: #8b949e;
462
  font-size: 0.8em;
463
  margin-left: 10px;
464
}
465
466
.file-date {
467
  color: #8b949e;
468
  font-size: 0.8em;
469
  margin-left: auto;
470
}
471
472
.repo-card-time {
473
  margin-top: 8px;
474
  color: #58a6ff;
475
}
476
477
478
.diff-container {
479
  display: flex;
480
  flex-direction: column;
481
  gap: 20px;
482
}
483
484
.diff-file {
485
  background: #161b22;
486
  border: 1px solid #30363d;
487
  border-radius: 6px;
488
  overflow: hidden;
489
}
490
491
.diff-header {
492
  background: #21262d;
493
  padding: 10px 16px;
494
  border-bottom: 1px solid #30363d;
495
  display: flex;
496
  align-items: center;
497
  gap: 10px;
498
}
499
500
.diff-path {
501
  font-family: monospace;
502
  font-size: 0.9rem;
503
  color: #f0f6fc;
504
}
505
506
.diff-binary {
507
  padding: 20px;
508
  text-align: center;
509
  color: #8b949e;
510
  font-style: italic;
511
}
512
513
.diff-content {
514
  overflow-x: auto;
515
}
516
517
.diff-content table {
518
  width: 100%;
519
  border-collapse: collapse;
520
  font-family: 'SFMono-Regular', Consolas, monospace;
521
  font-size: 12px;
522
}
523
524
.diff-content td {
525
  padding: 2px 0;
526
  line-height: 20px;
527
}
528
529
.diff-num {
530
  width: 1%;
531
  min-width: 40px;
532
  text-align: right;
533
  padding-right: 10px;
534
  color: #6e7681;
535
  user-select: none;
536
  background: #0d1117;
537
  border-right: 1px solid #30363d;
538
}
539
540
.diff-num::before {
541
  content: attr(data-num);
542
}
543
544
.diff-code {
545
  padding-left: 10px;
546
  white-space: pre-wrap;
547
  word-break: break-all;
548
  color: #c9d1d9;
549
}
550
551
.diff-marker {
552
  display: inline-block;
553
  width: 15px;
554
  user-select: none;
555
  color: #8b949e;
556
}
557
558
/* Protanopia Safe Colors: Blue (Add) and Yellow (Del) */
559
.diff-add {
560
  background-color: rgba(2, 59, 149, 0.25);
561
}
562
.diff-add .diff-code {
563
  color: #79c0ff;
564
}
565
.diff-add .diff-marker {
566
  color: #79c0ff;
567
}
568
569
.diff-del {
570
  background-color: rgba(148, 99, 0, 0.25);
571
}
572
.diff-del .diff-code {
573
  color: #d29922;
574
}
575
.diff-del .diff-marker {
576
  color: #d29922;
577
}
578
579
.diff-gap {
580
  background: #0d1117;
581
  color: #484f58;
582
  text-align: center;
583
  font-size: 0.8em;
584
  height: 20px;
585
}
586
.diff-gap td {
587
  padding: 0;
588
  line-height: 20px;
589
  background: rgba(110, 118, 129, 0.1);
590
}
591
592
.status-add { color: #58a6ff; }
593
.status-del { color: #d29922; }
594
.status-mod { color: #a371f7; }
595
596
.tag-table {
597
  width: 100%;
598
  border-collapse: collapse;
599
  margin-top: 10px;
600
}
601
602
.tag-table th {
603
  text-align: left;
604
  padding: 10px 16px;
605
  border-bottom: 2px solid #30363d;
606
  color: #8b949e;
607
  font-size: 0.875rem;
608
  font-weight: 600;
609
  white-space: nowrap;
610
}
611
612
.tag-table td {
613
  padding: 12px 16px;
614
  border-bottom: 1px solid #21262d;
615
  vertical-align: top;
616
  color: #c9d1d9;
617
  font-size: 0.9rem;
618
}
619
620
.tag-table tr:hover td {
621
  background: #161b22;
622
}
623
624
.tag-table .tag-name {
625
  min-width: 140px;
626
  width: 20%;
627
}
628
629
.tag-table .tag-message {
630
  width: auto;
631
  white-space: normal;
632
  word-break: break-word;
633
  color: #c9d1d9;
634
  font-weight: 500;
635
}
636
637
.tag-table .tag-author,
638
.tag-table .tag-time,
639
.tag-table .tag-hash {
640
  width: 1%;
641
  white-space: nowrap;
642
}
643
644
.tag-table .tag-time {
645
  text-align: right;
646
  color: #8b949e;
647
}
648
649
.tag-table .tag-hash {
650
  text-align: right;
651
}
652
653
.tag-table .tag-name a {
654
  color: #58a6ff;
655
  text-decoration: none;
656
  font-family: 'SFMono-Regular', Consolas, monospace;
657
}
658
659
.tag-table .tag-author {
660
  color: #c9d1d9;
661
}
662
663
.tag-table .tag-age-header {
664
  text-align: right;
665
}
666
667
.tag-table .tag-commit-header {
668
  text-align: right;
669
}
670
671
.tag-table .commit-hash {
672
  font-family: 'SFMono-Regular', Consolas, monospace;
673
  color: #58a6ff;
674
  text-decoration: none;
675
}
676
677
.tag-table .commit-hash:hover {
678
  text-decoration: underline;
679
}
680
681
.blob-code {
682
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
683
  background-color: #161b22;
684
  color: #fcfcfa;
685
}
686
687
.hl-comment,
688
.hl-doc-comment {
689
  color: #727072;
690
  font-style: italic;
691
}
692
693
.hl-keyword,
694
.hl-tag,
695
.hl-storage,
696
.hl-modifier {
697
  color: #ff6188;
698
  font-weight: 600;
699
}
700
701
.hl-function,
702
.hl-method,
703
.hl-class,
704
.hl-type,
705
.hl-label {
706
  color: #a9dc76;
707
}
708
709
.hl-string,
710
.hl-string_interp {
711
  color: #ffd866;
712
}
713
714
.hl-number,
715
.hl-boolean,
716
.hl-constant {
717
  color: #ab9df2;
718
}
719
720
.hl-attribute,
721
.hl-property {
722
  color: #fc9867;
723
}
724
725
.hl-operator,
726
.hl-punctuation,
727
.hl-escape {
728
  color: #78dce8;
729
}
730
731
.hl-variable {
732
  color: #fcfcfa;
733
}
734
735
.hl-interp-punct {
736
  color: #ff6188;
737
}
738
739
.hl-interp-punct {
740
  color: #ff6188;
741
}
742
743
.hl-code {
744
  display: inline-block;
745
  width: 100%;
746
  background-color: #21262d;
747
  color: #c9d1d9;
748
}
749
750
.hl-math {
751
  color: #78dce8;
215
  white-space: pre-wrap;
216
  overflow-wrap: break-word;
217
}
218
219
.refs-list {
220
  display: grid;
221
  gap: 10px;
222
}
223
224
.ref-item {
225
  background: #161b22;
226
  border: 1px solid #30363d;
227
  border-radius: 6px;
228
  padding: 12px 16px;
229
  display: flex;
230
  align-items: center;
231
  gap: 12px;
232
}
233
234
.ref-type {
235
  background: #238636;
236
  color: white;
237
  padding: 2px 8px;
238
  border-radius: 12px;
239
  font-size: 0.75rem;
240
  font-weight: 600;
241
  text-transform: uppercase;
242
}
243
244
.ref-type.tag {
245
  background: #8957e5;
246
}
247
248
.ref-name {
249
  font-weight: 600;
250
  color: #f0f6fc;
251
}
252
253
.empty-state {
254
  text-align: center;
255
  padding: 60px 20px;
256
  color: #8b949e;
257
}
258
259
.commit-details {
260
  background: #161b22;
261
  border: 1px solid #30363d;
262
  border-radius: 6px;
263
  padding: 20px;
264
  margin-bottom: 20px;
265
}
266
267
.commit-header {
268
  margin-bottom: 20px;
269
}
270
271
.commit-title {
272
  font-size: 1.25rem;
273
  color: #f0f6fc;
274
  margin-bottom: 10px;
275
}
276
277
.commit-info {
278
  display: grid;
279
  gap: 8px;
280
  font-size: 0.875rem;
281
}
282
283
.commit-info-row {
284
  display: flex;
285
  gap: 10px;
286
}
287
288
.commit-info-label {
289
  color: #8b949e;
290
  width: 80px;
291
  flex-shrink: 0;
292
}
293
294
.commit-info-value {
295
  color: #c9d1d9;
296
  font-family: monospace;
297
}
298
299
.parent-link {
300
  color: #58a6ff;
301
  text-decoration: none;
302
}
303
304
.parent-link:hover {
305
  text-decoration: underline;
306
}
307
308
.repo-grid {
309
  display: grid;
310
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
311
  gap: 16px;
312
  margin-top: 20px;
313
}
314
315
.repo-card {
316
  background: #161b22;
317
  border: 1px solid #30363d;
318
  border-radius: 8px;
319
  padding: 20px;
320
  text-decoration: none;
321
  color: inherit;
322
  transition: border-color 0.2s, transform 0.1s;
323
}
324
325
.repo-card:hover {
326
  border-color: #58a6ff;
327
  transform: translateY(-2px);
328
}
329
330
.repo-card h3 {
331
  color: #58a6ff;
332
  margin-bottom: 8px;
333
  font-size: 1.1rem;
334
}
335
336
.repo-card p {
337
  color: #8b949e;
338
  font-size: 0.875rem;
339
  margin: 0;
340
}
341
342
.current-repo {
343
  background: #21262d;
344
  border: 1px solid #58a6ff;
345
  padding: 8px 16px;
346
  border-radius: 6px;
347
  font-size: 0.875rem;
348
  color: #f0f6fc;
349
}
350
351
.current-repo strong {
352
  color: #58a6ff;
353
}
354
355
.branch-badge {
356
  background: #238636;
357
  color: white;
358
  padding: 2px 8px;
359
  border-radius: 12px;
360
  font-size: 0.75rem;
361
  font-weight: 600;
362
  margin-left: 10px;
363
}
364
365
.commit-row {
366
  display: flex;
367
  padding: 10px 0;
368
  border-bottom: 1px solid #30363d;
369
  gap: 15px;
370
  align-items: baseline;
371
}
372
373
.commit-row:last-child {
374
  border-bottom: none;
375
}
376
377
.commit-row .sha {
378
  font-family: monospace;
379
  color: #58a6ff;
380
  text-decoration: none;
381
}
382
383
.commit-row .message {
384
  flex: 1;
385
  font-weight: 500;
386
}
387
388
.commit-row .meta {
389
  font-size: 0.85em;
390
  color: #8b949e;
391
  white-space: nowrap;
392
}
393
394
.blob-content-image {
395
  text-align: center;
396
  padding: 20px;
397
  background: #0d1117;
398
}
399
400
.blob-content-image img {
401
  max-width: 100%;
402
  border: 1px solid #30363d;
403
}
404
405
.blob-content-video {
406
  text-align: center;
407
  padding: 20px;
408
  background: #000;
409
}
410
411
.blob-content-video video {
412
  max-width: 100%;
413
  max-height: 80vh;
414
}
415
416
.blob-content-audio {
417
  text-align: center;
418
  padding: 40px;
419
  background: #161b22;
420
}
421
422
.blob-content-audio audio {
423
  width: 100%;
424
  max-width: 600px;
425
}
426
427
.download-state {
428
  text-align: center;
429
  padding: 40px;
430
  border: 1px solid #30363d;
431
  border-radius: 6px;
432
  margin-top: 10px;
433
}
434
435
.download-state p {
436
  margin-bottom: 20px;
437
  color: #8b949e;
438
}
439
440
.btn-download {
441
  display: inline-block;
442
  padding: 6px 16px;
443
  background: #238636;
444
  color: white;
445
  text-decoration: none;
446
  border-radius: 6px;
447
  font-weight: 600;
448
}
449
450
.repo-info-banner {
451
  margin-top: 15px;
452
}
453
454
.file-icon-container {
455
  width: 20px;
456
  text-align: center;
457
  margin-right: 5px;
458
  color: #8b949e;
459
}
460
461
.file-size {
462
  color: #8b949e;
463
  font-size: 0.8em;
464
  margin-left: 10px;
465
}
466
467
.file-date {
468
  color: #8b949e;
469
  font-size: 0.8em;
470
  margin-left: auto;
471
}
472
473
.repo-card-time {
474
  margin-top: 8px;
475
  color: #58a6ff;
476
}
477
478
479
.diff-container {
480
  display: flex;
481
  flex-direction: column;
482
  gap: 20px;
483
}
484
485
.diff-file {
486
  background: #161b22;
487
  border: 1px solid #30363d;
488
  border-radius: 6px;
489
  overflow: hidden;
490
}
491
492
.diff-header {
493
  background: #21262d;
494
  padding: 10px 16px;
495
  border-bottom: 1px solid #30363d;
496
  display: flex;
497
  align-items: center;
498
  gap: 10px;
499
}
500
501
.diff-path {
502
  font-family: monospace;
503
  font-size: 0.9rem;
504
  color: #f0f6fc;
505
}
506
507
.diff-binary {
508
  padding: 20px;
509
  text-align: center;
510
  color: #8b949e;
511
  font-style: italic;
512
}
513
514
.diff-content {
515
  overflow-x: auto;
516
}
517
518
.diff-content table {
519
  width: 100%;
520
  border-collapse: collapse;
521
  font-family: 'SFMono-Regular', Consolas, monospace;
522
  font-size: 12px;
523
}
524
525
.diff-content td {
526
  padding: 2px 0;
527
  line-height: 20px;
528
}
529
530
.diff-num {
531
  width: 1%;
532
  min-width: 40px;
533
  text-align: right;
534
  padding-right: 10px;
535
  color: #6e7681;
536
  user-select: none;
537
  background: #0d1117;
538
  border-right: 1px solid #30363d;
539
}
540
541
.diff-num::before {
542
  content: attr(data-num);
543
}
544
545
.diff-code {
546
  padding-left: 10px;
547
  white-space: pre-wrap;
548
  word-break: break-all;
549
  color: #c9d1d9;
550
}
551
552
.diff-marker {
553
  display: inline-block;
554
  width: 15px;
555
  user-select: none;
556
  color: #8b949e;
557
}
558
559
/* Protanopia Safe Colors: Blue (Add) and Yellow (Del) */
560
.diff-add {
561
  background-color: rgba(2, 59, 149, 0.25);
562
}
563
.diff-add .diff-code {
564
  color: #79c0ff;
565
}
566
.diff-add .diff-marker {
567
  color: #79c0ff;
568
}
569
570
.diff-del {
571
  background-color: rgba(148, 99, 0, 0.25);
572
}
573
.diff-del .diff-code {
574
  color: #d29922;
575
}
576
.diff-del .diff-marker {
577
  color: #d29922;
578
}
579
580
.diff-gap {
581
  background: #0d1117;
582
  color: #484f58;
583
  text-align: center;
584
  font-size: 0.8em;
585
  height: 20px;
586
}
587
.diff-gap td {
588
  padding: 0;
589
  line-height: 20px;
590
  background: rgba(110, 118, 129, 0.1);
591
}
592
593
.status-add { color: #58a6ff; }
594
.status-del { color: #d29922; }
595
.status-mod { color: #a371f7; }
596
597
.tag-table {
598
  width: 100%;
599
  border-collapse: collapse;
600
  margin-top: 10px;
601
}
602
603
.tag-table th {
604
  text-align: left;
605
  padding: 10px 16px;
606
  border-bottom: 2px solid #30363d;
607
  color: #8b949e;
608
  font-size: 0.875rem;
609
  font-weight: 600;
610
  white-space: nowrap;
611
}
612
613
.tag-table td {
614
  padding: 12px 16px;
615
  border-bottom: 1px solid #21262d;
616
  vertical-align: top;
617
  color: #c9d1d9;
618
  font-size: 0.9rem;
619
}
620
621
.tag-table tr:hover td {
622
  background: #161b22;
623
}
624
625
.tag-table .tag-name {
626
  min-width: 140px;
627
  width: 20%;
628
}
629
630
.tag-table .tag-message {
631
  width: auto;
632
  white-space: normal;
633
  word-break: break-word;
634
  color: #c9d1d9;
635
  font-weight: 500;
636
}
637
638
.tag-table .tag-author,
639
.tag-table .tag-time,
640
.tag-table .tag-hash {
641
  width: 1%;
642
  white-space: nowrap;
643
}
644
645
.tag-table .tag-time {
646
  text-align: right;
647
  color: #8b949e;
648
}
649
650
.tag-table .tag-hash {
651
  text-align: right;
652
}
653
654
.tag-table .tag-name a {
655
  color: #58a6ff;
656
  text-decoration: none;
657
  font-family: 'SFMono-Regular', Consolas, monospace;
658
}
659
660
.tag-table .tag-author {
661
  color: #c9d1d9;
662
}
663
664
.tag-table .tag-age-header {
665
  text-align: right;
666
}
667
668
.tag-table .tag-commit-header {
669
  text-align: right;
670
}
671
672
.tag-table .commit-hash {
673
  font-family: 'SFMono-Regular', Consolas, monospace;
674
  color: #58a6ff;
675
  text-decoration: none;
676
}
677
678
.tag-table .commit-hash:hover {
679
  text-decoration: underline;
680
}
681
682
.blob-code {
683
  font-family: 'SFMono-Regular', Consolas, monospace;
684
  background-color: #161b22;
685
  color: #fcfcfa;
686
  font-size: 0.875rem;
687
  line-height: 1.6;
688
  tab-size: 2;
689
}
690
691
.hl-comment,
692
.hl-doc-comment {
693
  color: #727072;
694
  font-style: italic;
695
}
696
697
.hl-function,
698
.hl-method {
699
  color: #78dce8;
700
}
701
702
.hl-tag {
703
  color: #3e8bff;
704
}
705
706
.hl-class,
707
.hl-type,
708
.hl-interface,
709
.hl-struct {
710
  color: #a9dc76;
711
}
712
713
.hl-keyword,
714
.hl-storage,
715
.hl-modifier,
716
.hl-statement {
717
  color: #ff6188;
718
  font-weight: 600;
719
}
720
721
.hl-string,
722
.hl-string_interp {
723
  color: #ffd866;
724
}
725
726
.hl-number,
727
.hl-boolean,
728
.hl-constant,
729
.hl-preprocessor {
730
  color: #ab9df2;
731
}
732
733
.hl-variable {
734
  color: #fcfcfa;
735
}
736
737
.hl-attribute,
738
.hl-property {
739
  color: #fc9867;
740
}
741
742
.hl-operator,
743
.hl-punctuation,
744
.hl-escape {
745
  color: #939293;
746
}
747
748
.hl-interp-punct {
749
  color: #ff6188;
750
}
751
752
.hl-math {
753
  color: #ab9df2;
754
  font-style: italic;
755
}
756
757
.hl-code {
758
  display: inline-block;
759
  width: 100%;
760
  background-color: #0d1117;
761
  color: #c9d1d9;
762
  padding: 2px 4px;
763
  border-radius: 3px;
752764
}
753765