Add Help Assistant

This commit is contained in:
2025-09-02 16:49:11 +02:00
parent d71b435d31
commit fc18c30eaa

509
main2.pb
View File

@@ -356,7 +356,6 @@ Procedure.i RunExe(*call.runProgramCallStruct)
EndProcedure
; =============================================================================
;-GUI
; =============================================================================
@@ -461,33 +460,33 @@ Enumeration GadgetsIDs
#GID_CbScope
#GID_BtnSaveCfg
; === TAB 5: TAGS ===
#GID_Tab_Tags
#GID_FrmTagsList
#GID_ListTags
#GID_BtnCreateTag
#GID_BtnDeleteTag
#GID_BtnPushTag
#GID_BtnFetchTags
#GID_BtnCheckoutTag
; === TAB 5: TAGS ===
#GID_Tab_Tags
#GID_FrmTagsList
#GID_ListTags
#GID_BtnCreateTag
#GID_BtnDeleteTag
#GID_BtnPushTag
#GID_BtnFetchTags
#GID_BtnCheckoutTag
; Frame création de tag
#GID_FrmCreateTag
#GID_LblTagName
#GID_EdTagName
#GID_LblTagType
#GID_OptTagLight
#GID_OptTagAnnotated
#GID_LblTagMessage
#GID_EdTagMessage
#GID_LblTagTarget
#GID_CbTagTarget
#GID_BtnApplyTag
#GID_BtnCancelTag
; Frame création de tag
#GID_FrmCreateTag
#GID_LblTagName
#GID_EdTagName
#GID_LblTagType
#GID_OptTagLight
#GID_OptTagAnnotated
#GID_LblTagMessage
#GID_EdTagMessage
#GID_LblTagTarget
#GID_CbTagTarget
#GID_BtnApplyTag
#GID_BtnCancelTag
; Détails du tag
#GID_FrmTagDetails
#GID_TxtTagDetails
; Détails du tag
#GID_FrmTagDetails
#GID_TxtTagDetails
; --- App settings (langue) ---
#GID_FrmApp
@@ -655,36 +654,36 @@ Procedure ResizeGUI()
; Onglet 4 : statique (inchangé)
; === Onglet 5 : Tags ===
Protected tagsListH = (panelH - #UI_Inset*4) / 2
Protected tagsListInnerH = tagsListH - #UI_FrameHeaderH - #UI_RowH - #UI_Inset*2
If tagsListInnerH < 100 : tagsListInnerH = 100 : EndIf
Protected tagsListH = (panelH - #UI_Inset*4) / 2
Protected tagsListInnerH = tagsListH - #UI_FrameHeaderH - #UI_RowH - #UI_Inset*2
If tagsListInnerH < 100 : tagsListInnerH = 100 : EndIf
ResizeGadget(#GID_FrmTagsList, #UI_Inset, #UI_Inset, panelW - #UI_Inset*2, tagsListH)
ResizeGadget(#GID_ListTags, #UI_Inset, #UI_FrameHeaderH,
ResizeGadget(#GID_FrmTagsList, #UI_Inset, #UI_Inset, panelW - #UI_Inset*2, tagsListH)
ResizeGadget(#GID_ListTags, #UI_Inset, #UI_FrameHeaderH,
GadgetWidth(#GID_FrmTagsList) - #UI_Inset*2, tagsListInnerH)
Protected yTagsButtons = GadgetHeight(#GID_FrmTagsList) - #UI_RowH - #UI_Inset
ResizeGadget(#GID_BtnCheckoutTag, #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnDeleteTag, RightOf(#GID_BtnCheckoutTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnPushTag, RightOf(#GID_BtnDeleteTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnFetchTags, RightOf(#GID_BtnPushTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
Protected yTagsButtons = GadgetHeight(#GID_FrmTagsList) - #UI_RowH - #UI_Inset
ResizeGadget(#GID_BtnCheckoutTag, #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnDeleteTag, RightOf(#GID_BtnCheckoutTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnPushTag, RightOf(#GID_BtnDeleteTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
ResizeGadget(#GID_BtnFetchTags, RightOf(#GID_BtnPushTag) + #UI_Inset, yTagsButtons, 110, #UI_RowH)
Protected yCreateTag = BottomOf(#GID_FrmTagsList) + #UI_Inset
Protected createTagW = (panelW - #UI_Inset*3) / 2
Protected createTagH = panelH - yCreateTag - #UI_Inset
Protected yCreateTag = BottomOf(#GID_FrmTagsList) + #UI_Inset
Protected createTagW = (panelW - #UI_Inset*3) / 2
Protected createTagH = panelH - yCreateTag - #UI_Inset
ResizeGadget(#GID_FrmCreateTag, #UI_Inset, yCreateTag, createTagW, createTagH)
ResizeGadget(#GID_EdTagName, RightOf(#GID_LblTagName) + #UI_Inset,
ResizeGadget(#GID_FrmCreateTag, #UI_Inset, yCreateTag, createTagW, createTagH)
ResizeGadget(#GID_EdTagName, RightOf(#GID_LblTagName) + #UI_Inset,
GadgetY(#GID_EdTagName), createTagW - 120 - #UI_Inset*3, #UI_RowH)
ResizeGadget(#GID_CbTagTarget, RightOf(#GID_LblTagTarget) + #UI_Inset,
ResizeGadget(#GID_CbTagTarget, RightOf(#GID_LblTagTarget) + #UI_Inset,
GadgetY(#GID_CbTagTarget), createTagW - 120 - #UI_Inset*3, #UI_RowH)
ResizeGadget(#GID_EdTagMessage, #UI_Inset, GadgetY(#GID_EdTagMessage),
ResizeGadget(#GID_EdTagMessage, #UI_Inset, GadgetY(#GID_EdTagMessage),
createTagW - #UI_Inset*2, 80)
Protected xDetails = RightOf(#GID_FrmCreateTag) + #UI_Inset
ResizeGadget(#GID_FrmTagDetails, xDetails, yCreateTag,
Protected xDetails = RightOf(#GID_FrmCreateTag) + #UI_Inset
ResizeGadget(#GID_FrmTagDetails, xDetails, yCreateTag,
panelW - xDetails - #UI_Inset, createTagH)
ResizeGadget(#GID_TxtTagDetails, xDetails + #UI_Inset,
ResizeGadget(#GID_TxtTagDetails, xDetails + #UI_Inset,
yCreateTag + #UI_FrameHeaderH,
GadgetWidth(#GID_FrmTagDetails) - #UI_Inset*2,
GadgetHeight(#GID_FrmTagDetails) - #UI_FrameHeaderH - #UI_Inset)
@@ -764,17 +763,17 @@ Procedure ApplyToolTips()
; --- Tags ---
GadgetToolTip(#GID_ListTags, T("tip.tags.list", "Liste des tags du dépôt"))
GadgetToolTip(#GID_BtnCreateTag, T("tip.tags.create", "Créer un nouveau tag"))
GadgetToolTip(#GID_BtnDeleteTag, T("tip.tags.delete", "Supprimer le tag sélectionné"))
GadgetToolTip(#GID_BtnPushTag, T("tip.tags.push", "Envoyer le tag vers le dépôt distant"))
GadgetToolTip(#GID_BtnFetchTags, T("tip.tags.fetch", "Récupérer les tags du dépôt distant"))
GadgetToolTip(#GID_BtnCheckoutTag, T("tip.tags.checkout", "Basculer vers le tag sélectionné"))
GadgetToolTip(#GID_EdTagName, T("tip.tags.name", "Nom du tag (ex: v1.0.0)"))
GadgetToolTip(#GID_OptTagLight, T("tip.tags.light", "Tag léger (référence simple)"))
GadgetToolTip(#GID_OptTagAnnotated,T("tip.tags.annotated", "Tag annoté (avec message et signature)"))
GadgetToolTip(#GID_CbTagTarget, T("tip.tags.target", "Commit ou branche à taguer"))
GadgetToolTip(#GID_EdTagMessage, T("tip.tags.message", "Message descriptif du tag annoté"))
GadgetToolTip(#GID_ListTags, T("tip.tags.list", "Liste des tags du dépôt"))
GadgetToolTip(#GID_BtnCreateTag, T("tip.tags.create", "Créer un nouveau tag"))
GadgetToolTip(#GID_BtnDeleteTag, T("tip.tags.delete", "Supprimer le tag sélectionné"))
GadgetToolTip(#GID_BtnPushTag, T("tip.tags.push", "Envoyer le tag vers le dépôt distant"))
GadgetToolTip(#GID_BtnFetchTags, T("tip.tags.fetch", "Récupérer les tags du dépôt distant"))
GadgetToolTip(#GID_BtnCheckoutTag, T("tip.tags.checkout", "Basculer vers le tag sélectionné"))
GadgetToolTip(#GID_EdTagName, T("tip.tags.name", "Nom du tag (ex: v1.0.0)"))
GadgetToolTip(#GID_OptTagLight, T("tip.tags.light", "Tag léger (référence simple)"))
GadgetToolTip(#GID_OptTagAnnotated,T("tip.tags.annotated", "Tag annoté (avec message et signature)"))
GadgetToolTip(#GID_CbTagTarget, T("tip.tags.target", "Commit ou branche à taguer"))
GadgetToolTip(#GID_EdTagMessage, T("tip.tags.message", "Message descriptif du tag annoté"))
; --- Help (optionnel) ---
@@ -1051,104 +1050,104 @@ Procedure OpenGUI()
; ===========================================================================
; TAB 5: TAGS
; ===========================================================================
AddGadgetItem(#GID_Panel, #Panel_Tags, T("Tabs.Tags", "Tags"))
; TAB 5: TAGS
; ===========================================================================
AddGadgetItem(#GID_Panel, #Panel_Tags, T("Tabs.Tags", "Tags"))
; ---- Frame: Liste des tags ----
FrameGadget(#GID_FrmTagsList, #UI_Inset, #UI_Inset,
; ---- Frame: Liste des tags ----
FrameGadget(#GID_FrmTagsList, #UI_Inset, #UI_Inset,
GadgetWidth(#GID_Panel) - #UI_Inset*2,
(panelH - #UI_Inset*4) / 2,
T("Tags.FrameList", "Tags existants"), #PB_Frame_Container)
; Liste des tags avec colonnes
ListIconGadget(#GID_ListTags, #UI_Inset, #UI_FrameHeaderH,
; Liste des tags avec colonnes
ListIconGadget(#GID_ListTags, #UI_Inset, #UI_FrameHeaderH,
GadgetWidth(#GID_FrmTagsList) - #UI_Inset*2,
GadgetHeight(#GID_FrmTagsList) - #UI_FrameHeaderH - #UI_RowH - #UI_Inset*2,
T("Tags.List.Name", "Nom"), 200,
#PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
AddGadgetColumn(#GID_ListTags, 1, T("Tags.List.Type", "Type"), 100)
AddGadgetColumn(#GID_ListTags, 2, T("Tags.List.Commit", "Commit"), 100)
AddGadgetColumn(#GID_ListTags, 3, T("Tags.List.Date", "Date"), 150)
AddGadgetColumn(#GID_ListTags, 4, T("Tags.List.Author", "Auteur"), 150)
AddGadgetColumn(#GID_ListTags, 5, T("Tags.List.Message", "Message"), 300)
AddGadgetColumn(#GID_ListTags, 1, T("Tags.List.Type", "Type"), 100)
AddGadgetColumn(#GID_ListTags, 2, T("Tags.List.Commit", "Commit"), 100)
AddGadgetColumn(#GID_ListTags, 3, T("Tags.List.Date", "Date"), 150)
AddGadgetColumn(#GID_ListTags, 4, T("Tags.List.Author", "Auteur"), 150)
AddGadgetColumn(#GID_ListTags, 5, T("Tags.List.Message", "Message"), 300)
; Boutons d'action pour les tags
Define yTagButtons = GadgetHeight(#GID_FrmTagsList) - #UI_RowH - #UI_Inset
ButtonGadget(#GID_BtnCheckoutTag, #UI_Inset, yTagButtons, 110, #UI_RowH,
; Boutons d'action pour les tags
Define yTagButtons = GadgetHeight(#GID_FrmTagsList) - #UI_RowH - #UI_Inset
ButtonGadget(#GID_BtnCheckoutTag, #UI_Inset, yTagButtons, 110, #UI_RowH,
T("Tags.Button.Checkout", "Checkout"))
ButtonGadget(#GID_BtnDeleteTag, RightOf(#GID_BtnCheckoutTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
ButtonGadget(#GID_BtnDeleteTag, RightOf(#GID_BtnCheckoutTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
T("Tags.Button.Delete", "Supprimer"))
ButtonGadget(#GID_BtnPushTag, RightOf(#GID_BtnDeleteTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
ButtonGadget(#GID_BtnPushTag, RightOf(#GID_BtnDeleteTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
T("Tags.Button.Push", "Push Tag"))
ButtonGadget(#GID_BtnFetchTags, RightOf(#GID_BtnPushTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
ButtonGadget(#GID_BtnFetchTags, RightOf(#GID_BtnPushTag) + #UI_Inset, yTagButtons, 110, #UI_RowH,
T("Tags.Button.Fetch", "Fetch Tags"))
CloseGadgetList()
CloseGadgetList()
; ---- Frame: Création de tag ----
Define yCreateTag = BottomOf(#GID_FrmTagsList) + #UI_Inset
FrameGadget(#GID_FrmCreateTag, #UI_Inset, yCreateTag,
; ---- Frame: Création de tag ----
Define yCreateTag = BottomOf(#GID_FrmTagsList) + #UI_Inset
FrameGadget(#GID_FrmCreateTag, #UI_Inset, yCreateTag,
(GadgetWidth(#GID_Panel) - #UI_Inset*3) / 2,
panelH - yCreateTag - #UI_Inset,
T("Tags.FrameCreate", "Créer un tag"), #PB_Frame_Container)
; Nom du tag
TextGadget(#GID_LblTagName, #UI_Inset, #UI_FrameHeaderH + #UI_Inset, 100, #UI_RowH,
; Nom du tag
TextGadget(#GID_LblTagName, #UI_Inset, #UI_FrameHeaderH + #UI_Inset, 100, #UI_RowH,
T("Tags.Label.Name", "Nom :"))
StringGadget(#GID_EdTagName, RightOf(#GID_LblTagName) + #UI_Inset,
StringGadget(#GID_EdTagName, RightOf(#GID_LblTagName) + #UI_Inset,
#UI_FrameHeaderH + #UI_Inset,
GadgetWidth(#GID_FrmCreateTag) - 120 - #UI_Inset*3, #UI_RowH, "")
; Type de tag
Define yTagType = BottomOf(#GID_EdTagName) + #UI_Inset
TextGadget(#GID_LblTagType, #UI_Inset, yTagType, 100, #UI_RowH,
; Type de tag
Define yTagType = BottomOf(#GID_EdTagName) + #UI_Inset
TextGadget(#GID_LblTagType, #UI_Inset, yTagType, 100, #UI_RowH,
T("Tags.Label.Type", "Type :"))
OptionGadget(#GID_OptTagLight, RightOf(#GID_LblTagType) + #UI_Inset, yTagType, 100, #UI_RowH,
OptionGadget(#GID_OptTagLight, RightOf(#GID_LblTagType) + #UI_Inset, yTagType, 100, #UI_RowH,
T("Tags.Type.Light", "Léger"))
OptionGadget(#GID_OptTagAnnotated, RightOf(#GID_OptTagLight) + #UI_Inset, yTagType, 100, #UI_RowH,
OptionGadget(#GID_OptTagAnnotated, RightOf(#GID_OptTagLight) + #UI_Inset, yTagType, 100, #UI_RowH,
T("Tags.Type.Annotated", "Annoté"))
SetGadgetState(#GID_OptTagAnnotated, 1) ; Par défaut : tag annoté
SetGadgetState(#GID_OptTagAnnotated, 1) ; Par défaut : tag annoté
; Cible du tag (commit/branche)
Define yTagTarget = BottomOf(#GID_OptTagLight) + #UI_Inset
TextGadget(#GID_LblTagTarget, #UI_Inset, yTagTarget, 100, #UI_RowH,
; Cible du tag (commit/branche)
Define yTagTarget = BottomOf(#GID_OptTagLight) + #UI_Inset
TextGadget(#GID_LblTagTarget, #UI_Inset, yTagTarget, 100, #UI_RowH,
T("Tags.Label.Target", "Cible :"))
ComboBoxGadget(#GID_CbTagTarget, RightOf(#GID_LblTagTarget) + #UI_Inset, yTagTarget,
ComboBoxGadget(#GID_CbTagTarget, RightOf(#GID_LblTagTarget) + #UI_Inset, yTagTarget,
GadgetWidth(#GID_FrmCreateTag) - 120 - #UI_Inset*3, #UI_RowH)
AddGadgetItem(#GID_CbTagTarget, -1, "HEAD")
AddGadgetItem(#GID_CbTagTarget, -1, T("Tags.Target.SelectCommit", "Sélectionner un commit..."))
SetGadgetState(#GID_CbTagTarget, 0)
AddGadgetItem(#GID_CbTagTarget, -1, "HEAD")
AddGadgetItem(#GID_CbTagTarget, -1, T("Tags.Target.SelectCommit", "Sélectionner un commit..."))
SetGadgetState(#GID_CbTagTarget, 0)
; Message du tag (pour les tags annotés)
Define yTagMessage = BottomOf(#GID_CbTagTarget) + #UI_Inset
TextGadget(#GID_LblTagMessage, #UI_Inset, yTagMessage, 100, #UI_RowH,
; Message du tag (pour les tags annotés)
Define yTagMessage = BottomOf(#GID_CbTagTarget) + #UI_Inset
TextGadget(#GID_LblTagMessage, #UI_Inset, yTagMessage, 100, #UI_RowH,
T("Tags.Label.Message", "Message :"))
EditorGadget(#GID_EdTagMessage, #UI_Inset, BottomOf(#GID_LblTagMessage) + 5,
EditorGadget(#GID_EdTagMessage, #UI_Inset, BottomOf(#GID_LblTagMessage) + 5,
GadgetWidth(#GID_FrmCreateTag) - #UI_Inset*2, 80)
; Boutons Créer/Annuler
Define yCreateButtons = BottomOf(#GID_EdTagMessage) + #UI_Inset
ButtonGadget(#GID_BtnApplyTag, #UI_Inset, yCreateButtons, 100, #UI_RowH,
; Boutons Créer/Annuler
Define yCreateButtons = BottomOf(#GID_EdTagMessage) + #UI_Inset
ButtonGadget(#GID_BtnApplyTag, #UI_Inset, yCreateButtons, 100, #UI_RowH,
T("Tags.Button.Create", "Créer"))
ButtonGadget(#GID_BtnCancelTag, RightOf(#GID_BtnApplyTag) + #UI_Inset, yCreateButtons, 100, #UI_RowH,
ButtonGadget(#GID_BtnCancelTag, RightOf(#GID_BtnApplyTag) + #UI_Inset, yCreateButtons, 100, #UI_RowH,
T("Tags.Button.Cancel", "Annuler"))
ButtonGadget(#GID_BtnCreateTag, RightOf(#GID_BtnCancelTag) + #UI_Inset, yCreateButtons, 140, #UI_RowH,
ButtonGadget(#GID_BtnCreateTag, RightOf(#GID_BtnCancelTag) + #UI_Inset, yCreateButtons, 140, #UI_RowH,
T("Tags.Button.NewTag", "+ Nouveau Tag"))
CloseGadgetList()
CloseGadgetList()
; ---- Frame: Détails du tag sélectionné ----
Define xDetails = RightOf(#GID_FrmCreateTag) + #UI_Inset
FrameGadget(#GID_FrmTagDetails, xDetails, yCreateTag,
; ---- Frame: Détails du tag sélectionné ----
Define xDetails = RightOf(#GID_FrmCreateTag) + #UI_Inset
FrameGadget(#GID_FrmTagDetails, xDetails, yCreateTag,
GadgetWidth(#GID_Panel) - xDetails - #UI_Inset,
panelH - yCreateTag - #UI_Inset,
T("Tags.FrameDetails", "Détails du tag"), #PB_Frame_Container)
EditorGadget(#GID_TxtTagDetails, xDetails + #UI_Inset,
EditorGadget(#GID_TxtTagDetails, xDetails + #UI_Inset,
yCreateTag + #UI_FrameHeaderH,
GadgetWidth(#GID_FrmTagDetails) - #UI_Inset*2,
GadgetHeight(#GID_FrmTagDetails) - #UI_FrameHeaderH - #UI_Inset,
#PB_Editor_ReadOnly | #PB_Editor_WordWrap)
CloseGadgetList()
CloseGadgetList()
@@ -1159,11 +1158,12 @@ CloseGadgetList()
; HELP AREA (RIGHT SIDE) / ZONE AIDE (À DROITE)
; ===========================================================================
FrameGadget(#GID_HelpFrame, #UI_Margin*2 + #UI_PanelStartW, #UI_Margin, 400, panelH, T("Help.FrameTitle","Aide"), #PB_Frame_Container)
WebViewGadget(#GID_HelpWeb, GadgetX(#GID_HelpFrame) + #UI_Inset, GadgetY(#GID_HelpFrame) + #UI_FrameHeaderH, GadgetWidth(#GID_HelpFrame) - #UI_Inset*2, GadgetHeight(#GID_HelpFrame) - #UI_FrameHeaderH - #UI_Inset)
WebViewGadget(#GID_HelpWeb, #UI_Inset, #UI_FrameHeaderH, GadgetWidth(#GID_HelpFrame) - #UI_Inset*2, GadgetHeight(#GID_HelpFrame) - #UI_FrameHeaderH - #UI_Inset,#PB_WebView_Debug)
CloseGadgetList()
; Initial placement / placement initial
ResizeGUI()
;TODO Enable REsizeGui
;ResizeGUI()
; Tooltips
ApplyToolTips()
ProcedureReturn #True
@@ -1906,6 +1906,270 @@ Procedure readDirectory()
EndProcedure
; =============================================================================
;- HELP / ASSISTANT CONTEXTUEL
; =============================================================================
; -- Petit formateur pour T() avec %1 %2 %3
Procedure.s TF(key.s, Def.s, p1.s = "", p2.s = "", p3.s = "")
Protected s.s = T(key, Def)
If p1 <> "" : s = ReplaceString(s, "%1", p1) : EndIf
If p2 <> "" : s = ReplaceString(s, "%2", p2) : EndIf
If p3 <> "" : s = ReplaceString(s, "%3", p3) : EndIf
ProcedureReturn s
EndProcedure
; -- Échappe le HTML basique
Procedure.s HtmlEscape(s.s)
s = ReplaceString(s, "&", "&amp;")
s = ReplaceString(s, "<", "&lt;")
s = ReplaceString(s, ">", "&gt;")
s = ReplaceString(s, ~"\"", "&quot;")
ProcedureReturn s
EndProcedure
; -- file:// URL cross-plateforme
Procedure.s _ToFileURL(path$)
Protected p$ = ReplaceString(path$, "\", "/")
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
ProcedureReturn "file:///" + p$
CompilerDefault
ProcedureReturn "file://" + p$
CompilerEndSelect
EndProcedure
; -- Y a-t-il au moins un commit ?
Procedure.b _HasInitialCommit()
ProcedureReturn Bool(Git("rev-parse --verify HEAD") = 0)
EndProcedure
; -- Construis des petits "pills" d'état jolies
Procedure.s _Pill(text$)
ProcedureReturn ~"<span class='pill'>" + HtmlEscape(text$) + "</span>"
EndProcedure
; -- Procédure principale
Procedure RefreshHelp()
Protected repo.b = main\IsRepository
Protected workdir$ = GetCurrentDirectory()
Protected remoteText$ = SupTrim(GetGadgetText(#GID_EdRemote))
Protected hasRemoteCfg.b = main\hasRemoteUrl
Protected locBr$ = SupTrim(GetGadgetText(#GID_CbLocalBranch))
Protected remBr$ = SupTrim(GetGadgetText(#GID_CbRemoteBranch))
Protected hasCommit.b = #False
Protected cStaged.l = 0, cUnstaged.l = 0, cUntracked.l = 0, cConflicts.l = 0
Protected ahead.l, behind.l
; ========== Collecte dinfos ==========
If repo
hasCommit = _HasInitialCommit()
; -- Compte les états de travail à partir de main\Files()
Protected x$, y$
ForEach main\Files()
x$ = Left(main\Files()\status, 1)
y$ = Right(main\Files()\status, 1)
; Conflits
Select main\Files()\status
Case "DD","AU","UD","UA","DU","AA","UU"
cConflicts + 1 : Continue
EndSelect
If x$ = "U" Or y$ = "U" : cConflicts + 1 : Continue : EndIf
; Non suivis / ignorés
If main\Files()\status = "??" : cUntracked + 1 : Continue : EndIf
If main\Files()\status = "!!" : Continue : EndIf
; Staged (index) si X != espace
If x$ <> " " : cStaged + 1 : EndIf
; Non-staged (worktree) si Y != espace,? ,!
If y$ <> " " And y$ <> "?" And y$ <> "!" : cUnstaged + 1 : EndIf
Next
If hasRemoteCfg And locBr$ <> "" And remBr$ <> ""
; -- Récupère ahead/behind entre 2 branches
If Trim(locBr$) <> "" And Trim(remBr$) <> ""
If Git("rev-list --left-right --count " + locBr$ + "..." + remBr$) = 0
Protected counts$ = Trim(ReplaceString(ReplaceString(main\GitCall\output, #TAB$, " "), " ", " "))
ahead = Val(StringField(counts$, 1, " "))
behind = Val(StringField(counts$, 2, " "))
EndIf
EndIf
EndIf
EndIf
; ========== Construction HTML ==========
Protected html$
html$ + ~"<!doctype html><html><head><meta charset='utf-8'>"
html$ + ~"<style>"+
"body{font:14px -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:0;padding:16px 16px 40px;color:#1b1f23;}"+
"h1{font-size:18px;margin:0 0 10px;}"+
"h2{font-size:16px;margin:22px 0 8px;}"+
".section{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:14px 16px;margin-bottom:12px;box-shadow:0 1px 2px rgba(0,0,0,.04);}"+
"ul{margin:8px 0 0 18px;}"+
"li{margin:4px 0;}"+
".muted{color:#6b7280;}"+
".row{display:flex;gap:8px;flex-wrap:wrap;margin:6px 0;}"+
".pill{display:inline-block;padding:3px 8px;border-radius:999px;border:1px solid #e5e7eb;background:#f9fafb;font-size:12px}"+
"code{background:#f3f4f6;border:1px solid #e5e7eb;border-radius:6px;padding:1px 5px}"+
".callout{border-left:4px solid #3b82f6;padding-left:10px;margin:8px 0}"+
".ok{color:#16a34a}.warn{color:#d97706}.err{color:#dc2626}.info{color:#2563eb}"+
"</style></head><body>"
; --- En-tête
html$ + "<div class='section'>"
html$ + "<h1>" + HtmlEscape(T("help.title", "Assistant contextuel")) + "</h1>"
html$ + "<div class='row'>"
If repo
html$ + _Pill(T("help.pill.repo", "Dépôt détecté"))
Else
html$ + _Pill(T("help.pill.norepo", "Pas de dépôt"))
EndIf
If hasRemoteCfg
html$ + _Pill(TF("help.pill.remote", "Remote: %1", remoteText$))
EndIf
If locBr$ <> "" : html$ + _Pill(TF("help.pill.localbr", "Branche locale: %1", locBr$)) : EndIf
If remBr$ <> "" : html$ + _Pill(TF("help.pill.remotebr", "Branche distante: %1", remBr$)) : EndIf
html$ + "</div>"
html$ + "<div class='muted'>" + HtmlEscape(TF("help.workdir", "Dossier : %1", workdir$)) + "</div>"
html$ + "</div>"
; --- Corps selon contexte
If Not repo
; Pas de dépôt → proposer init ou clone
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.norepo.title","Aucun dépôt Git ici")) + "</h2>"
html$ + "<div class='callout info'>" + HtmlEscape(T("help.norepo.why",
"Initialiser un dépôt permet de suivre lhistorique de vos fichiers, de créer des versions et de collaborer.")) + "</div>"
html$ + "<ul>"
html$ + "<li>" + HtmlEscape(T("help.norepo.action.init",
"Cliquez sur « Init Dépôt » pour créer un dépôt vide (.git).")) + "</li>"
html$ + "<li>" + HtmlEscape(T("help.norepo.action.clone",
"Si vous avez une URL distante dans « Remote », cliquez sur « Clone » pour récupérer un dépôt existant.")) + "</li>"
html$ + "</ul>"
html$ + "</div>"
ElseIf repo And Not hasCommit
; Dépôt sans commit
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.nocommit.title","Aucun commit pour linstant")) + "</h2>"
html$ + "<p>" + HtmlEscape(T("help.nocommit.how",
"Cochez des fichiers à préparer (stage), écrivez un message puis cliquez « Commit ». Vous pouvez ignorer des fichiers via « Ignorer » (.gitignore).")) + "</p>"
html$ + "<ul>"
html$ + "<li>" + HtmlEscape(T("help.nocommit.step1","Cochez les fichiers à inclure.")) + "</li>"
html$ + "<li>" + HtmlEscape(T("help.nocommit.step2","Saisissez un message clair et concis.")) + "</li>"
html$ + "<li>" + HtmlEscape(T("help.nocommit.step3","Cliquez « Commit ». Un commit = un point de contrôle.")) + "</li>"
html$ + "</ul>"
If hasRemoteCfg
html$ + "<p class='muted'>" + HtmlEscape(T("help.nocommit.pushhint",
"Après votre premier commit, vous pourrez « Push » vers le dépôt distant.")) + "</p>"
EndIf
html$ + "</div>"
Else
; Dépôt avec (potentiellement) des changements
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.status.title","État des changements")) + "</h2>"
html$ + "<div class='row'>"
html$ + _Pill(TF("help.status.staged", "Préparés : %1", Str(cStaged)))
html$ + _Pill(TF("help.status.unstaged", "Non préparés : %1", Str(cUnstaged)))
html$ + _Pill(TF("help.status.untracked", "Non suivis : %1", Str(cUntracked)))
html$ + _Pill(TF("help.status.conflicts", "Conflits : %1", Str(cConflicts)))
html$ + "</div>"
If cConflicts > 0
html$ + "<p class='err'>" + HtmlEscape(T("help.status.conflicts.msg",
"Des conflits existent. Résolvez-les puis committez la résolution.")) + "</p>"
EndIf
If cStaged + cUnstaged + cUntracked = 0
html$ + "<p class='ok'>" + HtmlEscape(T("help.status.clean","Aucun changement local détecté.")) + "</p>"
Else
html$ + "<ul>"
If cUnstaged > 0 Or cStaged > 0
html$ + "<li>" + HtmlEscape(T("help.status.suggest.commit",
"Cochez les fichiers à inclure puis cliquez « Commit » pour enregistrer vos modifications.")) + "</li>"
EndIf
If cUntracked > 0
html$ + "<li>" + HtmlEscape(T("help.status.suggest.ignore",
"Des fichiers non suivis existent. Ajoutez-les au suivi (cochez/commit) ou ignorez-les via « Ignorer » pour les exclure du VCS.")) + "</li>"
EndIf
html$ + "</ul>"
EndIf
html$ + "</div>"
; --- Remote / Synchronisation
If hasRemoteCfg
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.remote.title","Synchronisation avec le dépôt distant")) + "</h2>"
If locBr$ <> "" And remBr$ <> ""
html$ + "<p>" + HtmlEscape(TF("help.remote.brpair",
"Comparaison %1 ↔ %2 :", locBr$, remBr$)) + " "
html$ + _Pill(TF("help.remote.ahead", "En avance : %1", Str(ahead)))
html$ + _Pill(TF("help.remote.behind", "En retard : %1", Str(behind))) + "</p>"
Select Bool(ahead>0) * 2 + Bool(behind>0) ; 0=à jour, 1=behind, 2=ahead, 3=divergent
Case 0
html$ + "<p class='ok'>" + HtmlEscape(T("help.remote.uptodate","Votre branche est à jour avec le distant.")) + "</p>"
Case 2
html$ + "<p class='info'>" + HtmlEscape(T("help.remote.push",
"Vous avez des commits en avance. Cliquez « Push » pour les publier.")) + "</p>"
Case 1
html$ + "<p class='warn'>" + HtmlEscape(T("help.remote.pull",
"Le distant a des commits que vous navez pas. Cliquez « Pull » pour mettre à jour.")) + "</p>"
Case 3
html$ + "<p class='err'>" + HtmlEscape(T("help.remote.diverge",
"Branches divergentes. Effectuez un merge ou un rebase, puis poussez le résultat.")) + "</p>"
EndSelect
Else
html$ + "<p class='muted'>" + HtmlEscape(T("help.remote.select",
"Sélectionnez une branche locale et une branche distante pour évaluer létat (Pull/Push).")) + "</p>"
EndIf
html$ + "</div>"
Else
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.remote.none.title","Aucun remote configuré")) + "</h2>"
html$ + "<p>" + HtmlEscape(T("help.remote.none.hint",
"Renseignez lURL dans « Remote » (ex. https://… ou git@host:org/repo.git) puis utilisez « Push » pour publier ou « Pull »/« Fetch » pour synchroniser.")) + "</p>"
html$ + "</div>"
EndIf
EndIf
; --- Concepts clés (toujours utile)
html$ + "<div class='section'>"
html$ + "<h2>" + HtmlEscape(T("help.concepts.title","Concepts clés")) + "</h2>"
html$ + "<ul>"
html$ + "<li><code>init</code> — " + HtmlEscape(T("help.c.init","crée un dépôt vide dans le dossier courant.")) + "</li>"
html$ + "<li><code>clone</code> — " + HtmlEscape(T("help.c.clone","copie un dépôt distant en local.")) + "</li>"
html$ + "<li><code>stage</code> — " + HtmlEscape(T("help.c.stage","prépare des fichiers pour le prochain commit (cochez dans la liste).")) + "</li>"
html$ + "<li><code>commit</code> — " + HtmlEscape(T("help.c.commit","enregistre un instantané avec message.")) + "</li>"
html$ + "<li><code>push</code> — " + HtmlEscape(T("help.c.push","envoie vos commits vers le dépôt distant.")) + "</li>"
html$ + "<li><code>pull</code> — " + HtmlEscape(T("help.c.pull","récupère et fusionne les commits distants.")) + "</li>"
html$ + "<li><code>rebase</code> — " + HtmlEscape(T("help.c.rebase","rejoue vos commits au-dessus de la branche distante pour un historique linéaire.")) + "</li>"
html$ + "</ul>"
html$ + "</div>"
html$ + "</body></html>"
; -- Écrit le HTML et charge dans le WebView
If CreateFile(0,GetTemporaryDirectory()+"test.htm")
WriteString(0,html$)
CloseFile(0)
EndIf
SetGadgetItemText(#GID_HelpWeb, #PB_WebView_HtmlCode,html$)
EndProcedure
Procedure RefreshGUI(null.i)
DoGitFetch() ;TODO add Branch
@@ -1948,6 +2212,8 @@ Procedure RefreshGUI(null.i)
SetGadgetItemState(#GID_ListStatus,n,#PB_ListIcon_Checked)
EndIf
Next
RefreshHelp()
EndProcedure
Procedure Refresh()
@@ -2018,6 +2284,7 @@ Procedure Main()
Repeat
UpdateGadgetsState()
ev = WaitWindowEvent()
Select ev
@@ -2038,8 +2305,10 @@ Procedure Main()
Case #Panel_Config
EndSelect
EndIf
RefreshHelp()
Case #GID_BtnInit
GitInit()
RefreshHelp()
Case #GID_BtnBrowseRepo
; FR: Sélection dossier / EN: select folder
Protected path$ = PathRequester(T("Dlg.SelectRepo","Sélectionnez le dépôt local..."), GetCurrentDirectory(),WindowID(#WinMain))
@@ -2053,13 +2322,14 @@ Procedure Main()
EndIf
;CreateThread(@RefreshFileList(),0) ;TODO refresh list
EndIf
RefreshHelp()
Case #GID_BtnInit
SetGadgetText(#GID_TxtAction, T("Action.InitRepo","Init dépôt demandé"))
; TODO: call your Git init logic
GitInit()
RefreshHelp()
Case #GID_BtnRefresh
Refresh()
RefreshHelp()
Case #GID_BtnClone
SetGadgetText(#GID_TxtAction, T("Action.Clone","Clone demandé"))
@@ -2072,6 +2342,7 @@ Procedure Main()
Case #GID_BtnPush
DoPush()
GetRemoteStatusInfo()
RefreshHelp()
; TODO: git push
Case #GID_BtnVerify
@@ -2097,7 +2368,7 @@ Procedure Main()
SetGadgetText(#GID_EdMessage,"")
Refresh()
EndIf
RefreshHelp()
Case #GID_BtnSaveGitIgnore
; TODO: save .gitignore
@@ -2114,9 +2385,9 @@ EndProcedure
Main()
; IDE Options = PureBasic 6.21 (Windows - x64)
; CursorPosition = 1197
; FirstLine = 1190
; Folding = ---------
; CursorPosition = 2370
; FirstLine = 2311
; Folding = ----------
; EnableThread
; EnableXP
; DPIAware