Repository: kovidgoyal/kitty

Issue:
MacOS: Open a window if none are open when opening a new tab. 
**Is your feature request related to a problem? Please describe.**
In iTerm, if I close my last window, then hit cmd+t (new tab), it opens a window for me (with 1 tab in it). In kitty, since no windows are open, hitting cmd+t does nothing (as far as I can tell). Obviously I can open a new window, but I tend to just nuke all of my windows then open a few tabs with a quick bit of cmd+t spam. I am aware of how small and unimportant this is in the big scheme of things, just a small convenience thing.

**Describe the solution you'd like**
Ideally, opening a new window when you hit cmd+t with no windows open


I'm happy to _try_ to submit a PR for this, I just wanted to verify that there isn't some different way things work/are viewed in kitty that would in general make this undesirable. 


Discussion:
> ... hitting cmd+t does nothing ...

I noticed this a long time ago. But what I would like to do is to gray out the "New Tab" and other items in the global menu bar.
Since there is no OS window, you can't use "New Tab" or "New Window".

So it's up to kovid to decide which way is the most appropriate.

kitty/boss.py
```text
     @ac('tab', 'Create a new tab')
     def new_tab(self, *args: str) -> None:
+        if not self.os_window_map:
+            self.add_os_window()
         self._create_tab(list(args))
```
IMO new_window/new_tab/launch should all just create the new OS window
when needed. I dont see why we should force the user to create a new OS
window first.


Resolving patch:
diff --git a/kitty/boss.py b/kitty/boss.py
--- a/kitty/boss.py
+++ b/kitty/boss.py
@@ -1812,6 +1812,8 @@ def _new_tab(self, args: Union[SpecialWindowInstance, Iterable[str]], cwd_from:
                 special_window = args
             else:
                 special_window = self.args_to_special_window(args, cwd_from=cwd_from)
+        if not self.os_window_map:
+            self.add_os_window()
         tm = self.active_tab_manager
         if tm is not None:
             return tm.new_tab(special_window=special_window, cwd_from=cwd_from, as_neighbor=as_neighbor)
@@ -1833,8 +1835,6 @@ def new_tab_with_cwd(self, *args: str) -> None:
         self._create_tab(list(args), cwd_from=CwdRequest(self.active_window_for_cwd))
 
     def new_tab_with_wd(self, wd: Union[str, List[str]], str_is_multiple_paths: bool = False) -> None:
-        if not self.os_window_map:
-            self.add_os_window()
         if isinstance(wd, str):
             wd = wd.split(os.pathsep) if str_is_multiple_paths else [wd]
         for path in wd:
@@ -1842,6 +1842,11 @@ def new_tab_with_wd(self, wd: Union[str, List[str]], str_is_multiple_paths: bool
             self._new_tab(special_window)
 
     def _new_window(self, args: List[str], cwd_from: Optional[CwdRequest] = None) -> Optional[Window]:
+        if not self.os_window_map:
+            os_window_id = self.add_os_window()
+            tm = self.os_window_map.get(os_window_id)
+            if tm is not None and not tm.active_tab:
+                tm.new_tab(empty_tab=True)
         tab = self.active_tab
         if tab is None:
             return None
diff --git a/kitty/rc/launch.py b/kitty/rc/launch.py
--- a/kitty/rc/launch.py
+++ b/kitty/rc/launch.py
@@ -85,7 +85,7 @@ def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get:
         tabs = self.tabs_for_match_payload(boss, window, payload_get)
         if tabs and tabs[0]:
             target_tab = tabs[0]
-        elif payload_get('type') not in ('os-window', 'background'):
+        elif payload_get('type') not in ('background', 'os-window', 'tab', 'window'):
             return None
         w = do_launch(boss, opts, payload_get('args') or [], target_tab=target_tab)
         return None if payload_get('no_response') else str(getattr(w, 'id', 0))


Test patch: