pi-studio 0.5.38 → 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 +15 -0
- package/client/studio-client.js +74 -26
- package/client/studio.css +16 -0
- package/index.ts +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ 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
|
+
|
|
13
|
+
## [0.5.39] — 2026-03-30
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Studio now supports the familiar `Cmd/Ctrl+S` shortcut for saving editor content.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- `Cmd/Ctrl+S` now triggers **Save editor** when a direct save path is available, and falls back to **Save editor as…** otherwise.
|
|
20
|
+
- Save button tooltips and the footer shortcut hint now advertise the save shortcut explicitly.
|
|
21
|
+
|
|
7
22
|
## [0.5.38] — 2026-03-29
|
|
8
23
|
|
|
9
24
|
### Added
|
package/client/studio-client.js
CHANGED
|
@@ -878,6 +878,19 @@
|
|
|
878
878
|
return true;
|
|
879
879
|
}
|
|
880
880
|
|
|
881
|
+
function triggerEditorSaveShortcut() {
|
|
882
|
+
if (saveOverBtn && !saveOverBtn.disabled && !saveOverBtn.hidden) {
|
|
883
|
+
saveOverBtn.click();
|
|
884
|
+
return true;
|
|
885
|
+
}
|
|
886
|
+
if (saveAsBtn && !saveAsBtn.disabled && !saveAsBtn.hidden) {
|
|
887
|
+
saveAsBtn.click();
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
setStatus("Save is unavailable right now.", "warning");
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
|
|
881
894
|
function handlePaneShortcut(event) {
|
|
882
895
|
if (!event || event.defaultPrevented) return;
|
|
883
896
|
|
|
@@ -914,6 +927,18 @@
|
|
|
914
927
|
return;
|
|
915
928
|
}
|
|
916
929
|
|
|
930
|
+
const isSaveShortcut =
|
|
931
|
+
key.toLowerCase() === "s"
|
|
932
|
+
&& (event.metaKey || event.ctrlKey)
|
|
933
|
+
&& !event.altKey
|
|
934
|
+
&& !event.shiftKey;
|
|
935
|
+
|
|
936
|
+
if (isSaveShortcut) {
|
|
937
|
+
event.preventDefault();
|
|
938
|
+
triggerEditorSaveShortcut();
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
|
|
917
942
|
if (plainEscape) {
|
|
918
943
|
const activeKind = getAbortablePendingKind();
|
|
919
944
|
if (activeKind === "direct" || activeKind === "critique") {
|
|
@@ -2525,11 +2550,11 @@
|
|
|
2525
2550
|
|
|
2526
2551
|
var effectivePath = getEffectiveSavePath();
|
|
2527
2552
|
if (effectivePath) {
|
|
2528
|
-
saveOverBtn.title = "Overwrite file: " + effectivePath;
|
|
2553
|
+
saveOverBtn.title = "Overwrite file: " + effectivePath + " · Shortcut: Cmd/Ctrl+S.";
|
|
2529
2554
|
return;
|
|
2530
2555
|
}
|
|
2531
2556
|
|
|
2532
|
-
saveOverBtn.title = "Save editor is available after opening a file, setting a working dir, or using Save editor as…";
|
|
2557
|
+
saveOverBtn.title = "Save editor is available after opening a file, setting a working dir, or using Save editor as…. Shortcut: Cmd/Ctrl+S falls back to Save editor as… when needed.";
|
|
2533
2558
|
}
|
|
2534
2559
|
|
|
2535
2560
|
function syncActionButtons() {
|
|
@@ -2743,7 +2768,7 @@
|
|
|
2743
2768
|
}
|
|
2744
2769
|
|
|
2745
2770
|
if (match[1]) {
|
|
2746
|
-
out += wrapHighlight("hl-code", token);
|
|
2771
|
+
out += wrapHighlight("hl-md-code", token);
|
|
2747
2772
|
} else if (match[2]) {
|
|
2748
2773
|
const linkMatch = token.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
2749
2774
|
if (linkMatch) {
|
|
@@ -2845,37 +2870,43 @@
|
|
|
2845
2870
|
}
|
|
2846
2871
|
|
|
2847
2872
|
if (lang === "javascript" || lang === "typescript") {
|
|
2848
|
-
const jsPattern = /(
|
|
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;
|
|
2849
2874
|
const highlighted = highlightCodeTokens(source, jsPattern, (match) => {
|
|
2850
2875
|
if (match[1]) return "hl-code-com";
|
|
2851
2876
|
if (match[2]) return "hl-code-str";
|
|
2852
2877
|
if (match[3]) return "hl-code-kw";
|
|
2853
|
-
if (match[4]) return "hl-code-
|
|
2878
|
+
if (match[4]) return "hl-code-fn";
|
|
2879
|
+
if (match[5]) return "hl-code-type";
|
|
2880
|
+
if (match[6]) return "hl-code-num";
|
|
2854
2881
|
return "hl-code";
|
|
2855
2882
|
});
|
|
2856
2883
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2857
2884
|
}
|
|
2858
2885
|
|
|
2859
2886
|
if (lang === "python") {
|
|
2860
|
-
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;
|
|
2861
2888
|
const highlighted = highlightCodeTokens(source, pyPattern, (match) => {
|
|
2862
2889
|
if (match[1]) return "hl-code-com";
|
|
2863
2890
|
if (match[2]) return "hl-code-str";
|
|
2864
|
-
if (match[3]) return "hl-code-
|
|
2865
|
-
if (match[4]) return "hl-code-
|
|
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";
|
|
2866
2896
|
return "hl-code";
|
|
2867
2897
|
});
|
|
2868
2898
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2869
2899
|
}
|
|
2870
2900
|
|
|
2871
2901
|
if (lang === "bash") {
|
|
2872
|
-
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;
|
|
2873
2903
|
const highlighted = highlightCodeTokens(source, shPattern, (match) => {
|
|
2874
2904
|
if (match[1]) return "hl-code-com";
|
|
2875
2905
|
if (match[2]) return "hl-code-str";
|
|
2876
2906
|
if (match[3]) return "hl-code-var";
|
|
2877
2907
|
if (match[4]) return "hl-code-kw";
|
|
2878
|
-
if (match[5]) return "hl-code-
|
|
2908
|
+
if (match[5]) return "hl-code-fn";
|
|
2909
|
+
if (match[6]) return "hl-code-num";
|
|
2879
2910
|
return "hl-code";
|
|
2880
2911
|
});
|
|
2881
2912
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
@@ -2894,74 +2925,85 @@
|
|
|
2894
2925
|
}
|
|
2895
2926
|
|
|
2896
2927
|
if (lang === "rust") {
|
|
2897
|
-
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;
|
|
2898
2929
|
const highlighted = highlightCodeTokens(source, rustPattern, (match) => {
|
|
2899
2930
|
if (match[1]) return "hl-code-com";
|
|
2900
2931
|
if (match[2]) return "hl-code-str";
|
|
2901
|
-
if (match[3]) return "hl-code-
|
|
2902
|
-
if (match[4]) return "hl-code-
|
|
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";
|
|
2903
2937
|
return "hl-code";
|
|
2904
2938
|
});
|
|
2905
2939
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2906
2940
|
}
|
|
2907
2941
|
|
|
2908
2942
|
if (lang === "c" || lang === "cpp") {
|
|
2909
|
-
const cPattern = /(
|
|
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;
|
|
2910
2944
|
const highlighted = highlightCodeTokens(source, cPattern, (match) => {
|
|
2911
2945
|
if (match[1]) return "hl-code-com";
|
|
2912
2946
|
if (match[2]) return "hl-code-str";
|
|
2913
2947
|
if (match[3]) return "hl-code-kw";
|
|
2914
2948
|
if (match[4]) return "hl-code-kw";
|
|
2915
|
-
if (match[5]) return "hl-code-
|
|
2949
|
+
if (match[5]) return "hl-code-fn";
|
|
2950
|
+
if (match[6]) return "hl-code-type";
|
|
2951
|
+
if (match[7]) return "hl-code-num";
|
|
2916
2952
|
return "hl-code";
|
|
2917
2953
|
});
|
|
2918
2954
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2919
2955
|
}
|
|
2920
2956
|
|
|
2921
2957
|
if (lang === "julia") {
|
|
2922
|
-
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;
|
|
2923
2959
|
const highlighted = highlightCodeTokens(source, jlPattern, (match) => {
|
|
2924
2960
|
if (match[1]) return "hl-code-com";
|
|
2925
2961
|
if (match[2]) return "hl-code-str";
|
|
2926
|
-
if (match[3]) return "hl-code-
|
|
2927
|
-
if (match[4]) return "hl-code-
|
|
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";
|
|
2928
2967
|
return "hl-code";
|
|
2929
2968
|
});
|
|
2930
2969
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2931
2970
|
}
|
|
2932
2971
|
|
|
2933
2972
|
if (lang === "fortran") {
|
|
2934
|
-
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;
|
|
2935
2974
|
const highlighted = highlightCodeTokens(source, fPattern, (match) => {
|
|
2936
2975
|
if (match[1]) return "hl-code-com";
|
|
2937
2976
|
if (match[2]) return "hl-code-str";
|
|
2938
2977
|
if (match[3]) return "hl-code-kw";
|
|
2939
|
-
if (match[4]) return "hl-code-
|
|
2978
|
+
if (match[4]) return "hl-code-fn";
|
|
2979
|
+
if (match[5]) return "hl-code-num";
|
|
2940
2980
|
return "hl-code";
|
|
2941
2981
|
});
|
|
2942
2982
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2943
2983
|
}
|
|
2944
2984
|
|
|
2945
2985
|
if (lang === "r") {
|
|
2946
|
-
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;
|
|
2947
2987
|
const highlighted = highlightCodeTokens(source, rPattern, (match) => {
|
|
2948
2988
|
if (match[1]) return "hl-code-com";
|
|
2949
2989
|
if (match[2]) return "hl-code-str";
|
|
2950
2990
|
if (match[3]) return "hl-code-kw";
|
|
2951
|
-
if (match[4]) return "hl-code-
|
|
2952
|
-
if (match[5]) return "hl-code-
|
|
2991
|
+
if (match[4]) return "hl-code-op";
|
|
2992
|
+
if (match[5]) return "hl-code-fn";
|
|
2993
|
+
if (match[6]) return "hl-code-num";
|
|
2953
2994
|
return "hl-code";
|
|
2954
2995
|
});
|
|
2955
2996
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
2956
2997
|
}
|
|
2957
2998
|
|
|
2958
2999
|
if (lang === "matlab") {
|
|
2959
|
-
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;
|
|
2960
3001
|
const highlighted = highlightCodeTokens(source, matPattern, (match) => {
|
|
2961
3002
|
if (match[1]) return "hl-code-com";
|
|
2962
3003
|
if (match[2]) return "hl-code-str";
|
|
2963
3004
|
if (match[3]) return "hl-code-kw";
|
|
2964
|
-
if (match[4]) return "hl-code-
|
|
3005
|
+
if (match[4]) return "hl-code-fn";
|
|
3006
|
+
if (match[5]) return "hl-code-num";
|
|
2965
3007
|
return "hl-code";
|
|
2966
3008
|
});
|
|
2967
3009
|
return "<span class='hl-code'>" + highlighted + "</span>";
|
|
@@ -3059,7 +3101,13 @@
|
|
|
3059
3101
|
}
|
|
3060
3102
|
|
|
3061
3103
|
if (inFence) {
|
|
3062
|
-
|
|
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
|
+
}
|
|
3063
3111
|
continue;
|
|
3064
3112
|
}
|
|
3065
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/index.ts
CHANGED
|
@@ -5732,8 +5732,8 @@ ${cssVarsBlock}
|
|
|
5732
5732
|
<header>
|
|
5733
5733
|
<h1><span class="app-logo" aria-hidden="true">π</span> Studio <span class="app-subtitle">${appSubtitle}</span></h1>
|
|
5734
5734
|
<div class="controls">
|
|
5735
|
-
<button id="saveAsBtn" type="button" title="Save editor content to a new file path.">Save editor as…</button>
|
|
5736
|
-
<button id="saveOverBtn" type="button" title="Overwrite current file with editor content."
|
|
5735
|
+
<button id="saveAsBtn" type="button" title="Save editor content to a new file path. Cmd/Ctrl+S falls back here when no direct save path is available.">Save editor as…</button>
|
|
5736
|
+
<button id="saveOverBtn" type="button" title="Overwrite current file with editor content. Shortcut: Cmd/Ctrl+S.">Save editor</button>
|
|
5737
5737
|
<label class="file-label" title="Load a local file into editor text.">Load file content<input id="fileInput" type="file" accept=".md,.markdown,.mdx,.qmd,.js,.mjs,.cjs,.jsx,.ts,.mts,.cts,.tsx,.py,.pyw,.sh,.bash,.zsh,.json,.jsonc,.json5,.rs,.c,.h,.cpp,.cxx,.cc,.hpp,.hxx,.jl,.f90,.f95,.f03,.f,.for,.r,.R,.m,.tex,.latex,.diff,.patch,.java,.go,.rb,.swift,.html,.htm,.css,.xml,.yaml,.yml,.toml,.lua,.txt,.rst,.adoc" /></label>
|
|
5738
5738
|
<button id="loadGitDiffBtn" type="button" title="Load the current git diff from the Studio context into the editor.">Load git diff</button>
|
|
5739
5739
|
<button id="getEditorBtn" type="button" title="Load the current terminal editor draft into Studio.">Load from pi editor</button>
|
|
@@ -5884,7 +5884,7 @@ ${cssVarsBlock}
|
|
|
5884
5884
|
<footer>
|
|
5885
5885
|
<span id="statusLine"><span id="statusSpinner" aria-hidden="true"> </span><span id="status">Booting studio…</span></span>
|
|
5886
5886
|
<span id="footerMeta" class="footer-meta"><span id="footerMetaText" class="footer-meta-text">Model: ${initialModel} · Terminal: ${initialTerminal} · Context: unknown</span><button id="compactBtn" class="footer-compact-btn" type="button" title="Trigger pi context compaction now.">Compact</button></span>
|
|
5887
|
-
<span class="shortcut-hint">Focus pane: F10 (or Cmd/Ctrl+Esc) to toggle · Run / queue steering: Cmd/Ctrl+Enter · Stop request: Esc</span>
|
|
5887
|
+
<span class="shortcut-hint">Focus pane: F10 (or Cmd/Ctrl+Esc) to toggle · Save editor: Cmd/Ctrl+S · Run / queue steering: Cmd/Ctrl+Enter · Stop request: Esc</span>
|
|
5888
5888
|
</footer>
|
|
5889
5889
|
|
|
5890
5890
|
<div id="scratchpadOverlay" class="scratchpad-overlay" hidden>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.5.
|
|
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",
|