pi-studio 0.5.39 → 0.5.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `pi-studio` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.5.40] — 2026-03-30
8
+
9
+ ### Changed
10
+ - Studio editor highlighting now keeps plain text in code files closer to the normal editor foreground while preserving a distinct markdown-code tint for inline backticks and unlabeled fenced blocks.
11
+ - Language-aware Studio syntax highlighting for code files and labeled fenced code blocks is now a bit richer, with additional highlighting for function-like identifiers, type/class-like names, and a few language-specific constructs such as decorators or macros.
12
+
7
13
  ## [0.5.39] — 2026-03-30
8
14
 
9
15
  ### Added
@@ -2768,7 +2768,7 @@
2768
2768
  }
2769
2769
 
2770
2770
  if (match[1]) {
2771
- out += wrapHighlight("hl-code", token);
2771
+ out += wrapHighlight("hl-md-code", token);
2772
2772
  } else if (match[2]) {
2773
2773
  const linkMatch = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
2774
2774
  if (linkMatch) {
@@ -2870,37 +2870,43 @@
2870
2870
  }
2871
2871
 
2872
2872
  if (lang === "javascript" || lang === "typescript") {
2873
- const jsPattern = /(\/\/.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:const|let|var|function|return|if|else|for|while|switch|case|break|continue|try|catch|finally|throw|new|class|extends|import|from|export|default|async|await|true|false|null|undefined|typeof|instanceof)\b)|(\b\d+(?:\.\d+)?\b)/g;
2873
+ const jsPattern = /(\/\/.*$|\/\*.*?\*\/)|(`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:const|let|var|function|return|if|else|for|while|switch|case|break|continue|try|catch|finally|throw|new|class|extends|import|from|export|default|async|await|true|false|null|undefined|typeof|instanceof|interface|implements|enum|type|public|private|protected|readonly|abstract|declare|this|super)\b)|(\b[A-Za-z_$][A-Za-z0-9_$]*(?=\s*\())|(\b[A-Z][A-Za-z0-9_$]*\b)|(\b\d+(?:\.\d+)?\b)/g;
2874
2874
  const highlighted = highlightCodeTokens(source, jsPattern, (match) => {
2875
2875
  if (match[1]) return "hl-code-com";
2876
2876
  if (match[2]) return "hl-code-str";
2877
2877
  if (match[3]) return "hl-code-kw";
2878
- if (match[4]) return "hl-code-num";
2878
+ if (match[4]) return "hl-code-fn";
2879
+ if (match[5]) return "hl-code-type";
2880
+ if (match[6]) return "hl-code-num";
2879
2881
  return "hl-code";
2880
2882
  });
2881
2883
  return "<span class='hl-code'>" + highlighted + "</span>";
2882
2884
  }
2883
2885
 
2884
2886
  if (lang === "python") {
2885
- const pyPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:def|class|return|if|elif|else|for|while|try|except|finally|import|from|as|with|lambda|yield|True|False|None|and|or|not|in|is|pass|break|continue|raise|global|nonlocal|assert)\b)|(\b\d+(?:\.\d+)?\b)/g;
2887
+ const pyPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(@[A-Za-z_][A-Za-z0-9_]*)|(\b(?:def|class|return|if|elif|else|for|while|try|except|finally|import|from|as|with|lambda|yield|True|False|None|and|or|not|in|is|pass|break|continue|raise|global|nonlocal|assert)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\())|(\b[A-Z][A-Za-z0-9_]*\b)|(\b\d+(?:\.\d+)?\b)/g;
2886
2888
  const highlighted = highlightCodeTokens(source, pyPattern, (match) => {
2887
2889
  if (match[1]) return "hl-code-com";
2888
2890
  if (match[2]) return "hl-code-str";
2889
- if (match[3]) return "hl-code-kw";
2890
- if (match[4]) return "hl-code-num";
2891
+ if (match[3]) return "hl-code-fn";
2892
+ if (match[4]) return "hl-code-kw";
2893
+ if (match[5]) return "hl-code-fn";
2894
+ if (match[6]) return "hl-code-type";
2895
+ if (match[7]) return "hl-code-num";
2891
2896
  return "hl-code";
2892
2897
  });
2893
2898
  return "<span class='hl-code'>" + highlighted + "</span>";
2894
2899
  }
2895
2900
 
2896
2901
  if (lang === "bash") {
2897
- const shPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'[^']*')|(\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*)|(\b(?:if|then|else|fi|for|in|do|done|case|esac|function|local|export|readonly|return|break|continue|while|until)\b)|(\b\d+\b)/g;
2902
+ const shPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'[^']*')|(\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*)|(\b(?:if|then|else|fi|for|in|do|done|case|esac|function|local|export|readonly|return|break|continue|while|until)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\(\s*\)))|(\b\d+\b)/g;
2898
2903
  const highlighted = highlightCodeTokens(source, shPattern, (match) => {
2899
2904
  if (match[1]) return "hl-code-com";
2900
2905
  if (match[2]) return "hl-code-str";
2901
2906
  if (match[3]) return "hl-code-var";
2902
2907
  if (match[4]) return "hl-code-kw";
2903
- if (match[5]) return "hl-code-num";
2908
+ if (match[5]) return "hl-code-fn";
2909
+ if (match[6]) return "hl-code-num";
2904
2910
  return "hl-code";
2905
2911
  });
2906
2912
  return "<span class='hl-code'>" + highlighted + "</span>";
@@ -2919,74 +2925,85 @@
2919
2925
  }
2920
2926
 
2921
2927
  if (lang === "rust") {
2922
- const rustPattern = /(\/\/.*$)|("(?:[^"\\]|\\.)*")|(\b(?:fn|let|mut|const|struct|enum|impl|trait|pub|mod|use|crate|self|super|match|if|else|for|while|loop|return|break|continue|where|as|in|ref|move|async|await|unsafe|extern|type|static|true|false|Some|None|Ok|Err|Self)\b)|(\b\d[\d_]*(?:\.\d[\d_]*)?(?:f32|f64|u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize)?\b)/g;
2928
+ const rustPattern = /(\/\/.*$)|("(?:[^"\\]|\\.)*")|(\b[A-Za-z_][A-Za-z0-9_]*!(?=\s*(?:\(|\{|\[)))|(\b(?:fn|let|mut|const|struct|enum|impl|trait|pub|mod|use|crate|self|super|match|if|else|for|while|loop|return|break|continue|where|as|in|ref|move|async|await|unsafe|extern|type|static|true|false|Some|None|Ok|Err|Self)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\())|(\b[A-Z][A-Za-z0-9_]*\b)|(\b\d[\d_]*(?:\.\d[\d_]*)?(?:f32|f64|u8|u16|u32|u64|u128|usize|i8|i16|i32|i64|i128|isize)?\b)/g;
2923
2929
  const highlighted = highlightCodeTokens(source, rustPattern, (match) => {
2924
2930
  if (match[1]) return "hl-code-com";
2925
2931
  if (match[2]) return "hl-code-str";
2926
- if (match[3]) return "hl-code-kw";
2927
- if (match[4]) return "hl-code-num";
2932
+ if (match[3]) return "hl-code-fn";
2933
+ if (match[4]) return "hl-code-kw";
2934
+ if (match[5]) return "hl-code-fn";
2935
+ if (match[6]) return "hl-code-type";
2936
+ if (match[7]) return "hl-code-num";
2928
2937
  return "hl-code";
2929
2938
  });
2930
2939
  return "<span class='hl-code'>" + highlighted + "</span>";
2931
2940
  }
2932
2941
 
2933
2942
  if (lang === "c" || lang === "cpp") {
2934
- const cPattern = /(\/\/.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)')|(#\s*\w+)|(\b(?:if|else|for|while|do|switch|case|break|continue|return|goto|struct|union|enum|typedef|sizeof|void|int|char|short|long|float|double|unsigned|signed|const|static|extern|volatile|register|inline|auto|restrict|true|false|NULL|nullptr|class|public|private|protected|virtual|override|template|typename|namespace|using|new|delete|try|catch|throw|noexcept|constexpr|auto|decltype|static_cast|dynamic_cast|reinterpret_cast|const_cast|std|include|define|ifdef|ifndef|endif|pragma)\b)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[fFlLuU]*\b)/g;
2943
+ const cPattern = /(\/\/.*$|\/\*.*?\*\/)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)')|(#\s*\w+)|(\b(?:if|else|for|while|do|switch|case|break|continue|return|goto|struct|union|enum|typedef|sizeof|void|int|char|short|long|float|double|unsigned|signed|const|static|extern|volatile|register|inline|auto|restrict|true|false|NULL|nullptr|class|public|private|protected|virtual|override|template|typename|namespace|using|new|delete|try|catch|throw|noexcept|constexpr|auto|decltype|static_cast|dynamic_cast|reinterpret_cast|const_cast|std|include|define|ifdef|ifndef|endif|pragma)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\())|(\b[A-Z][A-Za-z0-9_]*\b)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[fFlLuU]*\b)/g;
2935
2944
  const highlighted = highlightCodeTokens(source, cPattern, (match) => {
2936
2945
  if (match[1]) return "hl-code-com";
2937
2946
  if (match[2]) return "hl-code-str";
2938
2947
  if (match[3]) return "hl-code-kw";
2939
2948
  if (match[4]) return "hl-code-kw";
2940
- if (match[5]) return "hl-code-num";
2949
+ if (match[5]) return "hl-code-fn";
2950
+ if (match[6]) return "hl-code-type";
2951
+ if (match[7]) return "hl-code-num";
2941
2952
  return "hl-code";
2942
2953
  });
2943
2954
  return "<span class='hl-code'>" + highlighted + "</span>";
2944
2955
  }
2945
2956
 
2946
2957
  if (lang === "julia") {
2947
- const jlPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:function|end|if|elseif|else|for|while|begin|let|local|global|const|return|break|continue|do|try|catch|finally|throw|module|import|using|export|struct|mutable|abstract|primitive|where|macro|quote|true|false|nothing|missing|in|isa|typeof)\b)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)/g;
2958
+ const jlPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(@[A-Za-z_][A-Za-z0-9_]*)|(\b(?:function|end|if|elseif|else|for|while|begin|let|local|global|const|return|break|continue|do|try|catch|finally|throw|module|import|using|export|struct|mutable|abstract|primitive|where|macro|quote|true|false|nothing|missing|in|isa|typeof)\b)|(\b[A-Za-z_][A-Za-z0-9_]*!?(?=\s*\())|(\b[A-Z][A-Za-z0-9_]*\b)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)/g;
2948
2959
  const highlighted = highlightCodeTokens(source, jlPattern, (match) => {
2949
2960
  if (match[1]) return "hl-code-com";
2950
2961
  if (match[2]) return "hl-code-str";
2951
- if (match[3]) return "hl-code-kw";
2952
- if (match[4]) return "hl-code-num";
2962
+ if (match[3]) return "hl-code-fn";
2963
+ if (match[4]) return "hl-code-kw";
2964
+ if (match[5]) return "hl-code-fn";
2965
+ if (match[6]) return "hl-code-type";
2966
+ if (match[7]) return "hl-code-num";
2953
2967
  return "hl-code";
2954
2968
  });
2955
2969
  return "<span class='hl-code'>" + highlighted + "</span>";
2956
2970
  }
2957
2971
 
2958
2972
  if (lang === "fortran") {
2959
- const fPattern = /(!.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:program|end|subroutine|function|module|use|implicit|none|integer|real|double|precision|complex|character|logical|dimension|allocatable|intent|in|out|inout|parameter|data|do|if|then|else|elseif|endif|enddo|call|return|write|read|print|format|stop|contains|type|class|select|case|where|forall|associate|block|procedure|interface|abstract|extends|allocate|deallocate|cycle|exit|go|to|common|equivalence|save|external|intrinsic)\b)|(\b\d+(?:\.\d+)?(?:[dDeE][+-]?\d+)?\b)/gi;
2973
+ const fPattern = /(!.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:program|end|subroutine|function|module|use|implicit|none|integer|real|double|precision|complex|character|logical|dimension|allocatable|intent|in|out|inout|parameter|data|do|if|then|else|elseif|endif|enddo|call|return|write|read|print|format|stop|contains|type|class|select|case|where|forall|associate|block|procedure|interface|abstract|extends|allocate|deallocate|cycle|exit|go|to|common|equivalence|save|external|intrinsic)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\())|(\b\d+(?:\.\d+)?(?:[dDeE][+-]?\d+)?\b)/gi;
2960
2974
  const highlighted = highlightCodeTokens(source, fPattern, (match) => {
2961
2975
  if (match[1]) return "hl-code-com";
2962
2976
  if (match[2]) return "hl-code-str";
2963
2977
  if (match[3]) return "hl-code-kw";
2964
- if (match[4]) return "hl-code-num";
2978
+ if (match[4]) return "hl-code-fn";
2979
+ if (match[5]) return "hl-code-num";
2965
2980
  return "hl-code";
2966
2981
  });
2967
2982
  return "<span class='hl-code'>" + highlighted + "</span>";
2968
2983
  }
2969
2984
 
2970
2985
  if (lang === "r") {
2971
- const rPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:function|if|else|for|while|repeat|in|next|break|return|TRUE|FALSE|NULL|NA|NA_integer_|NA_real_|NA_complex_|NA_character_|Inf|NaN|library|require|source|local|switch)\b)|(<-|->|<<-|->>)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[Li]?\b)/g;
2986
+ const rPattern = /(#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')|(\b(?:function|if|else|for|while|repeat|in|next|break|return|TRUE|FALSE|NULL|NA|NA_integer_|NA_real_|NA_complex_|NA_character_|Inf|NaN|library|require|source|local|switch)\b)|(<-|->|<<-|->>)|(\b[A-Za-z.][A-Za-z0-9._]*(?=\s*\())|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[Li]?\b)/g;
2972
2987
  const highlighted = highlightCodeTokens(source, rPattern, (match) => {
2973
2988
  if (match[1]) return "hl-code-com";
2974
2989
  if (match[2]) return "hl-code-str";
2975
2990
  if (match[3]) return "hl-code-kw";
2976
- if (match[4]) return "hl-code-kw";
2977
- if (match[5]) return "hl-code-num";
2991
+ if (match[4]) return "hl-code-op";
2992
+ if (match[5]) return "hl-code-fn";
2993
+ if (match[6]) return "hl-code-num";
2978
2994
  return "hl-code";
2979
2995
  });
2980
2996
  return "<span class='hl-code'>" + highlighted + "</span>";
2981
2997
  }
2982
2998
 
2983
2999
  if (lang === "matlab") {
2984
- const matPattern = /(%.*$)|('(?:[^']|'')*'|"(?:[^"\\]|\\.)*")|(\b(?:function|end|if|elseif|else|for|while|switch|case|otherwise|try|catch|return|break|continue|global|persistent|classdef|properties|methods|events|enumeration|true|false)\b)|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[i]?\b)/g;
3000
+ const matPattern = /(%.*$)|('(?:[^']|'')*'|"(?:[^"\\]|\\.)*")|(\b(?:function|end|if|elseif|else|for|while|switch|case|otherwise|try|catch|return|break|continue|global|persistent|classdef|properties|methods|events|enumeration|true|false)\b)|(\b[A-Za-z_][A-Za-z0-9_]*(?=\s*\())|(\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?[i]?\b)/g;
2985
3001
  const highlighted = highlightCodeTokens(source, matPattern, (match) => {
2986
3002
  if (match[1]) return "hl-code-com";
2987
3003
  if (match[2]) return "hl-code-str";
2988
3004
  if (match[3]) return "hl-code-kw";
2989
- if (match[4]) return "hl-code-num";
3005
+ if (match[4]) return "hl-code-fn";
3006
+ if (match[5]) return "hl-code-num";
2990
3007
  return "hl-code";
2991
3008
  });
2992
3009
  return "<span class='hl-code'>" + highlighted + "</span>";
@@ -3084,7 +3101,13 @@
3084
3101
  }
3085
3102
 
3086
3103
  if (inFence) {
3087
- out.push(line.length > 0 ? highlightCodeLine(line, fenceLanguage) : EMPTY_OVERLAY_LINE);
3104
+ if (line.length === 0) {
3105
+ out.push(EMPTY_OVERLAY_LINE);
3106
+ } else if (fenceLanguage) {
3107
+ out.push(highlightCodeLine(line, fenceLanguage));
3108
+ } else {
3109
+ out.push(wrapHighlight("hl-md-code", line));
3110
+ }
3088
3111
  continue;
3089
3112
  }
3090
3113
 
package/client/studio.css CHANGED
@@ -482,6 +482,10 @@
482
482
  }
483
483
 
484
484
  .hl-code {
485
+ color: var(--text);
486
+ }
487
+
488
+ .hl-md-code {
485
489
  color: var(--md-code);
486
490
  }
487
491
 
@@ -508,6 +512,18 @@
508
512
  color: var(--syntax-variable);
509
513
  }
510
514
 
515
+ .hl-code-fn {
516
+ color: var(--syntax-function);
517
+ }
518
+
519
+ .hl-code-type {
520
+ color: var(--syntax-type);
521
+ }
522
+
523
+ .hl-code-op {
524
+ color: var(--syntax-operator);
525
+ }
526
+
511
527
  .hl-diff-add {
512
528
  color: var(--ok);
513
529
  background: rgba(46, 160, 67, 0.12);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.5.39",
3
+ "version": "0.5.40",
4
4
  "description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview",
5
5
  "type": "module",
6
6
  "license": "MIT",